Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix missing Program tab #4551

Merged
merged 3 commits into from
Feb 10, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// 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 queries.common

import clue.GraphQLSubquery
import clue.annotation.GraphQL
import explore.model.CallForProposal
import lucuma.schemas.ObservationDB

@GraphQL
object CallForProposalsSubquery
extends GraphQLSubquery.Typed[ObservationDB, CallForProposal]("CallForProposal"):
override val subquery: String = s"""
{
id
semester
title
cfpType: type
nonPartnerDeadline
active {
start
end
}
partners {
partner
submissionDeadline
}
coordinateLimits {
north $SiteCoordinatesLimitsSubquery
south $SiteCoordinatesLimitsSubquery
}
}
"""
20 changes: 1 addition & 19 deletions explore/src/clue/scala/queries/common/CallsQueriesGQL.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,7 @@ object CallsQueriesGQL:
val document: String = s"""
query {
callsForProposals(WHERE: {isOpen: {EQ: true}}) {
matches {
id
semester
title
cfpType: type
nonPartnerDeadline
active {
start
end
}
partners {
partner
submissionDeadline
}
coordinateLimits {
north $SiteCoordinatesLimitsSubquery
south $SiteCoordinatesLimitsSubquery
}
}
matches $CallForProposalsSubquery
}
}
"""
Expand Down
4 changes: 1 addition & 3 deletions explore/src/clue/scala/queries/common/ProposalSubquery.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ import lucuma.schemas.ObservationDB
object ProposalSubquery extends GraphQLSubquery.Typed[ObservationDB, Proposal]("Proposal"):
override val subquery: String = s"""
{
call {
id
}
call $CallForProposalsSubquery
category
reference {
label
Expand Down
12 changes: 6 additions & 6 deletions explore/src/main/scala/explore/ExploreLayout.scala
Original file line number Diff line number Diff line change
Expand Up @@ -222,12 +222,12 @@ object ExploreLayout:
val deadline: Option[Timestamp] =
programSummaries.toOption
.flatMap: programSummaries =>
(ProgramSummaries.proposal.getOption(programSummaries).flatten,
RootModel.cfps.get(view.get).toOption
).mapN: (p, c) =>
val piP = ProgramSummaries.piPartner.getOption(programSummaries)
p.deadline(c, piP)
.flatten
ProgramSummaries.proposal
.getOption(programSummaries)
.flatten
.flatMap: p =>
val piP = ProgramSummaries.piPartner.getOption(programSummaries)
p.deadline(piP)

val cacheKey: String =
userVault.get
Expand Down
4 changes: 0 additions & 4 deletions explore/src/main/scala/explore/Routing.scala
Original file line number Diff line number Diff line change
Expand Up @@ -194,9 +194,6 @@ object Routing:
programDetails <-
programSummaries.model.zoom(ProgramSummaries.optProgramDetails).toOptionView
proposal <- programDetails.get.proposal
callId <- proposal.callId
cfps <- model.rootModel.get.cfps.toOption
cfp <- cfps.find(_.id === callId)
yield ProgramTabContents(
routingInfo.programId,
programDetails,
Expand All @@ -207,7 +204,6 @@ object Routing:
programSummaries.get.targets,
model.rootModel.zoom(RootModel.vault).get,
programSummaries.get.programTimesPot,
cfp.semester,
userPreferences(model.rootModel),
model.userIsReadonlyCoi
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ object ProgramCacheController
ProgramQueriesGQL.ProgramEditDetailsSubscription.Data,
ProgramSummaries => ProgramSummaries
] =
_.map(_.programEdit.value.proposal.flatMap(_.callId)).changes.void
_.map(_.programEdit.value.proposal.flatMap(_.call.map(_.id))).changes.void
.evalMap(_ => updateObservationsWorkflows(props.programId.toWhereObservation))

val updateProgramDetails: Resource[IO, Stream[IO, ProgramSummaries => ProgramSummaries]] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ trait ProposalQueries:
extension (proposal: Proposal)
def toInput: ProposalPropertiesInput =
ProposalPropertiesInput(
callId = proposal.callId.orUnassign,
callId = proposal.call.map(_.id).orUnassign,
category = proposal.category.orUnassign,
`type` = proposal.proposalType.map(_.toInput).orUnassign
)
Expand Down
23 changes: 15 additions & 8 deletions explore/src/main/scala/explore/programs/ProgramDetailsTile.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,20 @@ import explore.model.ProgramUser
import japgolly.scalajs.react.*
import japgolly.scalajs.react.vdom.html_<^.*
import lucuma.core.model.Program
import lucuma.core.model.Semester
import lucuma.core.syntax.display.*
import lucuma.core.util.DateInterval
import lucuma.react.common.ReactFnProps
import lucuma.refined.*
import lucuma.ui.primereact.CheckboxView
import lucuma.ui.primereact.FormInfo
import lucuma.ui.primereact.given

import java.time.LocalDate

case class ProgramDetailsTile(
programId: Program.Id,
programDetails: View[ProgramDetails],
programTimes: Pot[ProgramTimes],
semester: Semester,
userIsReadonlyCoi: Boolean
) extends ReactFnProps(ProgramDetailsTile.component)

Expand All @@ -41,19 +42,25 @@ object ProgramDetailsTile:
useContext(AppContext.ctx).map: ctx =>
import ctx.given

val details: ProgramDetails = props.programDetails.get
val thesis: Boolean = details.allUsers.exists(_.thesis.exists(_ === true))
val users: View[List[ProgramUser]] = props.programDetails.zoom(ProgramDetails.allUsers)
val newDataNotificationView =
val details: ProgramDetails = props.programDetails.get
val thesis: Boolean = details.allUsers.exists(_.thesis.exists(_ === true))
val users: View[List[ProgramUser]] = props.programDetails.zoom(ProgramDetails.allUsers)
val newDataNotificationView =
props.programDetails
.zoom(ProgramDetails.shouldNotify)
.withOnMod(b => ProgramQueries.updateGoaShouldNotify[IO](props.programId, b).runAsync)
val cfpActivePeriod: Option[DateInterval] = details.proposal.flatMap(_.call).map(_.active)

// We `should` always have a call for proposal if we get here, but...
def dateOrMissing(o: Option[LocalDate], label: String) =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can just omit the element if the date is missing?

Also, could we add explicit types?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could do that, but I thought it might be easier to debug in the future if we ever run into this again. It should never happen since the program tab is only for ACCEPTED proposals, which should have a CfP. But, omitting them would be cleaner, I guess.

val s = o.fold("Missing!")(Constants.GppDateFormatter.format)
FormInfo(s, label)

<.div(ExploreStyles.ProgramDetailsTile)(
<.div(ExploreStyles.ProgramDetailsInfoArea, ExploreStyles.ProgramDetailsLeft)(
FormInfo(details.reference.map(_.label).getOrElse("---"), "Reference"),
FormInfo(Constants.GppDateFormatter.format(props.semester.start.localDate), "Start"),
FormInfo(Constants.GppDateFormatter.format(props.semester.end.localDate), "End"),
dateOrMissing(cfpActivePeriod.map(_.start), "Start"),
dateOrMissing(cfpActivePeriod.map(_.end), "End"),
// Thesis should be set True if any of the investigators will use the proposal as part of their thesis (3390)
FormInfo(if (thesis) "Yes" else "No", "Thesis"),
FormInfo(s"${details.proprietaryMonths} months", "Proprietary")
Expand Down
34 changes: 20 additions & 14 deletions explore/src/main/scala/explore/proposal/ProposalDetailsTile.scala
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ object ProposalDetailsBody:
)(using Logger[IO]): VdomNode = {
val proposalCfpView: View[Proposal] =
props.proposalAligner.viewMod { p =>
ProposalPropertiesInput.callId.replace(p.callId.orUnassign) >>>
ProposalPropertiesInput.callId.replace(p.call.map(_.id).orUnassign) >>>
ProposalPropertiesInput.`type`.replace(p.proposalType.map(_.toInput).orUnassign)
}

Expand All @@ -171,13 +171,20 @@ object ProposalDetailsBody:

val titleView = titleAligner.view(_.orUnassign)

val callId: Option[CallForProposals.Id] = props.proposalAligner.get.callId
val scienceSubtype = props.proposalAligner.get.proposalType.map(_.scienceSubtype)
val scienceSubtype = props.proposalAligner.get.proposalType.map(_.scienceSubtype)

val selectedCfp = callId.flatMap(id => props.cfps.find(_.id === id))
val isCfpSelected = selectedCfp.isDefined
val subtypes = selectedCfp.map(_.cfpType.subTypes)
val hasSubtypes = subtypes.exists(_.size > 1)
val selectedCfp: Option[CallForProposal] = props.proposalAligner.get.call
val isCfpSelected = selectedCfp.isDefined
val subtypes = selectedCfp.map(_.cfpType.subTypes)
val hasSubtypes = subtypes.exists(_.size > 1)

// need to include the current cfp as disabled if it doesn't exist in the list of calls
val cfpOptions: List[SelectItem[CallForProposal]] =
selectedCfp
.filterNot(cfp => props.cfps.exists(_.id === cfp.id))
.map(cfp => SelectItem(cfp, cfp.title, disabled = true))
.toList ++
props.cfps.map(cfp => SelectItem(cfp, cfp.title))

val categoryAligner: Aligner[Option[TacCategory], Input[TacCategory]] =
props.proposalAligner.zoom(Proposal.category, ProposalPropertiesInput.category.modify)
Expand Down Expand Up @@ -405,17 +412,16 @@ object ProposalDetailsBody:
FormDropdownOptional(
id = "cfp".refined,
label = React.Fragment("Call For Proposal", HelpIcon("proposal/main/cfp.md".refined)),
value = callId,
options = props.cfps.map(r => SelectItem(r.id, r.title)),
onChange = _.map { cid =>
val call = props.cfps.find(_.id === cid)
value = selectedCfp,
options = cfpOptions,
onChange = _.map { cfp =>
proposalCfpView.mod(
_.copy(callId = cid.some, proposalType = call.map(_.cfpType.defaultType))
_.copy(call = cfp.some, proposalType = cfp.cfpType.defaultType.some)
)
}.orEmpty,
disabled = props.readonly,
modifiers = List(^.id := "cfp"),
clazz = ExploreStyles.WarningInput.when_(callId.isEmpty)
clazz = ExploreStyles.WarningInput.when_(selectedCfp.isEmpty)
),
// Proposal type selector, visible when cfp is selected and has more than one subtpye
FormDropdown(
Expand Down Expand Up @@ -456,7 +462,7 @@ object ProposalDetailsBody:
.getOrElse(Hours.unsafeFrom(0))
showDialog <- useStateView(Visible.Hidden) // show partner splits modal
splitsList <- useStateView(List.empty[PartnerSplit]) // partner splits modal
_ <- useEffectWithDeps((props.proposal.callId, props.cfps)):
_ <- useEffectWithDeps((props.proposal.call.map(_.id), props.cfps)):
// Update the partner splits when a new callId is set
(callId, cfps) =>
callId.foldMap(cid =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ object ProposalSubmissionBar:
props.canSubmit && props.proposalStatus.get === ProposalStatus.Submitted && !isDueDeadline
,
errorMessage.get
.map(r => Message(text = r, severity = Message.Severity.Error))
.map(r =>
<.span(ExploreStyles.ProposalDeadline)(
Message(text = r, severity = Message.Severity.Error)
)
)
)
)
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ object ProposalTabContents:
props.programDetails.zoom(ProgramDetails.piPartner.some).get

val deadline: Option[Timestamp] =
proposal.get.deadline(props.cfps, piPartner)
proposal.get.deadline(piPartner)

<.div(ExploreStyles.ProposalTab)(
ProposalEditor(
Expand All @@ -133,7 +133,7 @@ object ProposalTabContents:
props.programId,
props.programDetails.zoom(ProgramDetails.proposalStatus),
deadline,
proposal.get.callId,
proposal.get.call.map(_.id),
isStdUser && !props.userIsReadonlyCoi,
props.hasUndefinedObservations
)
Expand Down
3 changes: 0 additions & 3 deletions explore/src/main/scala/explore/tabs/ProgramTabContents.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ import japgolly.scalajs.react.vdom.html_<^.*
import lucuma.core.model.Configuration
import lucuma.core.model.ConfigurationRequest
import lucuma.core.model.Program
import lucuma.core.model.Semester
import lucuma.core.model.User
import lucuma.react.common.ReactFnProps
import lucuma.react.resizeDetector.*
Expand All @@ -51,7 +50,6 @@ case class ProgramTabContents(
targets: TargetList,
userVault: Option[UserVault],
programTimes: Pot[ProgramTimes],
semester: Semester,
userPreferences: UserPreferences,
userIsReadonlyCoi: Boolean
) extends ReactFnProps(ProgramTabContents.component)
Expand Down Expand Up @@ -82,7 +80,6 @@ object ProgramTabContents:
props.programId,
props.programDetails,
props.programTimes,
props.semester,
props.userIsReadonlyCoi
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,39 @@
package explore.model.arb

import lucuma.core.enums.TacCategory
import explore.model.CallForProposal
import explore.model.Proposal
import lucuma.core.util.arb.ArbEnumerated.given
import lucuma.core.util.arb.ArbGid.given
import lucuma.core.model.arb.ArbProposalReference.given
import org.scalacheck.*
import org.scalacheck.Arbitrary.arbitrary
import org.scalacheck.Cogen.*

import explore.model.arb.ArbCallForProposal.given
import explore.model.arb.ArbProposalType.given
import explore.model.ProposalType
import lucuma.core.model.CallForProposals
import lucuma.core.model.ProposalReference

trait ArbProposal:

given Arbitrary[Proposal] =
Arbitrary {
for {
callId <- arbitrary[Option[CallForProposals.Id]]
call <- arbitrary[Option[CallForProposal]]
category <- arbitrary[Option[TacCategory]]
proposalType <- arbitrary[Option[ProposalType]]
reference <- arbitrary[Option[ProposalReference]]
} yield Proposal(callId, category, proposalType, reference)
} yield Proposal(call, category, proposalType, reference)
}

given Cogen[Proposal] =
Cogen[
(
Option[CallForProposals.Id],
Option[CallForProposal],
Option[TacCategory],
Option[ProposalType],
Option[ProposalReference]
)
].contramap(p => (p.callId, p.category, p.proposalType, p.reference))
].contramap(p => (p.call, p.category, p.proposalType, p.reference))

object ArbProposal extends ArbProposal
Loading
Loading