diff --git a/USERPREFS.md b/USERPREFS.md index 9ab5dea92a..3b1a908162 100644 --- a/USERPREFS.md +++ b/USERPREFS.md @@ -27,6 +27,14 @@ We have four environments with a respective db and heroku app: - staging - production +## Console + +You can spin up a server that servers a console app for an environment by using the CLI from the `hasura/user-prefs` directory. For example: + +``` +hasura console --endpoint https://user-prefs-master.herokuapp.com +``` + ## Copy the database To copy the database between instances you can create a backup on the postgress page under the diff --git a/common/src/main/scala/explore/model/ExploreGridLayouts.scala b/common/src/main/scala/explore/model/ExploreGridLayouts.scala index b9ee11f2ef..09a5b824ce 100644 --- a/common/src/main/scala/explore/model/ExploreGridLayouts.scala +++ b/common/src/main/scala/explore/model/ExploreGridLayouts.scala @@ -24,14 +24,15 @@ import scala.collection.immutable.SortedMap object ExploreGridLayouts: def sectionLayout: GridLayoutSection => LayoutsMap = _ match { - case GridLayoutSection.ProgramsLayout => programs.defaultProgramsLayouts - case GridLayoutSection.ConstraintsLayout => constraints.defaultConstraintsLayouts - case GridLayoutSection.SchedulingLayout => scheduling.defaultSchedulingLayouts - case GridLayoutSection.TargetLayout => targets.defaultTargetLayouts - case GridLayoutSection.ObservationsLayout => observations.defaultObsLayouts - case GridLayoutSection.OverviewLayout => overview.defaultOverviewLayouts - case GridLayoutSection.ProposalLayout => proposal.defaultProposalLayouts - case GridLayoutSection.GroupEditLayout => groupEdit.defaultGroupEditLayouts + case GridLayoutSection.ProgramsLayout => programs.defaultProgramsLayouts + case GridLayoutSection.ConstraintsLayout => constraints.defaultConstraintsLayouts + case GridLayoutSection.SchedulingLayout => scheduling.defaultSchedulingLayouts + case GridLayoutSection.TargetLayout => targets.defaultTargetLayouts + case GridLayoutSection.ObservationsLayout => observations.defaultObsLayouts + case GridLayoutSection.ObservationListLayout => observationList.defaultObsListLayouts + case GridLayoutSection.OverviewLayout => overview.defaultOverviewLayouts + case GridLayoutSection.ProposalLayout => proposal.defaultProposalLayouts + case GridLayoutSection.GroupEditLayout => groupEdit.defaultGroupEditLayouts } extension (l: LayoutsMap) @@ -63,14 +64,14 @@ object ExploreGridLayouts: private lazy val layoutMedium: Layout = Layout( List( LayoutItem( - i = ObsTabTilesIds.ConstraintsId.id.value, + i = ObsTabTileIds.ConstraintsId.id.value, x = 0, y = 0, w = DefaultWidth.value, h = ConstraintsHeight.value ), LayoutItem( - i = ObsTabTilesIds.TimingWindowsId.id.value, + i = ObsTabTileIds.TimingWindowsId.id.value, x = 0, y = ConstraintsHeight.value, w = DefaultWidth.value, @@ -95,7 +96,7 @@ object ExploreGridLayouts: private lazy val layoutMedium: Layout = Layout( List( LayoutItem( - i = ObsTabTilesIds.TimingWindowsId.id.value, + i = ObsTabTileIds.TimingWindowsId.id.value, x = 0, y = 0, w = DefaultWidth.value, @@ -122,21 +123,21 @@ object ExploreGridLayouts: private lazy val layoutMedium: Layout = Layout( List( LayoutItem( - i = ObsTabTilesIds.TargetSummaryId.id.value, + i = ObsTabTileIds.TargetSummaryId.id.value, x = 0, y = 0, w = DefaultWidth.value, h = SummaryHeight.value ), LayoutItem( - i = ObsTabTilesIds.TargetId.id.value, + i = ObsTabTileIds.TargetId.id.value, x = 0, y = SummaryHeight.value, w = DefaultWidth.value, h = TargetHeight.value ), LayoutItem( - i = ObsTabTilesIds.PlotId.id.value, + i = ObsTabTileIds.PlotId.id.value, x = 0, y = SummaryHeight.value + TargetHeight.value, w = DefaultWidth.value, @@ -157,7 +158,7 @@ object ExploreGridLayouts: private lazy val singleLayoutMedium: Layout = Layout( List( LayoutItem( - i = ObsTabTilesIds.TargetSummaryId.id.value, + i = ObsTabTileIds.TargetSummaryId.id.value, x = 0, y = 0, w = DefaultWidth.value, @@ -195,35 +196,35 @@ object ExploreGridLayouts: y = 0, w = DefaultWidth.value, h = NotesMaxHeight.value, - i = ObsTabTilesIds.NotesId.id.value + i = ObsTabTileIds.NotesId.id.value ), LayoutItem( x = 0, y = NotesMaxHeight.value, w = DefaultWidth.value, h = TargetHeight.value, - i = ObsTabTilesIds.TargetId.id.value + i = ObsTabTileIds.TargetId.id.value ), LayoutItem( x = 0, y = (NotesMaxHeight |+| TargetHeight).value, w = DefaultWidth.value, h = FinderChartHeight.value, - i = ObsTabTilesIds.FinderChartsId.id.value + i = ObsTabTileIds.FinderChartsId.id.value ), LayoutItem( x = 0, y = (NotesMaxHeight |+| TargetHeight |+| FinderChartHeight).value, w = DefaultWidth.value, h = SkyPlotHeight.value, - i = ObsTabTilesIds.PlotId.id.value + i = ObsTabTileIds.PlotId.id.value ), LayoutItem( x = 0, y = (NotesMaxHeight |+| TargetHeight |+| FinderChartHeight |+| SkyPlotHeight).value, w = DefaultWidth.value, h = ConstraintsMaxHeight.value, - i = ObsTabTilesIds.ConstraintsId.id.value + i = ObsTabTileIds.ConstraintsId.id.value ), LayoutItem( x = 0, @@ -231,7 +232,7 @@ object ExploreGridLayouts: (NotesMaxHeight |+| TargetHeight |+| FinderChartHeight |+| SkyPlotHeight |+| ConstraintsMaxHeight).value, w = DefaultWidth.value, h = TimingWindowsMaxHeight.value, - i = ObsTabTilesIds.TimingWindowsId.id.value + i = ObsTabTileIds.TimingWindowsId.id.value ), LayoutItem( x = 0, @@ -239,7 +240,7 @@ object ExploreGridLayouts: (NotesMaxHeight |+| TargetHeight |+| FinderChartHeight |+| SkyPlotHeight |+| ConstraintsMaxHeight |+| TimingWindowsMaxHeight).value, w = DefaultWidth.value, h = ConfigurationMaxHeight.value, - i = ObsTabTilesIds.ConfigurationId.id.value + i = ObsTabTileIds.ConfigurationId.id.value ), LayoutItem( x = 0, @@ -247,7 +248,7 @@ object ExploreGridLayouts: (NotesMaxHeight |+| TargetHeight |+| FinderChartHeight |+| SkyPlotHeight |+| ConstraintsMaxHeight |+| TimingWindowsMaxHeight |+| ConfigurationMaxHeight).value, w = DefaultWidth.value, h = ItcMaxHeight.value, - i = ObsTabTilesIds.ItcId.id.value + i = ObsTabTileIds.ItcId.id.value ), LayoutItem( x = 0, @@ -255,7 +256,7 @@ object ExploreGridLayouts: (NotesMaxHeight |+| TargetHeight |+| FinderChartHeight |+| SkyPlotHeight |+| ConstraintsMaxHeight |+| TimingWindowsMaxHeight |+| ConfigurationMaxHeight |+| ItcMaxHeight).value, w = DefaultWidth.value, h = SequenceMaxHeight.value, - i = ObsTabTilesIds.SequenceId.id.value + i = ObsTabTileIds.SequenceId.id.value ) ) ) @@ -269,6 +270,40 @@ object ExploreGridLayouts: ).withMinWidth end observations + object observationList: + private lazy val SummaryHeight: NonNegInt = 9.refined + private lazy val SkyPlotHeight: NonNegInt = 9.refined + + private lazy val layoutMedium: Layout = Layout( + List( + LayoutItem( + i = ObsSummaryTabTileIds.SummaryId.id.value, + x = 0, + y = 0, + w = DefaultWidth.value, + h = SummaryHeight.value + ), + LayoutItem( + i = ObsSummaryTabTileIds.PlotId.id.value, + x = 0, + y = SummaryHeight.value, + w = DefaultWidth.value, + h = SkyPlotHeight.value + ) + ) + ) + + lazy val defaultObsListLayouts: LayoutsMap = + defineStdLayouts( + Map( + (BreakpointName.lg, + layoutItems.andThen(layoutItemWidth).replace(DefaultLargeWidth)(layoutMedium) + ), + (BreakpointName.md, layoutMedium) + ) + ).withMinWidth + end observationList + object programs: private lazy val DetailsHeight: NonNegInt = 6.refined private lazy val NotesHeight: NonNegInt = 6.refined @@ -318,14 +353,14 @@ object ExploreGridLayouts: private lazy val layoutMedium: Layout = Layout( List( LayoutItem( - i = ObsTabTilesIds.WarningsAndErrorsId.id.value, + i = ObsTabTileIds.WarningsAndErrorsId.id.value, x = 0, y = 0, w = DefaultWidth.value, h = WarningsAndErrorsHeight.value ), LayoutItem( - i = ObsTabTilesIds.ObsAttachmentsId.id.value, + i = ObsTabTileIds.ObsAttachmentsId.id.value, x = 0, y = WarningsAndErrorsHeight.value, w = DefaultWidth.value, @@ -403,14 +438,14 @@ object ExploreGridLayouts: private lazy val layoutMedium: Layout = Layout( List( LayoutItem( - i = GroupEditIds.GroupEditId.id.value, + i = GroupEditTileIds.GroupEditId.id.value, x = 0, y = 0, w = DefaultWidth.value, h = GroupEditHeight.value ), LayoutItem( - i = GroupEditIds.GroupNotesId.id.value, + i = GroupEditTileIds.GroupNotesId.id.value, x = 0, y = GroupEditHeight.value, w = DefaultWidth.value, diff --git a/common/src/main/scala/explore/model/UserPreferences.scala b/common/src/main/scala/explore/model/UserPreferences.scala index 79285b6780..5f70c4352f 100644 --- a/common/src/main/scala/explore/model/UserPreferences.scala +++ b/common/src/main/scala/explore/model/UserPreferences.scala @@ -29,6 +29,9 @@ case class UserPreferences( val observationsTabLayout = tabLayout(GridLayoutSection.ObservationsLayout) + val observationListTabLayout = + tabLayout(GridLayoutSection.ObservationListLayout) + val programsTabLayout = tabLayout(GridLayoutSection.ProgramsLayout) diff --git a/common/src/main/webapp/css/react-grid-layout.css b/common/src/main/webapp/css/react-grid-layout.css index f789d490b5..bea8f32ac6 100644 --- a/common/src/main/webapp/css/react-grid-layout.css +++ b/common/src/main/webapp/css/react-grid-layout.css @@ -64,7 +64,8 @@ } .rgl-tile-overlay { - display: none; /* Enable for grid debugging */ + display: none; + /* Enable for grid debugging */ pointer-events: none; position: absolute; top: 5px; diff --git a/explore/src/main/scala/explore/config/sequence/SequenceEditorTile.scala b/explore/src/main/scala/explore/config/sequence/SequenceEditorTile.scala index 890ae47680..f6adf8fcc6 100644 --- a/explore/src/main/scala/explore/config/sequence/SequenceEditorTile.scala +++ b/explore/src/main/scala/explore/config/sequence/SequenceEditorTile.scala @@ -8,7 +8,7 @@ import crystal.react.View import explore.components.Tile import explore.model.AsterismIds import explore.model.Execution -import explore.model.ObsTabTilesIds +import explore.model.ObsTabTileIds import explore.model.Observation import lucuma.core.model.Program @@ -22,7 +22,7 @@ object SequenceEditorTile: sequenceChanged: View[Pot[Unit]] ) = Tile( - ObsTabTilesIds.SequenceId.id, + ObsTabTileIds.SequenceId.id, s"Sequence" )( _ => diff --git a/explore/src/main/scala/explore/notes/NotesTile.scala b/explore/src/main/scala/explore/notes/NotesTile.scala index e31900b494..4d53ffae66 100644 --- a/explore/src/main/scala/explore/notes/NotesTile.scala +++ b/explore/src/main/scala/explore/notes/NotesTile.scala @@ -10,7 +10,7 @@ import explore.Icons import explore.components.HelpIcon import explore.components.Tile import explore.components.ui.ExploreStyles -import explore.model.ObsTabTilesIds +import explore.model.ObsTabTileIds import explore.model.Observation import explore.model.enums.TileSizeState import japgolly.scalajs.react.* @@ -33,7 +33,7 @@ object NotesTile: def notesTile(obsId: Observation.Id, notes: View[Option[NonEmptyString]]) = Tile( - ObsTabTilesIds.NotesId.id, + ObsTabTileIds.NotesId.id, s"Note for Observer", NotesTileState(notes.get.foldMap(_.value), Editing.NotEditing), bodyClass = ExploreStyles.NotesTile diff --git a/explore/src/main/scala/explore/tabs/AsterismEditorTile.scala b/explore/src/main/scala/explore/tabs/AsterismEditorTile.scala index c5f913c663..10442de0b0 100644 --- a/explore/src/main/scala/explore/tabs/AsterismEditorTile.scala +++ b/explore/src/main/scala/explore/tabs/AsterismEditorTile.scala @@ -13,7 +13,7 @@ import explore.model.GlobalPreferences import explore.model.GuideStarSelection import explore.model.ObsConfiguration import explore.model.ObsIdSet -import explore.model.ObsTabTilesIds +import explore.model.ObsTabTileIds import explore.model.ObservationsAndTargets import explore.model.OnAsterismUpdateParams import explore.model.OnCloneParameters @@ -74,7 +74,7 @@ object AsterismEditorTile: ) Tile( - ObsTabTilesIds.TargetId.id, + ObsTabTileIds.TargetId.id, title, AsterismTileState(), back = backButton, diff --git a/explore/src/main/scala/explore/tabs/ConfigurationTile.scala b/explore/src/main/scala/explore/tabs/ConfigurationTile.scala index 2cdf60760e..f5d16cd211 100644 --- a/explore/src/main/scala/explore/tabs/ConfigurationTile.scala +++ b/explore/src/main/scala/explore/tabs/ConfigurationTile.scala @@ -12,7 +12,7 @@ import explore.config.ConfigurationPanel import explore.model.AsterismIds import explore.model.BasicConfigAndItc import explore.model.ObsConfiguration -import explore.model.ObsTabTilesIds +import explore.model.ObsTabTileIds import explore.model.Observation import explore.model.ScienceRequirements import explore.model.TargetList @@ -48,7 +48,7 @@ object ConfigurationTile: units: WavelengthUnits )(using Logger[IO]) = Tile( - ObsTabTilesIds.ConfigurationId.id, + ObsTabTileIds.ConfigurationId.id, "Configuration", bodyClass = ExploreStyles.ConfigurationTileBody )(_ => diff --git a/explore/src/main/scala/explore/tabs/ConstraintsTabContents.scala b/explore/src/main/scala/explore/tabs/ConstraintsTabContents.scala index c7dd03b506..564225993e 100644 --- a/explore/src/main/scala/explore/tabs/ConstraintsTabContents.scala +++ b/explore/src/main/scala/explore/tabs/ConstraintsTabContents.scala @@ -240,7 +240,7 @@ object ConstraintsTabContents extends TwoPanels: val constraintsTile: Tile[Option[VdomNode]] = Tile( - ObsTabTilesIds.ConstraintsId.id, + ObsTabTileIds.ConstraintsId.id, constraintsTitle, backButton.some )(_ => diff --git a/explore/src/main/scala/explore/tabs/ElevationPlotTile.scala b/explore/src/main/scala/explore/tabs/ElevationPlotTile.scala index bb43abd60b..a0cf4f51e7 100644 --- a/explore/src/main/scala/explore/tabs/ElevationPlotTile.scala +++ b/explore/src/main/scala/explore/tabs/ElevationPlotTile.scala @@ -4,10 +4,10 @@ package explore.tabs import cats.syntax.all.* +import eu.timepit.refined.types.string.NonEmptyString import explore.components.Tile import explore.components.ui.ExploreStyles import explore.model.GlobalPreferences -import explore.model.ObsTabTilesIds import explore.targeteditor.plots.ObjectPlotSection import explore.targeteditor.plots.PlotData import japgolly.scalajs.react.* @@ -24,6 +24,7 @@ object ElevationPlotTile: def elevationPlotTile( userId: Option[User.Id], + tileId: NonEmptyString, plotData: PlotData, site: Option[Site], vizTime: Option[Instant], @@ -32,7 +33,7 @@ object ElevationPlotTile: globalPreferences: GlobalPreferences ): Tile[Unit] = Tile( - ObsTabTilesIds.PlotId.id, + tileId, "Elevation Plot", bodyClass = ExploreStyles.ElevationPlotTileBody ) { _ => diff --git a/explore/src/main/scala/explore/tabs/FinderChartsTile.scala b/explore/src/main/scala/explore/tabs/FinderChartsTile.scala index 8310a25a8b..7ccace87f7 100644 --- a/explore/src/main/scala/explore/tabs/FinderChartsTile.scala +++ b/explore/src/main/scala/explore/tabs/FinderChartsTile.scala @@ -13,7 +13,7 @@ import explore.findercharts.FinderChartsBody import explore.findercharts.FinderChartsTileState import explore.findercharts.FinderChartsTitle import explore.model.ObsAttachmentList -import explore.model.ObsTabTilesIds +import explore.model.ObsTabTileIds import explore.model.Observation import japgolly.scalajs.react.vdom.html_<^.* import lucuma.core.math.Angle @@ -35,7 +35,7 @@ object FinderChartsTile: readOnly: Boolean ) = Tile( - ObsTabTilesIds.FinderChartsId.id, + ObsTabTileIds.FinderChartsId.id, s"Finder Charts", FinderChartsTileState(ChartSelector.Closed, None), bodyClass = ExploreStyles.FinderChartsTile diff --git a/explore/src/main/scala/explore/tabs/ItcTile.scala b/explore/src/main/scala/explore/tabs/ItcTile.scala index 5090511c18..95a3214f4f 100644 --- a/explore/src/main/scala/explore/tabs/ItcTile.scala +++ b/explore/src/main/scala/explore/tabs/ItcTile.scala @@ -13,7 +13,7 @@ import explore.itc.ItcPanelTitle import explore.itc.ItcProps import explore.itc.SelectedItcTarget import explore.model.GlobalPreferences -import explore.model.ObsTabTilesIds +import explore.model.ObsTabTileIds import explore.model.Observation import explore.model.TargetList import explore.model.itc.ItcAsterismGraphResults @@ -33,7 +33,7 @@ object ItcTile: globalPreferences: View[GlobalPreferences] ) = Tile( - ObsTabTilesIds.ItcId.id, + ObsTabTileIds.ItcId.id, s"ITC", SelectedItcTarget(itcGraphResults.toOption.flatMap(_.brightestTarget)), bodyClass = diff --git a/explore/src/main/scala/explore/tabs/ObsGroupTiles.scala b/explore/src/main/scala/explore/tabs/ObsGroupTiles.scala index ebddb597f5..f872a2d894 100644 --- a/explore/src/main/scala/explore/tabs/ObsGroupTiles.scala +++ b/explore/src/main/scala/explore/tabs/ObsGroupTiles.scala @@ -9,7 +9,7 @@ import explore.components.Tile import explore.components.TileController import explore.components.ui.ExploreStyles import explore.model.Group -import explore.model.GroupEditIds +import explore.model.GroupEditTileIds import explore.model.GroupTree import explore.model.ProgramTimeRange import explore.model.enums.GridLayoutSection @@ -71,7 +71,7 @@ object ObsGroupTiles: ) val editTile = Tile( - GroupEditIds.GroupEditId.id, + GroupEditTileIds.GroupEditId.id, s"${ if group.get.system then group.get.name.foldMap(_.value) else if group.get.isAnd then "AND" @@ -93,7 +93,7 @@ object ObsGroupTiles: ) val notesTile = Tile( - GroupEditIds.GroupNotesId.id, + GroupEditTileIds.GroupNotesId.id, s"Note for Observer" )(_ => <.div( diff --git a/explore/src/main/scala/explore/tabs/ObsTabContents.scala b/explore/src/main/scala/explore/tabs/ObsTabContents.scala index 21d568dc19..48ccf231ec 100644 --- a/explore/src/main/scala/explore/tabs/ObsTabContents.scala +++ b/explore/src/main/scala/explore/tabs/ObsTabContents.scala @@ -9,9 +9,11 @@ import crystal.* import crystal.react.* import crystal.react.hooks.* import eu.timepit.refined.types.numeric.NonNegInt +import eu.timepit.refined.types.string.NonEmptyString import explore.* import explore.Icons import explore.components.Tile +import explore.components.TileController import explore.components.ToolbarTooltipOptions import explore.components.ui.ExploreStyles import explore.data.KeyedIndexedList @@ -30,6 +32,8 @@ import explore.observationtree.* import explore.shortcuts.* import explore.shortcuts.given import explore.syntax.ui.* +import explore.targeteditor.plots.ObjectPlotData +import explore.targeteditor.plots.PlotData import explore.undo.UndoContext import explore.undo.UndoSetter import explore.utils.* @@ -48,7 +52,6 @@ import lucuma.react.resizeDetector.* import lucuma.react.resizeDetector.hooks.* import lucuma.react.table.Expandable import lucuma.react.table.Table -import lucuma.refined.* import lucuma.ui.optics.* import lucuma.ui.primereact.* import lucuma.ui.reusability.given @@ -90,7 +93,9 @@ case class ObsTabContents( private val targets: UndoSetter[TargetList] = programSummaries.zoom(ProgramSummaries.targets) private val observationIdsWithIndices: List[(Observation.Id, NonNegInt)] = observations.get.toIndexedList.map((o, idx) => (o.id, idx)) - val readonly: Boolean = programSummaries.get.proposalIsSubmitted + private val readonly: Boolean = programSummaries.get.proposalIsSubmitted + private val globalPreferences: View[GlobalPreferences] = + userPreferences.zoom(UserPreferences.globalPreferences) object ObsTabContents extends TwoPanels: private type Props = ObsTabContents @@ -117,14 +122,16 @@ object ObsTabContents extends TwoPanels: shadowClipboardObs.setStateAsync(idSet.some) case _ => IO.unit .useStateView(List.empty[Observation.Id]) // selectedObsIds - .localValBy: (props, _, _, _, _, selectedObsIds) => + .localValBy: (props, _, _, _, _, selectedObsIds) => // selectedOrFocusedObsIds props.focusedObs.map(ObsIdSet.one(_)).orElse(ObsIdSet.fromList(selectedObsIds.get)) - .useCallbackWithDepsBy((_, _, _, _, _, _, selectedObsIdSet) => selectedObsIdSet): // COPY Action Callback + .useCallbackWithDepsBy((_, _, _, _, _, _, selectedOrFocusedObsIds) => + selectedOrFocusedObsIds + ): // COPY Action Callback (_, ctx, _, _, shadowClipboardObs, _, _) => - selectedObsIdSet => + selectedOrFocusedObsIds => import ctx.given - selectedObsIdSet + selectedOrFocusedObsIds .map: obsIdSet => (ExploreClipboard .set(LocalClipboard.CopiedObservations(obsIdSet)) >> @@ -218,7 +225,7 @@ object ObsTabContents extends TwoPanels: resize, shadowClipboardObs, selectedObsIds, - _, + selectedOrFocusedObsIds, // Mixes focused obs and selected obs in table copyCallback, pasteCallback, deckShown @@ -262,9 +269,9 @@ object ObsTabContents extends TwoPanels: val backButton: VdomNode = makeBackButton(props.programId, AppTab.Observations, twoPanelState, ctx) - val observationTable: VdomNode = + val obsSummaryTableTile: Tile[?] = Tile( - "observations".refined, + ObsSummaryTabTileIds.SummaryId.id, "Observations Summary", none[Table[Expandable[ObsSummaryTable.ObsSummaryRow], Nothing]], backButton.some, @@ -283,10 +290,50 @@ object ObsTabContents extends TwoPanels: _ ), (s, _) => ObsSummaryTable.Title(s.get) - // TODO: asterism elevation view ) - def obsTiles(obsId: Observation.Id, resize: UseResizeDetectorReturn): VdomNode = + val plotData: PlotData = + PlotData: + selectedOrFocusedObsIds + .foldMap(_.idSet.toList) + .map(props.observations.get.getValue(_)) + .flattenOption + .map: obs => + obs + .asterismTracking(props.programSummaries.get.targets) + .map: tracking => + ObjectPlotData.Id(obs.id.asLeft) -> + ObjectPlotData( + NonEmptyString.unsafeFrom(s"${obs.title} (${obs.id.toString})"), + tracking, + obs.basicConfiguration.foldMap(conf => List(conf.siteFor)) + ) + .flattenOption + .toMap + + val skyPlotTile: Tile[?] = + ElevationPlotTile.elevationPlotTile( + props.vault.userId, + ObsSummaryTabTileIds.PlotId.id, + plotData, + none, // TODO Deduce site from the first target? + none, + none, + List.empty, + props.globalPreferences.get + ) + + val summaryTiles: VdomNode = + TileController( + props.vault.map(_.user.id), + resize.width.getOrElse(0), + ExploreGridLayouts.sectionLayout(GridLayoutSection.ObservationListLayout), + props.userPreferences.get.observationListTabLayout, + List(obsSummaryTableTile, skyPlotTile), + GridLayoutSection.ObservationListLayout + ) + + def obsEditorTiles(obsId: Observation.Id, resize: UseResizeDetectorReturn): VdomNode = { val indexValue = Iso.id[ObservationList].index(obsId).andThen(KeyedIndexedList.value) props.observations.model @@ -311,12 +358,13 @@ object ObsTabContents extends TwoPanels: ExploreGridLayouts.sectionLayout(GridLayoutSection.ObservationsLayout), props.userPreferences.get.observationsTabLayout, resize, - props.userPreferences.zoom(UserPreferences.globalPreferences), + props.globalPreferences, props.readonly ).withKey(s"${obsId.show}") ) + } - def groupTiles(groupId: Group.Id, resize: UseResizeDetectorReturn): VdomNode = + def groupEditorTiles(groupId: Group.Id, resize: UseResizeDetectorReturn): VdomNode = ObsGroupTiles( props.vault.userId, groupId, @@ -330,9 +378,9 @@ object ObsTabContents extends TwoPanels: def rightSide(resize: UseResizeDetectorReturn): VdomNode = (props.focusedObs, props.focusedGroup) match - case (Some(obsId), _) => obsTiles(obsId, resize) - case (_, Some(groupId)) => groupTiles(groupId, resize) - case _ => observationTable + case (Some(obsId), _) => obsEditorTiles(obsId, resize) + case (_, Some(groupId)) => groupEditorTiles(groupId, resize) + case _ => summaryTiles makeOneOrTwoPanels( twoPanelState, diff --git a/explore/src/main/scala/explore/tabs/ObsTabTiles.scala b/explore/src/main/scala/explore/tabs/ObsTabTiles.scala index d7cce4fc71..679e167398 100644 --- a/explore/src/main/scala/explore/tabs/ObsTabTiles.scala +++ b/explore/src/main/scala/explore/tabs/ObsTabTiles.scala @@ -59,7 +59,6 @@ import lucuma.core.model.Program import lucuma.core.model.Target import lucuma.core.model.TimingWindow import lucuma.core.syntax.all.* -import lucuma.core.util.Enumerated import lucuma.core.util.TimeSpan import lucuma.react.common.ReactFnProps import lucuma.react.primereact.Dropdown @@ -109,12 +108,7 @@ case class ObsTabTiles( val obsAttachmentAssignments: ObsAttachmentAssignmentMap = programSummaries.obsAttachmentAssignments val asterismTracking: Option[ObjectTracking] = - NonEmptyList - .fromList: - observation.get.scienceTargetIds.toList - .map(id => allTargets.get(id)) - .flattenOption - .map(ObjectTracking.fromAsterism(_)) + observation.get.asterismTracking(allTargets) object ObsTabTiles: private type Props = ObsTabTiles @@ -378,6 +372,23 @@ object ObsTabTiles: props.observation.undoableView[List[TimingWindow]](Observation.timingWindows) ) + val obsConf: ObsConfiguration = + ObsConfiguration( + basicConfiguration, + paProps.some, + constraints.get.some, + ScienceRequirements.spectroscopy + .getOption(props.observation.get.scienceRequirements) + .flatMap(_.wavelength), + sequenceOffsets.toOption.flatMap(_.science), + sequenceOffsets.toOption.flatMap(_.acquisition), + averagePA, + obsDuration.map(_.toDuration), + props.observation.get.needsAGS, + props.observation.get.selectedGSName, + props.observation.get.calibrationRole + ) + val plotData: Option[PlotData] = props.asterismTracking.map: tracking => PlotData: @@ -386,14 +397,15 @@ object ObsTabTiles: ObjectPlotData( NonEmptyString.from(props.obsId.toString).getOrElse("Observation".refined), tracking, - Enumerated[Site].all // In obs elevation plot, we want all solid lines + obsConf.configuration.foldMap(conf => List(conf.siteFor)) ) ) - val skyPlotTile = + val skyPlotTile: Option[Tile[?]] = plotData.map: ElevationPlotTile.elevationPlotTile( props.vault.userId, + ObsTabTileIds.PlotId.id, _, props.observation.get.observingMode.map(_.siteFor), vizTimeView.get, @@ -402,23 +414,6 @@ object ObsTabTiles: props.globalPreferences.get ) - val obsConf = - ObsConfiguration( - basicConfiguration, - paProps.some, - constraints.get.some, - ScienceRequirements.spectroscopy - .getOption(props.observation.get.scienceRequirements) - .flatMap(_.wavelength), - sequenceOffsets.toOption.flatMap(_.science), - sequenceOffsets.toOption.flatMap(_.acquisition), - averagePA, - obsDuration.map(_.toDuration), - props.observation.get.needsAGS, - props.observation.get.selectedGSName, - props.observation.get.calibrationRole - ) - def getObsInfo(obsId: Observation.Id)(targetId: Target.Id): TargetEditObsInfo = TargetEditObsInfo.fromProgramSummaries( targetId, @@ -485,7 +480,7 @@ object ObsTabTiles: // as changing the css classes on the various tiles when the dropdown is clicked to control z-index. val constraintsTile = Tile( - ObsTabTilesIds.ConstraintsId.id, + ObsTabTileIds.ConstraintsId.id, "Constraints" )( renderInTitle => diff --git a/explore/src/main/scala/explore/tabs/OverviewTabContents.scala b/explore/src/main/scala/explore/tabs/OverviewTabContents.scala index e3f2224d06..8be6fb2f1d 100644 --- a/explore/src/main/scala/explore/tabs/OverviewTabContents.scala +++ b/explore/src/main/scala/explore/tabs/OverviewTabContents.scala @@ -15,7 +15,7 @@ import explore.model.AppContext import explore.model.ExploreGridLayouts import explore.model.ObsAttachmentAssignmentMap import explore.model.ObsAttachmentList -import explore.model.ObsTabTilesIds +import explore.model.ObsTabTileIds import explore.model.ObservationList import explore.model.enums.GridLayoutSection import explore.model.layout.LayoutsMap @@ -53,7 +53,7 @@ object OverviewTabContents { val defaultLayouts = ExploreGridLayouts.sectionLayout(GridLayoutSection.OverviewLayout) val warningsAndErrorsTile = Tile( - ObsTabTilesIds.WarningsAndErrorsId.id, + ObsTabTileIds.WarningsAndErrorsId.id, "Warnings And Errors", ObservationValidationsTableTileState(_ => Callback.empty) )(ObservationValidationsTableBody(props.programId, props.observations, _), @@ -63,7 +63,7 @@ object OverviewTabContents { val obsAttachmentsTile = props.userVault .map(vault => Tile( - ObsTabTilesIds.ObsAttachmentsId.id, + ObsTabTileIds.ObsAttachmentsId.id, "Observation Attachments", ObsAttachmentsTableTileState() )( diff --git a/explore/src/main/scala/explore/tabs/SiderealTargetEditorTile.scala b/explore/src/main/scala/explore/tabs/SiderealTargetEditorTile.scala index a381ee1d3a..efdab24b41 100644 --- a/explore/src/main/scala/explore/tabs/SiderealTargetEditorTile.scala +++ b/explore/src/main/scala/explore/tabs/SiderealTargetEditorTile.scala @@ -11,7 +11,7 @@ import explore.model.AladinFullScreen import explore.model.Asterism import explore.model.GlobalPreferences import explore.model.GuideStarSelection -import explore.model.ObsTabTilesIds +import explore.model.ObsTabTileIds import explore.model.ObservationsAndTargets import explore.model.OnCloneParameters import explore.model.TargetEditObsInfo @@ -44,7 +44,7 @@ object SiderealTargetEditorTile: backButton: Option[VdomNode] = none ) = Tile( - ObsTabTilesIds.TargetId.id, + ObsTabTileIds.TargetId.id, title, back = backButton, bodyClass = ExploreStyles.TargetTileBody diff --git a/explore/src/main/scala/explore/tabs/TargetTabContents.scala b/explore/src/main/scala/explore/tabs/TargetTabContents.scala index 5aca92d5ef..11ec50a0c6 100644 --- a/explore/src/main/scala/explore/tabs/TargetTabContents.scala +++ b/explore/src/main/scala/explore/tabs/TargetTabContents.scala @@ -333,7 +333,7 @@ object TargetTabContents extends TwoPanels: * Render the summary table. */ val renderSummary: Tile[TargetSummaryTileState] = Tile( - ObsTabTilesIds.TargetSummaryId.id, + ObsTabTileIds.TargetSummaryId.id, "Target Summary", TargetSummaryTileState(Nil, none), backButton.some @@ -580,6 +580,7 @@ object TargetTabContents extends TwoPanels: val skyPlotTile: Tile[?] = ElevationPlotTile.elevationPlotTile( props.userId, + TargetTabTileIds.ElevationPlot.id, plotData, configuration.map(_.siteFor), obsTimeView.get, @@ -593,9 +594,9 @@ object TargetTabContents extends TwoPanels: // We still want to render these 2 tiles, even when not shown, so as not to mess up the stored layout. val dummyTargetTile: Tile[Unit] = - Tile(ObsTabTilesIds.TargetId.id, "", hidden = true)(_ => EmptyVdom) + Tile(ObsTabTileIds.TargetId.id, "", hidden = true)(_ => EmptyVdom) val dummyElevationTile: Tile[Unit] = - Tile(ObsTabTilesIds.PlotId.id, "", hidden = true)(_ => EmptyVdom) + Tile(ObsTabTileIds.PlotId.id, "", hidden = true)(_ => EmptyVdom) /** * Renders a single sidereal target editor without an obs context @@ -633,6 +634,7 @@ object TargetTabContents extends TwoPanels: val skyPlotTile: Tile[?] = ElevationPlotTile.elevationPlotTile( props.userId, + TargetTabTileIds.ElevationPlot.id, plotData, selectedTargetIds.get.headOption.flatMap(props.sitesForTarget(_).headOption), none, @@ -672,9 +674,9 @@ object TargetTabContents extends TwoPanels: val (tiles, key) = observationSetTargetEditorTile .map: - (_, TargetTabControllerIds.AsterismEditor.id) + (_, TargetTabTileIds.AsterismEditor.id) .getOrElse: - (selectedTargetsTiles, TargetTabControllerIds.Summary.id) + (selectedTargetsTiles, TargetTabTileIds.Summary.id) TileController( props.userId, diff --git a/explore/src/main/scala/explore/timingwindows/TimingWindowsTile.scala b/explore/src/main/scala/explore/timingwindows/TimingWindowsTile.scala index 8f5bb0ce78..0cabe4a329 100644 --- a/explore/src/main/scala/explore/timingwindows/TimingWindowsTile.scala +++ b/explore/src/main/scala/explore/timingwindows/TimingWindowsTile.scala @@ -13,7 +13,7 @@ import explore.Icons import explore.components.Tile import explore.components.ui.ExploreStyles import explore.model.Constants.BadTimingWindow -import explore.model.ObsTabTilesIds +import explore.model.ObsTabTileIds import explore.model.enums.TileSizeState import explore.model.formats.* import explore.model.reusability.given @@ -75,7 +75,7 @@ object TimingWindowsTile: val base = "Scheduling Windows" val title = if (timingWindows.get.isEmpty) base else s"$base (${timingWindows.get.length})" - Tile(ObsTabTilesIds.TimingWindowsId.id, + Tile(ObsTabTileIds.TimingWindowsId.id, title, TimingWindowsTileState(), canMaximize = !fullSize, diff --git a/hasura/user-prefs/metadata/databases/databases.yaml b/hasura/user-prefs/metadata/databases/databases.yaml index 269c3300b1..fcf83f6cd2 100644 --- a/hasura/user-prefs/metadata/databases/databases.yaml +++ b/hasura/user-prefs/metadata/databases/databases.yaml @@ -11,4 +11,4 @@ max_connections: 15 retries: 1 use_prepared_statements: true - tables: "!include default/tables/tables.yaml" + tables: '!include default/tables/tables.yaml' diff --git a/hasura/user-prefs/metadata/databases/default/tables/public_exploreAsterismPreferences.yaml b/hasura/user-prefs/metadata/databases/default/tables/public_exploreAsterismPreferences.yaml index bdff741018..11e2419c57 100644 --- a/hasura/user-prefs/metadata/databases/default/tables/public_exploreAsterismPreferences.yaml +++ b/hasura/user-prefs/metadata/databases/default/tables/public_exploreAsterismPreferences.yaml @@ -2,14 +2,14 @@ table: name: exploreAsterismPreferences schema: public object_relationships: -- name: lucumaUser - using: - foreign_key_constraint_on: userId + - name: lucumaUser + using: + foreign_key_constraint_on: userId array_relationships: -- name: lucumaAsterisms - using: - foreign_key_constraint_on: - column: prefId - table: - name: lucumaAsterism - schema: public + - name: lucumaAsterisms + using: + foreign_key_constraint_on: + column: prefId + table: + name: lucumaAsterism + schema: public diff --git a/hasura/user-prefs/metadata/databases/default/tables/public_exploreFinderChart.yaml b/hasura/user-prefs/metadata/databases/default/tables/public_exploreFinderChart.yaml index 4fe4a9c9fe..d88d814b62 100644 --- a/hasura/user-prefs/metadata/databases/default/tables/public_exploreFinderChart.yaml +++ b/hasura/user-prefs/metadata/databases/default/tables/public_exploreFinderChart.yaml @@ -2,6 +2,6 @@ table: name: exploreFinderChart schema: public object_relationships: -- name: lucumaObservation - using: - foreign_key_constraint_on: observationId + - name: lucumaObservation + using: + foreign_key_constraint_on: observationId diff --git a/hasura/user-prefs/metadata/databases/default/tables/public_lucumaAsterism.yaml b/hasura/user-prefs/metadata/databases/default/tables/public_lucumaAsterism.yaml index e33f906904..4f4944a19c 100644 --- a/hasura/user-prefs/metadata/databases/default/tables/public_lucumaAsterism.yaml +++ b/hasura/user-prefs/metadata/databases/default/tables/public_lucumaAsterism.yaml @@ -2,6 +2,6 @@ table: name: lucumaAsterism schema: public object_relationships: -- name: exploreAsterismPreference - using: - foreign_key_constraint_on: prefId + - name: exploreAsterismPreference + using: + foreign_key_constraint_on: prefId diff --git a/hasura/user-prefs/metadata/databases/default/tables/public_lucumaItcPlotPreferences.yaml b/hasura/user-prefs/metadata/databases/default/tables/public_lucumaItcPlotPreferences.yaml index de27199be6..3c42e2981e 100644 --- a/hasura/user-prefs/metadata/databases/default/tables/public_lucumaItcPlotPreferences.yaml +++ b/hasura/user-prefs/metadata/databases/default/tables/public_lucumaItcPlotPreferences.yaml @@ -2,6 +2,6 @@ table: name: lucumaItcPlotPreferences schema: public object_relationships: -- name: lucuma_observation - using: - foreign_key_constraint_on: observationId + - name: lucuma_observation + using: + foreign_key_constraint_on: observationId diff --git a/hasura/user-prefs/metadata/databases/default/tables/public_lucumaObservation.yaml b/hasura/user-prefs/metadata/databases/default/tables/public_lucumaObservation.yaml index 049330dfde..0e6d3d3e7a 100644 --- a/hasura/user-prefs/metadata/databases/default/tables/public_lucumaObservation.yaml +++ b/hasura/user-prefs/metadata/databases/default/tables/public_lucumaObservation.yaml @@ -2,10 +2,10 @@ table: name: lucumaObservation schema: public array_relationships: -- name: exploreFinderCharts - using: - foreign_key_constraint_on: - column: observationId - table: - name: exploreFinderChart - schema: public + - name: exploreFinderCharts + using: + foreign_key_constraint_on: + column: observationId + table: + name: exploreFinderChart + schema: public diff --git a/hasura/user-prefs/metadata/databases/default/tables/public_lucumaUserPreferences.yaml b/hasura/user-prefs/metadata/databases/default/tables/public_lucumaUserPreferences.yaml index d7e466a700..5cab1eb3b3 100644 --- a/hasura/user-prefs/metadata/databases/default/tables/public_lucumaUserPreferences.yaml +++ b/hasura/user-prefs/metadata/databases/default/tables/public_lucumaUserPreferences.yaml @@ -2,6 +2,6 @@ table: name: lucumaUserPreferences schema: public object_relationships: -- name: lucuma_user - using: - foreign_key_constraint_on: userId + - name: lucuma_user + using: + foreign_key_constraint_on: userId diff --git a/hasura/user-prefs/metadata/databases/default/tables/tables.yaml b/hasura/user-prefs/metadata/databases/default/tables/tables.yaml index 169f2d748e..d31797aacd 100644 --- a/hasura/user-prefs/metadata/databases/default/tables/tables.yaml +++ b/hasura/user-prefs/metadata/databases/default/tables/tables.yaml @@ -1,16 +1,16 @@ -- "!include public_ItcChartType.yaml" -- "!include public_exploreAsterismPreferences.yaml" -- "!include public_exploreFinderChart.yaml" -- "!include public_explorePlotRange.yaml" -- "!include public_explorePlotTime.yaml" -- "!include public_lucumaAsterism.yaml" -- "!include public_lucumaGridBreakpointName.yaml" -- "!include public_lucumaGridLayoutId.yaml" -- "!include public_lucumaGridLayoutPositions.yaml" -- "!include public_lucumaObservation.yaml" -- "!include public_lucumaSortDirection.yaml" -- "!include public_lucumaTableColumnPreferences.yaml" -- "!include public_lucumaTableIds.yaml" -- "!include public_lucumaUser.yaml" -- "!include public_lucumaUserPreferences.yaml" -- "!include public_lucumaWavelengthUnits.yaml" +- '!include public_ItcChartType.yaml' +- '!include public_exploreAsterismPreferences.yaml' +- '!include public_exploreFinderChart.yaml' +- '!include public_explorePlotRange.yaml' +- '!include public_explorePlotTime.yaml' +- '!include public_lucumaAsterism.yaml' +- '!include public_lucumaGridBreakpointName.yaml' +- '!include public_lucumaGridLayoutId.yaml' +- '!include public_lucumaGridLayoutPositions.yaml' +- '!include public_lucumaObservation.yaml' +- '!include public_lucumaSortDirection.yaml' +- '!include public_lucumaTableColumnPreferences.yaml' +- '!include public_lucumaTableIds.yaml' +- '!include public_lucumaUser.yaml' +- '!include public_lucumaUserPreferences.yaml' +- '!include public_lucumaWavelengthUnits.yaml' diff --git a/model/shared/src/main/scala/explore/model/Observation.scala b/model/shared/src/main/scala/explore/model/Observation.scala index 927d0ae5bc..24754940f2 100644 --- a/model/shared/src/main/scala/explore/model/Observation.scala +++ b/model/shared/src/main/scala/explore/model/Observation.scala @@ -26,6 +26,7 @@ import lucuma.core.enums.ScienceBand import lucuma.core.math.Wavelength import lucuma.core.model.Configuration import lucuma.core.model.ConstraintSet +import lucuma.core.model.ObjectTracking import lucuma.core.model.ObsAttachment import lucuma.core.model.ObservationValidation import lucuma.core.model.ObservationWorkflow @@ -215,6 +216,14 @@ case class Observation( if (configurationApplies(config)) updateNeedsApprovalToPending else this + def asterismTracking(allTargets: TargetList): Option[ObjectTracking] = + NonEmptyList + .fromList: + scienceTargetIds.toList + .map(id => allTargets.get(id)) + .flattenOption + .map(ObjectTracking.fromAsterism(_)) + object Observation: type Id = lucuma.core.model.Observation.Id val Id = lucuma.core.model.Observation.Id diff --git a/model/shared/src/main/scala/explore/model/TileIds.scala b/model/shared/src/main/scala/explore/model/TileIds.scala index 3c412760da..b6b4f07cca 100644 --- a/model/shared/src/main/scala/explore/model/TileIds.scala +++ b/model/shared/src/main/scala/explore/model/TileIds.scala @@ -6,7 +6,7 @@ package explore.model import eu.timepit.refined.types.string.NonEmptyString import lucuma.refined.* -enum ObsTabTilesIds: +enum ObsTabTileIds: case NotesId, TargetSummaryId, TargetId, PlotId, ConstraintsId, ConfigurationId, ItcId, TimingWindowsId, WarningsAndErrorsId, ObsAttachmentsId, FinderChartsId, SequenceId @@ -24,6 +24,13 @@ enum ObsTabTilesIds: case FinderChartsId => "finderCharts".refined case SequenceId => "sequence".refined +enum ObsSummaryTabTileIds: + case SummaryId, PlotId + + def id: NonEmptyString = this match + case SummaryId => "summary".refined + case PlotId => "plot".refined + enum ProgramTabTileIds: case DetailsId, NotesId, ChangeRequestsId @@ -41,13 +48,14 @@ enum ProposalTabTileIds: case AbstractId => "proposalAbstract".refined case AttachmentsId => "proposalAttachments".refined -enum GroupEditIds: +enum GroupEditTileIds: case GroupEditId, GroupNotesId def id: NonEmptyString = this match case GroupEditId => "groupEdit".refined case GroupNotesId => "groupNotes".refined -enum TargetTabControllerIds(val id: NonEmptyString): - case Summary extends TargetTabControllerIds("target-summary-controller".refined) - case AsterismEditor extends TargetTabControllerIds("target-obs-controller".refined) +enum TargetTabTileIds(val id: NonEmptyString): + case Summary extends TargetTabTileIds("targetSummary".refined) + case AsterismEditor extends TargetTabTileIds("targetEditor".refined) + case ElevationPlot extends TargetTabTileIds("targetPlot".refined) diff --git a/model/shared/src/main/scala/explore/model/enums/GridLayoutSection.scala b/model/shared/src/main/scala/explore/model/enums/GridLayoutSection.scala index 2433b7e930..09ff471ae6 100644 --- a/model/shared/src/main/scala/explore/model/enums/GridLayoutSection.scala +++ b/model/shared/src/main/scala/explore/model/enums/GridLayoutSection.scala @@ -6,13 +6,14 @@ package explore.model.enums import lucuma.core.util.Enumerated enum GridLayoutSection(val value: String) derives Enumerated: - case ProgramsLayout extends GridLayoutSection("programs") - case ObservationsLayout extends GridLayoutSection("observations") - case TargetLayout extends GridLayoutSection("targets") - case ConstraintsLayout extends GridLayoutSection("constraints") - case SchedulingLayout extends GridLayoutSection("scheduling") - case OverviewLayout extends GridLayoutSection("overview") - case ProposalLayout extends GridLayoutSection("proposal") - case GroupEditLayout extends GridLayoutSection("groupedit") + case ProgramsLayout extends GridLayoutSection("programs") + case ObservationsLayout extends GridLayoutSection("observations") + case ObservationListLayout extends GridLayoutSection("observation_list") + case TargetLayout extends GridLayoutSection("targets") + case ConstraintsLayout extends GridLayoutSection("constraints") + case SchedulingLayout extends GridLayoutSection("scheduling") + case OverviewLayout extends GridLayoutSection("overview") + case ProposalLayout extends GridLayoutSection("proposal") + case GroupEditLayout extends GridLayoutSection("groupedit") private val tag = value