1- import type { MouseEventHandler } from "react" ;
21import { useState } from "react" ;
32import { useCookies } from "react-cookie" ;
4- import { exampleAccount , SessionsPage } from "@/components" ;
5- import type { PaginatedMeta , Session , Uuid } from "@/primer-api" ;
3+ import {
4+ exampleAccount ,
5+ SessionList ,
6+ SessionNameModal ,
7+ SessionsNavBar ,
8+ SimplePaginationBar ,
9+ } from "@/components" ;
10+ import type { Uuid } from "@/primer-api" ;
611import {
712 useGetSessionList ,
813 useCreateSession ,
@@ -15,6 +20,12 @@ import { useQueryClient } from "@tanstack/react-query";
1520const ChooseSession = ( ) : JSX . Element => {
1621 const [ cookies ] = useCookies ( [ "id" ] ) ;
1722
23+ const [ importPrelude , setImportPrelude ] = useState ( true ) ;
24+ const [ showModal , setShowModal ] = useState ( false ) ;
25+ const onClickNewProgram = ( ) : void => {
26+ setShowModal ( true ) ;
27+ } ;
28+
1829 // NOTE: pagination in our API is 1-indexed.
1930 const [ page , setPage ] = useState ( 1 ) ;
2031 const [ pageSize ] = useState ( 20 ) ;
@@ -68,64 +79,88 @@ const ChooseSession = (): JSX.Element => {
6879 }
6980 ) ;
7081
71- // Note that we show data if it's available, without first checking for
82+ // Note that we show data if it's available, regardless of the status of
7283 // `isLoading` or `isError`. This means we may show stale data, but we prefer
73- // this over showing a loading message or an error for short server outages. See:
84+ // this over showing a loading message or an error for short server outages.
85+ // See:
7486 //
7587 // https://tkdodo.eu/blog/status-checks-in-react-query
7688 //
7789 // Note that React Query will not show stale data indefinitely, and will
7890 // eventually show an error message if the data is stale for too long.
7991
80- if ( data ) {
81- const sessions : Session [ ] = data . items ;
82- const meta : PaginatedMeta = data . meta ;
83- const startIndex : number = ( meta . thisPage - 1 ) * meta . pageSize + 1 ;
84-
85- const onClickNextPage : MouseEventHandler < unknown > | undefined =
86- meta . thisPage < meta . lastPage ? ( ) => setPage ( page + 1 ) : undefined ;
87- const onClickPreviousPage : MouseEventHandler < unknown > | undefined =
88- meta . thisPage > 1 ? ( ) => setPage ( page - 1 ) : undefined ;
89-
90- return (
91- < SessionsPage
92- account = { { ...exampleAccount , id : cookies . id } }
93- sessions = { sessions }
94- startIndex = { startIndex }
95- numItems = { meta . pageSize }
96- totalItems = { meta . totalItems }
97- onClickNewProgram = { ( name : string , importPrelude : boolean ) =>
98- newSession . mutate ( { data : { name, importPrelude } } )
99- }
100- onClickNextPage = { onClickNextPage }
101- onClickPreviousPage = { onClickPreviousPage }
102- onClickDelete = { ( sessionId ) => deleteSession . mutate ( { sessionId } ) }
103- onSubmitSearch = { ( nameFilter : string ) => {
104- // Unlike `onChangeSearch`, this callback is always triggered
105- // by an explicit action, and never by, e.g., a page refresh,
106- // so we always want to reset the page when this callback is
107- // invoked.
108- setSessionNameFilter ( nameFilter ) ;
109- setPage ( 1 ) ;
110- } }
111- onChangeSearch = { ( nameFilter : string ) => {
112- // For technical reasons, this callback may be triggered even
113- // if the value of the search term didn't actually change
114- // (e.g., because the page is redrawn), and in these cases, we
115- // don't want to update the page, so we filter these spurious
116- // "changes" out.
117- if ( nameFilter != sessionNameFilter ) {
92+ return (
93+ < div className = "relative grid h-[100dvh] grid-cols-1 grid-rows-[auto,1fr] overflow-hidden" >
94+ < div className = "relative z-40 px-1 shadow-md lg:px-4" >
95+ < SessionsNavBar
96+ onClickNewProgram = { onClickNewProgram }
97+ account = { { ...exampleAccount , id : cookies . id } }
98+ onSubmitSearch = { ( nameFilter : string ) => {
99+ // Unlike `onChangeSearch`, this callback is always triggered
100+ // by an explicit action, and never by, e.g., a page refresh,
101+ // so we always want to reset the page when this callback is
102+ // invoked.
118103 setSessionNameFilter ( nameFilter ) ;
119104 setPage ( 1 ) ;
120- }
105+ } }
106+ onChangeSearch = { ( nameFilter : string ) => {
107+ // For technical reasons, this callback may be triggered even
108+ // if the value of the search term didn't actually change
109+ // (e.g., because the page is redrawn), and in these cases, we
110+ // don't want to update the page, so we filter these spurious
111+ // "changes" out.
112+ if ( nameFilter != sessionNameFilter ) {
113+ setSessionNameFilter ( nameFilter ) ;
114+ setPage ( 1 ) ;
115+ }
116+ } }
117+ />
118+ </ div >
119+ < div className = "max-h-screen overflow-auto rounded-sm bg-grey-primary p-3 shadow-inner" >
120+ { data ? (
121+ < SessionList
122+ sessions = { data . items }
123+ onClickDelete = { ( sessionId ) => deleteSession . mutate ( { sessionId } ) }
124+ />
125+ ) : isError ? (
126+ < div > Error: { error . message } </ div >
127+ ) : (
128+ < div > Loading...</ div >
129+ ) }
130+ </ div >
131+ < div className = "relative z-40 px-1 shadow-2xl lg:px-4" >
132+ { data && (
133+ < SimplePaginationBar
134+ itemNamePlural = "sessions"
135+ startIndex = { ( data . meta . thisPage - 1 ) * data . meta . pageSize + 1 }
136+ numItems = { data . items . length }
137+ totalItems = { data . meta . totalItems }
138+ onClickNextPage = {
139+ data . meta . thisPage < data . meta . lastPage
140+ ? ( ) => setPage ( page + 1 )
141+ : undefined
142+ }
143+ onClickPreviousPage = {
144+ data . meta . thisPage > 1 ? ( ) => setPage ( page - 1 ) : undefined
145+ }
146+ />
147+ ) }
148+ </ div >
149+ < SessionNameModal
150+ open = { showModal }
151+ importPrelude = { importPrelude }
152+ onClose = { ( ) => setShowModal ( false ) }
153+ onCancel = { ( ) => setShowModal ( false ) }
154+ onSubmit = { ( name : string , _importPrelude : boolean ) => {
155+ // Remember the student's choice of whether or not to import the Prelude.
156+ setImportPrelude ( _importPrelude ) ;
157+ newSession . mutate ( {
158+ data : { name, importPrelude : _importPrelude } ,
159+ } ) ;
121160 } }
122161 />
123- ) ;
124- } else if ( isError ) {
125- return < div > Error: { error . message } </ div > ;
126- } else {
127- return < div > Loading...</ div > ;
128- }
162+ </ div >
163+ ) ;
129164} ;
130165
131166export default ChooseSession ;
0 commit comments