1- import h from "./main.module.scss " ;
2- import { Spinner , Switch , AnchorButton } from "@blueprintjs/core" ;
1+ import h from "./main.module.sass " ;
2+ import { Switch , AnchorButton , Icon } from "@blueprintjs/core" ;
33import { ContentPage } from "~/layouts" ;
44import {
5- PageHeader ,
65 DevLinkButton ,
76 AssistantLinks ,
87 LinkCard ,
98 StickyHeader ,
109} from "~/components" ;
11- import { useState , useRef , useEffect } from "react" ;
12- import { useAPIResult } from "@macrostrat/ui-components" ;
10+ import { useState } from "react" ;
11+ import { PostgRESTInfiniteScrollView } from "@macrostrat/ui-components" ;
1312import { apiDomain } from "@macrostrat-web/settings" ;
1413import { IDTag , SearchBar } from "~/components/general" ;
1514import { useData } from "vike-react/useData" ;
1615import { PageBreadcrumbs } from "~/components" ;
1716
17+ const PAGE_SIZE = 20 ;
18+
1819export function Page ( ) {
1920 const { sources } = useData ( ) ;
2021
21- const [ input , setInput ] = useState ( "" ) ;
2222 const [ activeOnly , setActiveOnly ] = useState ( true ) ;
23- const [ recentOrder , setRecentOrder ] = useState ( false ) ;
24-
25- const startingID = sources [ sources . length - 1 ] . source_id ;
26- const [ key , setKey ] = useState ( {
27- lastID : startingID ,
28- lastYear : 9999 ,
29- } ) ;
30- const [ data , setData ] = useState ( sources ) ;
31- const pageSize = 20 ;
32-
33- const result = useSourceData (
34- key . lastID ,
35- input ,
36- pageSize ,
37- activeOnly ,
38- recentOrder ,
39- key . lastYear
40- ) ;
41-
42- useEffect ( ( ) => {
43- if (
44- result &&
45- data [ data . length - 1 ] ?. source_id !== result [ result . length - 1 ] ?. source_id
46- ) {
47- setData ( ( prevData ) => {
48- return [ ...prevData , ...result ] ;
49- } ) ;
50- }
51- } , [ result ] ) ;
52-
53- const resetData = ( ) => {
54- window . scrollTo ( 0 , 0 ) ;
55- setData ( [ ] ) ;
56- setKey ( {
57- lastID : 0 ,
58- lastYear : 9999 ,
59- } ) ;
60- } ;
61-
62- const handleInputChange = ( event ) => {
63- setInput ( event . toLowerCase ( ) ) ;
64- resetData ( ) ;
65- } ;
66-
67- const handleActiveChange = ( ) => {
68- setActiveOnly ( ! activeOnly ) ;
69- resetData ( ) ;
70- } ;
71-
72- const handleRecentChange = ( ) => {
73- setRecentOrder ( ! recentOrder ) ;
74- resetData ( ) ;
75- } ;
76-
77- return h ( "div.maps-page" , [
78- h ( ContentPage , [
79- h ( StickyHeader , { className : "header-container" } , [
80- h ( "div.header" , [
81- h ( PageBreadcrumbs , {
82- title : "Maps" ,
83- } ) ,
84- h ( SearchBar , {
85- placeholder : "Filter by name..." ,
86- onChange : handleInputChange ,
87- } ) ,
88- h ( Switch , {
89- label : "Active only" ,
90- checked : activeOnly ,
91- onChange : handleActiveChange ,
92- } ) ,
93- h ( Switch , {
94- label : "Recent order" ,
95- checked : recentOrder ,
96- onChange : handleRecentChange ,
97- } ) ,
98- ] ) ,
99- h ( AssistantLinks , { className : "assistant-links" } , [
100- h (
101- AnchorButton ,
102- { icon : "flows" , href : "/maps/ingestion" } ,
103- "Ingestion system"
23+ const [ recentOrder , setRecentOrder ] = useState ( true ) ;
24+
25+ const key = "" + activeOnly + recentOrder ; // should be able to remove on next release (4.3.7)
26+
27+ const initialItems = recentOrder && activeOnly ? sources : undefined ;
28+
29+ return h ( "div.maps-list-page" , [
30+ h ( ContentPage , [
31+ h ( "div.flex-row" , [
32+ h ( 'div.main' , [
33+ h ( StickyHeader , { className : "header-container" } , [
34+ h ( "div.header" , [
35+ h ( PageBreadcrumbs , {
36+ title : "Maps" ,
37+ showLogo : true ,
38+ } ) ,
39+ h ( 'div.search' , [
40+ h ( 'div.switches' , [
41+ h ( Switch , {
42+ label : "Active only" ,
43+ checked : activeOnly ,
44+ onChange : ( ) => {
45+ window . scrollTo ( 0 , 0 ) ;
46+ setActiveOnly ( ! activeOnly ) ;
47+ } ,
48+ } ) ,
49+ h ( Switch , {
50+ label : "Recent order" ,
51+ checked : recentOrder ,
52+ onChange : ( ) => {
53+ window . scrollTo ( 0 , 0 ) ;
54+ setRecentOrder ( ! recentOrder )
55+ } ,
56+ } ) ,
57+ ] ) ,
58+ ] ) ,
59+ ] ) ,
60+ ] ) ,
61+ h ( PostgRESTInfiniteScrollView , {
62+ route : `${ apiDomain } /api/pg/sources_metadata` ,
63+ id_key : "source_id" ,
64+ order_key : recentOrder ? "ref_year" : undefined ,
65+ filterable : true ,
66+ extraParams : {
67+ is_finalized : activeOnly ? "eq.true" : undefined ,
68+ } ,
69+ ascending : ! recentOrder ,
70+ limit : PAGE_SIZE ,
71+ itemComponent : SourceItem ,
72+ initialItems,
73+ key
74+ } )
75+ ] ) ,
76+ h ( "div.sidebar" ,
77+ h ( "div.sidebar-content" , [
78+ h ( AssistantLinks , { className : "assistant-links" } , [
79+ h (
80+ AnchorButton ,
81+ { icon : "flows" , href : "/maps/ingestion" } ,
82+ "Ingestion system"
83+ ) ,
84+ h ( AnchorButton , { icon : "map" , href : "/map/sources" } , "Show on map" ) ,
85+ h ( DevLinkButton , { href : "/maps/legend" } , "Legend table" ) ,
86+ ] ) ,
87+ ] )
10488 ) ,
105- h ( AnchorButton , { icon : "map" , href : "/map/sources" } , "Show on map" ) ,
106- h ( DevLinkButton , { href : "/maps/legend" } , "Legend table" ) ,
107- ] ) ,
89+ ] )
10890 ] ) ,
109- h (
110- "div.strat-list" ,
111- h (
112- "div.strat-items" ,
113- data . map ( ( data ) => h ( SourceItem , { data } ) )
114- )
115- ) ,
116- LoadMoreTrigger ( { data, setKey, pageSize, result } ) ,
117- ] ) ,
118- ] ) ;
119- }
120-
121- function useSourceData (
122- lastID ,
123- input ,
124- pageSize ,
125- activeOnly ,
126- recentOrder ,
127- lastYear
128- ) {
129- const url = `${ apiDomain } /api/pg/sources_metadata` ;
130-
131- const result = useAPIResult ( url , {
132- is_finalized : activeOnly ? "eq.true" : undefined ,
133- status_code : activeOnly ? "eq.active" : undefined ,
134- source_id : ! recentOrder ? `gt.${ lastID } ` : undefined ,
135- or : recentOrder
136- ? `(ref_year.lt.${ lastYear } ,and(ref_year.eq.${ lastYear } ,source_id.gt.${ lastID } ))`
137- : undefined ,
138- name : `ilike.%${ input } %` ,
139- limit : pageSize ,
140- order : recentOrder ? "ref_year.desc,source_id.asc" : "source_id.asc" ,
141- } ) ;
142- return result ;
143- }
144-
145- function LoadMoreTrigger ( { data, setKey, pageSize, result } ) {
146- const ref = useRef ( null ) ;
147-
148- useEffect ( ( ) => {
149- if ( ! ref . current ) return ;
150-
151- const observer = new IntersectionObserver ( ( [ entry ] ) => {
152- if ( entry . isIntersecting ) {
153- if ( data . length > 0 ) {
154- const id = data [ data . length - 1 ] . source_id ;
155- const year = data [ data . length - 1 ] . ref_year || 9999 ;
156-
157- setKey ( {
158- lastID : id ,
159- lastYear : year ,
160- } ) ;
161- }
162- }
163- } ) ;
164-
165- observer . observe ( ref . current ) ;
166-
167- return ( ) => observer . disconnect ( ) ;
168- } , [ data ] ) ;
169-
170- return h . if ( result ?. length == pageSize ) ( "div.load-more" , { ref } , h ( Spinner ) ) ;
91+ ] ) ;
17192}
17293
17394function SourceItem ( { data } ) {
174- const { source_id, name, ref_title, url, scale, ref_year } = data ;
95+ const { source_id, name, ref_title, url, scale, ref_year, ref_source } = data ;
17596 const href = `/maps/${ source_id } ` ;
17697
17798 return h (
@@ -185,7 +106,12 @@ function SourceItem({ data }) {
185106 } ,
186107 [
187108 h ( "div.content" , [
188- h ( "a" , { href : url , target : "_blank" } , ref_title ) ,
109+ h ( 'div.source' , [
110+ h ( "span" , ref_source + ": " + ref_title + " (" + ref_year + ") " ) ,
111+ h ( "a" , { href : url , target : "_blank" } ,
112+ h ( Icon , { icon : "link" } ) ,
113+ ) ,
114+ ] ) ,
189115 h ( "div.tags" , [ h ( IDTag , { id : source_id } ) ] ) ,
190116 ] ) ,
191117 ]
0 commit comments