1- import React , { useState } from "react" ;
1+ import hyper from "@macrostrat/hyper" ;
2+ import styles from "./main.module.sass" ;
3+ import React , { useState , useEffect , useRef , useMemo } from "react" ;
4+
25import { ContentPage } from "~/layouts" ;
36import {
47 Link ,
58 DevLinkButton ,
69 PageBreadcrumbs ,
710 StickyHeader ,
811} from "~/components" ;
9- import { AnchorButton , ButtonGroup } from "@blueprintjs/core" ;
10- import { Tag } from "@blueprintjs/core" ;
11- import hyper from "@macrostrat/hyper" ;
12- import styles from "./main.module.sass" ;
12+ import { FlexRow } from "~/components/lex/tag" ;
13+ import { SearchBar } from "~/components/general" ;
14+ import { getGroupedColumns } from "./grouped-cols" ;
1315
16+ import { AnchorButton , ButtonGroup , Switch } from "@blueprintjs/core" ;
17+ import { Tag , Icon } from "@blueprintjs/core" ;
1418import { useData } from "vike-react/useData" ;
1519import { ClientOnly } from "vike-react/ClientOnly" ;
1620import { navigate } from "vike/client/router" ;
17- import { SearchBar } from "~/components/general" ;
21+
22+ import { LexSelection } from "@macrostrat/form-components" ;
23+ import { postgrestPrefix } from "@macrostrat-web/settings" ;
24+ import { useAPIResult } from "@macrostrat/ui-components"
25+ import { cdrPrefix } from "packages/settings" ;
1826
1927const h = hyper . styled ( styles ) ;
2028
@@ -35,69 +43,132 @@ function ColumnMapContainer(props) {
3543}
3644
3745function ColumnListPage ( { title = "Columns" , linkPrefix = "/" } ) {
38- const { columnGroups, project } = useData ( ) ;
46+ const { allColumnGroups, project } = useData ( ) ;
47+
48+ const [ columnGroups , setColumnGroups ] = useState ( null ) ;
49+ const [ loading , setLoading ] = useState ( false ) ;
50+ const [ extraParams , setExtraParams ] = useState ( { } ) ;
3951
4052 const [ columnInput , setColumnInput ] = useState ( "" ) ;
41- const shouldFilter = columnInput . length >= 3 ;
42-
43- const filteredGroups = shouldFilter
44- ? columnGroups ?. filter ( ( group ) => {
45- const filteredColumns = group . columns . filter ( ( col ) => {
46- const name = col . col_name . toLowerCase ( ) ;
47- const input = columnInput . toLowerCase ( ) ;
48- return name . includes ( input ) ;
49- } ) ;
50-
51- if (
52- filteredColumns . length > 0 ||
53- group . name . toLowerCase ( ) . includes ( columnInput . toLowerCase ( ) )
54- ) {
55- return { ...group , columns : filteredColumns } ;
56- }
57-
58- return false ;
59- } )
60- : null ;
61-
62- const columnIDs = filteredGroups ?. flatMap ( ( item ) =>
63- item . columns
64- . filter ( ( col ) =>
65- col . col_name . toLowerCase ( ) . includes ( columnInput . toLowerCase ( ) )
66- )
67- . map ( ( col ) => col . col_id )
68- ) ;
53+ const [ showEmpty , setShowEmpty ] = useState ( true ) ;
54+ const [ filteredInput , setFilteredInput ] = useState ( "" ) ;
55+ const [ showInProcess , setShowInProcess ] = useState ( true ) ;
56+
57+ const [ selectedLiths , setSelectedLiths ] = useState ( null ) ;
58+ const [ selectedUnits , setSelectedUnits ] = useState ( null ) ;
59+ const [ selectedStratNames , setSelectedStratNames ] = useState ( null ) ;
60+
61+ const isEmpty = Object . keys ( extraParams ) . length === 0 ;
62+ const filteredGroups = isEmpty ? allColumnGroups : columnGroups ?? [ ] ;
63+
64+ useEffect ( ( ) => {
65+ const params : any = { } ;
66+
67+ if ( filteredInput . length >= 3 ) {
68+ params . name = `ilike.%${ filteredInput } %` ;
69+ }
70+ if ( ! showEmpty ) {
71+ params . empty = `is.false` ;
72+ }
73+ if ( ! showInProcess ) {
74+ params . status_code = 'eq.active' ;
75+ }
76+ if ( selectedLiths ) {
77+ params . liths = `cs.[${ selectedLiths } ]` ;
78+ }
79+ if ( selectedUnits ) {
80+ params . units = `cs.[${ selectedUnits } ]` ;
81+ }
82+ if ( selectedStratNames ) {
83+ params . strat_names = `cs.[${ selectedStratNames } ]` ;
84+ }
85+
86+ setExtraParams ( params ) ;
87+ } , [ filteredInput , showEmpty , showInProcess , selectedLiths , selectedUnits , selectedStratNames ] ) ;
88+
89+
90+ // set filtered input
91+ useEffect ( ( ) => {
92+ const prevLength = prevInputLengthRef . current ;
93+
94+ if ( columnInput . length >= 3 ) {
95+ setFilteredInput ( columnInput ) ;
96+ } else if ( prevLength >= 3 && columnInput . length === 2 ) {
97+ setFilteredInput ( "" ) ;
98+ }
99+
100+ prevInputLengthRef . current = columnInput . length ;
101+ } , [ columnInput , showEmpty , showInProcess ] ) ;
102+
103+ const prevInputLengthRef = useRef ( columnInput . length ) ;
69104
70- const hideColumns = columnIDs ?. length === 0 && columnInput . length >= 3 ;
105+ useEffect ( ( ) => {
106+ if ( ! isEmpty ) {
107+ setLoading ( true ) ;
108+ getGroupedColumns ( project ?. project_id , extraParams )
109+ . then ( ( groups ) => setColumnGroups ( groups ) )
110+ . finally ( ( ) => setLoading ( false ) ) ;
111+ }
112+ } , [ project ?. project_id , extraParams ] ) ;
113+
114+
115+ const columnIDs = useMemo ( ( ) => {
116+ return filteredGroups ?. flatMap ( ( item ) =>
117+ item . columns . map ( ( col ) => col . col_id )
118+ ) ;
119+ } , [ filteredGroups ] ) ;
71120
72121 const handleInputChange = ( value , target ) => {
73122 setColumnInput ( value . toLowerCase ( ) ) ;
74123 } ;
75-
76- const allGroups = filteredGroups ?? columnGroups ?? [ ] ;
77-
124+
78125 return h ( "div.column-list-page" , [
79126 h ( ContentPage , [
80127 h ( "div.flex-row" , [
81128 h ( "div.main" , [
82129 h ( StickyHeader , [
83130 h ( PageBreadcrumbs , { showLogo : true } ) ,
84- h ( SearchBar , {
85- placeholder : "Search columns..." ,
86- onChange : handleInputChange ,
87- } ) ,
131+ h ( 'div.filters' , [
132+ h ( SearchBar , {
133+ placeholder : "Search columns..." ,
134+ onChange : handleInputChange ,
135+ className : "search-bar" ,
136+ } ) ,
137+ h ( 'div.switches' , [
138+ h ( Switch , {
139+ checked : showEmpty ,
140+ label : "Show empty" ,
141+ onChange : ( ) => setShowEmpty ( ! showEmpty ) ,
142+ } ) ,
143+ h ( Switch , {
144+ checked : showInProcess ,
145+ label : "Show in process" ,
146+ onChange : ( ) => setShowInProcess ( ! showInProcess ) ,
147+ } ) ,
148+ ] ) ,
149+ ] ) ,
150+ h ( LexFilters , {
151+ selectedLiths,
152+ setSelectedLiths,
153+ selectedUnits,
154+ setSelectedUnits,
155+ selectedStratNames,
156+ setSelectedStratNames,
157+ } )
88158 ] ) ,
89- h (
159+ h . if ( ! loading ) (
90160 "div.column-groups" ,
91- allGroups . map ( ( d ) =>
161+ filteredGroups ? .map ( ( d ) =>
92162 h ( ColumnGroup , {
93163 data : d ,
94164 key : d . id ,
95165 linkPrefix,
96- columnInput,
97- shouldFilter,
166+ showEmpty,
98167 } )
99168 )
100169 ) ,
170+ h . if ( columnGroups ?. length == 0 && ! loading ) ( "div.empty" , "No columns found" ) ,
171+ h . if ( loading ) ( "div.loading" , "Loading columns..." ) ,
101172 ] ) ,
102173 h ( "div.sidebar" , [
103174 h ( "div.sidebar-content" , [
@@ -111,9 +182,8 @@ function ColumnListPage({ title = "Columns", linkPrefix = "/" }) {
111182 ] ) ,
112183 h ( ColumnMapContainer , {
113184 columnIDs,
114- projectID : project ?. project_id ,
185+ projectID : project ?. project_id ,
115186 className : "column-map-container" ,
116- hideColumns,
117187 } ) ,
118188 ] ) ,
119189 ] ) ,
@@ -122,15 +192,9 @@ function ColumnListPage({ title = "Columns", linkPrefix = "/" }) {
122192 ] ) ;
123193}
124194
125- function ColumnGroup ( { data, linkPrefix, columnInput , shouldFilter } ) {
195+ function ColumnGroup ( { data, linkPrefix } ) {
126196 const [ isOpen , setIsOpen ] = useState ( false ) ;
127- const filteredColumns = shouldFilter
128- ? data . columns . filter ( ( col ) => {
129- const name = col . col_name . toLowerCase ( ) ;
130- const input = columnInput . toLowerCase ( ) ;
131- return name . includes ( input ) ;
132- } )
133- : data . columns ;
197+ const filteredColumns = data . columns
134198
135199 if ( filteredColumns ?. length === 0 ) return null ;
136200
@@ -167,16 +231,9 @@ function ColumnGroup({ data, linkPrefix, columnInput, shouldFilter }) {
167231
168232const ColumnItem = React . memo (
169233 function ColumnItem ( { data, linkPrefix = "/" } ) {
170- const { col_id, col_name } = data ;
171-
172- let nUnits = 0 ;
173- try {
174- nUnits = parseInt ( data . t_units ) ;
175- } catch ( e ) {
176- console . warn ( "Invalid number of units for column" , col_id , data . t_units ) ;
177- }
234+ const { col_id, name, units } = data ;
178235
179- const unitsText = nUnits > 0 ? `${ nUnits } units` : "empty" ;
236+ const unitsText = units ?. length > 0 ? `${ units ?. length } units` : "empty" ;
180237
181238 const href = linkPrefix + `columns/${ col_id } ` ;
182239 return h (
@@ -188,9 +245,9 @@ const ColumnItem = React.memo(
188245 } ,
189246 [
190247 h ( "td.col-id" , h ( "code.bp5-code" , col_id ) ) ,
191- h ( "td.col-name" , h ( "a" , { href } , col_name ) ) ,
248+ h ( "td.col-name" , h ( "a" , { href } , name ) ) ,
192249 h ( "td.col-status" , [
193- data . status === "in process" &&
250+ data . status_code === "in process" &&
194251 h (
195252 Tag ,
196253 { minimal : true , color : "lightgreen" , size : "small" } ,
@@ -202,7 +259,7 @@ const ColumnItem = React.memo(
202259 {
203260 minimal : true ,
204261 size : "small" ,
205- color : nUnits === 0 ? "orange" : "dodgerblue" ,
262+ color : units ?. length === 0 ? "orange" : "dodgerblue" ,
206263 } ,
207264 unitsText
208265 ) ,
@@ -220,3 +277,65 @@ const ColumnItem = React.memo(
220277 ) ;
221278 }
222279) ;
280+
281+ function LexFilters ( { selectedLiths, setSelectedLiths, selectedUnits, setSelectedUnits, selectedStratNames, setSelectedStratNames } ) {
282+ const liths = useLiths ( ) ;
283+ const units = useUnits ( ) ;
284+ const stratNames = useStratNames ( ) ;
285+
286+ if ( ! liths || ! units || ! stratNames ) return null
287+
288+ return h ( 'div.lex-filters' , [
289+ h ( FlexRow , {
290+ align : "center" ,
291+ gap : ".5em" ,
292+ } , [
293+ h ( 'p' , "Filtering columns by " ) ,
294+ h ( LexSelection , {
295+ value : selectedLiths ,
296+ onConfirm : ( value ) => setSelectedLiths ( value ) ,
297+ items : liths ,
298+ placeholder : "Select a lithology" ,
299+ } ) ,
300+ h . if ( selectedLiths ) ( Icon , { className : 'close-btn' , icon : "cross" , onClick : ( ) => setSelectedLiths ( null ) } )
301+ ] ) ,
302+ h ( FlexRow , {
303+ align : "center" ,
304+ gap : ".5em" ,
305+ } , [
306+ h ( 'p' , "Filtering columns by " ) ,
307+ h ( LexSelection , {
308+ value : selectedUnits ,
309+ onConfirm : ( value ) => setSelectedUnits ( value ) ,
310+ items : units ,
311+ placeholder : "Select a unit" ,
312+ } ) ,
313+ h . if ( selectedUnits ) ( Icon , { className : 'close-btn' , icon : "cross" , onClick : ( ) => setSelectedUnits ( null ) } )
314+ ] ) ,
315+ h ( FlexRow , {
316+ align : "center" ,
317+ gap : ".5em" ,
318+ } , [
319+ h ( 'p' , "Filtering columns by " ) ,
320+ h ( LexSelection , {
321+ value : selectedStratNames ,
322+ onConfirm : ( value ) => setSelectedStratNames ( value ) ,
323+ items : stratNames ,
324+ placeholder : "Select a strat name" ,
325+ } ) ,
326+ h . if ( selectedStratNames ) ( Icon , { className : 'close-btn' , icon : "cross" , onClick : ( ) => setSelectedStratNames ( null ) } )
327+ ] ) ,
328+ ] ) ;
329+ }
330+
331+ function useLiths ( ) {
332+ return useAPIResult ( postgrestPrefix + "/liths?select=id,name:lith,color:lith_color" ) ;
333+ }
334+
335+ function useUnits ( ) {
336+ return useAPIResult ( postgrestPrefix + "/units?select=id,name:strat_name" ) ;
337+ }
338+
339+ function useStratNames ( ) {
340+ return useAPIResult ( postgrestPrefix + "/strat_names?select=id,name:strat_name" ) ;
341+ }
0 commit comments