@fto-consult/expo-ui 2.29.3 → 2.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fto-consult/expo-ui",
3
- "version": "2.29.3",
3
+ "version": "2.30.0",
4
4
  "description": "Bibliothèque de composants UI Expo,react-native",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -61,7 +61,7 @@
61
61
  "@emotion/native": "^11.10.0",
62
62
  "@expo/html-elements": "^0.2.0",
63
63
  "@expo/vector-icons": "^13.0.0",
64
- "@fto-consult/common": "^1.25.9",
64
+ "@fto-consult/common": "^1.25.10",
65
65
  "@gorhom/portal": "^1.0.14",
66
66
  "@react-native-async-storage/async-storage": "~1.17.3",
67
67
  "@react-native-community/datetimepicker": "6.5.2",
@@ -2,6 +2,7 @@ import Fab from "$ecomponents/Fab";
2
2
  import {isObj,defaultStr,defaultVal,defaultObj} from "$utils";
3
3
  import React from "$react";
4
4
  import PropTypes from "prop-types";
5
+ import theme from "$theme";
5
6
 
6
7
  const SCREEN_INDENT = 20;
7
8
  export const isNativeScrollEvent = (nativeEvent)=>!isObj(nativeEvent) || !isObj(nativeEvent.layoutMeasurement) || !isObj(nativeEvent.contentOffset) ? false : true;
@@ -49,7 +50,7 @@ const BackToTopComponent = React.forwardRef((props,ref)=>{
49
50
  }
50
51
  },[visible])
51
52
  const style = defaultStr(position).toLowerCase() =='right' ? {
52
- right: 0
53
+ right: 0,
53
54
  } : {left : 0};
54
55
  React.setRef(ref,context);
55
56
  React.useEffect(()=>{
@@ -67,7 +68,7 @@ const BackToTopComponent = React.forwardRef((props,ref)=>{
67
68
  }
68
69
  }}
69
70
  icon = {defaultVal(icon,'arrow-up')}
70
- style = {[rest.style,style]}
71
+ style = {[theme.styles.ph1,rest.style,style]}
71
72
  />
72
73
  });
73
74
 
@@ -72,12 +72,7 @@ const BottomSheetMenuComponent = React.forwardRef((props,ref)=>{
72
72
  open();
73
73
  return false;
74
74
  }
75
- if(typeof children ==='function'){
76
- if(!visible) children = null;
77
- else {
78
- children = children({open,close});
79
- }
80
- }
75
+
81
76
  }
82
77
  React.useEffect(()=>{
83
78
  const closeModal = ()=>{
@@ -92,7 +87,12 @@ const BottomSheetMenuComponent = React.forwardRef((props,ref)=>{
92
87
  APP.off(APP.EVENTS.RESIZE_PAGE,closeModal);
93
88
  }
94
89
  },[]);
95
-
90
+ if(typeof children ==='function'){
91
+ if(!visible) children = null;
92
+ else {
93
+ children = children({open,close});
94
+ }
95
+ }
96
96
  return <>
97
97
  {isMob && React.isValidElement(anchor)? anchor : null}
98
98
  <Component
@@ -18,7 +18,14 @@ const CountUpComponent = ({from,to,format,type,duration,step,formatter,interval,
18
18
  const isNegative = end < 0 ? true : false;
19
19
  const intervalRef = React.useRef(null);
20
20
  const increment = (isNegative ? (-1):(1))* Math.abs(defaultNumber(step,getDefaultStep(to)));
21
- const [current, setCurrent] = React.useState(from);
21
+ const [current, _setCurrent] = React.useState(from);
22
+ const setCurrent = (current)=>{
23
+ if(typeof current !=='number') return;
24
+ if(current > end){
25
+ current = end;
26
+ }
27
+ _setCurrent(current);
28
+ }
22
29
  const isMounted = React.useIsMounted();
23
30
  const formatValue = typeof formatter =="function"? formatter : (number)=>{
24
31
  return isCurrency ? number.formatMoney() : number.formatNumber();
@@ -0,0 +1,24 @@
1
+ // Copyright 2022 @fto-consult/Boris Fouomene. All rights reserved.
2
+ // Use of this source code is governed by a BSD-style
3
+ // license that can be found in the LICENSE file.
4
+
5
+ import React from "$react";
6
+ import Filter from "$ecomponents/Filter";
7
+
8
+ export default function FilterAccordionComponent({onChange,...props}){
9
+ const valuesRefs = React.useRef({});
10
+ console.log("rendering ",valuesRefs);
11
+ return <Filter
12
+ {...props}
13
+ {...valuesRefs.current}
14
+ onChange = {(args)=>{
15
+ ["action","operator","defaultValue","ignoreCase","manualRun"].map((k)=>{
16
+ valuesRefs.current[k] = args[k];
17
+ })
18
+ console.log(valuesRefs," is changeddd");
19
+ if(onChange){
20
+ onChange(args);
21
+ }
22
+ }}
23
+ />
24
+ }
@@ -2,7 +2,7 @@ import {Content} from "$ecomponents/BottomSheet";
2
2
  import Icon from "$ecomponents/Icon";
3
3
  import React from "$react";
4
4
  import {defaultStr,defaultBool,defaultObj} from "$utils";
5
- import Filter,{canHandleFilter,getFilterStateValues} from "$ecomponents/Filter";
5
+ import Filter, {canHandleFilter,getFilterStateValues} from "$ecomponents/Filter";
6
6
  import PropTypes from "prop-types";
7
7
  import { StyleSheet,View } from "react-native";
8
8
  import Menu from "$ecomponents/Menu";
@@ -11,17 +11,20 @@ import theme from "$theme"
11
11
  import Expandable from "$ecomponents/Expandable";
12
12
  import { Dimensions } from "react-native";
13
13
  import Grid from "$components/Grid";
14
+ import { Pressable } from "react-native";
15
+ import Tooltip from "$ecomponents/Tooltip";
14
16
 
15
17
  const MIN_WIDTH = 250;
16
18
  let windowWidth = Dimensions.get("window").width;
17
19
 
18
20
  const FiltersAccordionComponent = React.forwardRef((props,ref)=>{
19
- const {filters,isLoading,filteredColumns,children,filterTitle:customFilterTitle,visible:customVisible,orOperator,andOperator,onToggleFilters,context:customContext,...restProps} = props;
21
+ const {filters,isLoading,filteredColumns,children,label,filterTitle:customFilterTitle,visible:customVisible,orOperator,andOperator,onToggleFilters,context:customContext,...restProps} = props;
20
22
  const context = defaultObj(customContext);
21
23
  const [state,setState] = React.useState({
22
24
  visible : defaultBool(customVisible,false),
23
25
  visibleColumns : defaultObj(filteredColumns),
24
26
  });
27
+ const valuesRefs = React.useRef({});
25
28
  windowWidth = Dimensions.get("window").width;
26
29
  const innerRef = React.useRef(null);
27
30
  const {visible,visibleColumns} = state;
@@ -35,10 +38,11 @@ const FiltersAccordionComponent = React.forwardRef((props,ref)=>{
35
38
  phoneSize : 12,
36
39
  style : [theme.styles.ph1],
37
40
  }
38
- const prepareContent = (filters)=>{
41
+ const prepareContent = (filters,renderMenusOnly)=>{
39
42
  const content = []
40
43
  const colMenus = [];
41
44
  let mainFilterTitle = defaultStr(filterTitle);
45
+ let counter = 0;
42
46
  const containerProps = {
43
47
  style : [styles.filter,{minWidth : Math.min(windowWidth,MIN_WIDTH)}]
44
48
  };
@@ -60,28 +64,26 @@ const FiltersAccordionComponent = React.forwardRef((props,ref)=>{
60
64
  setState({...state,visibleColumns:{...visibleColumns,[key]:!visible}})
61
65
  }}
62
66
  />)
63
- if(!visible) return;
64
- let defVal = filter.defaultValue;
65
- if(typeof defVal !== 'string' && typeof defVal !=='boolean'){
66
- if(Array.isArray(defVal)){
67
- defVal = "["+defVal.join(",")+"]";
68
- } else {
69
- if(defVal && defVal.toString){
70
- defVal = defVal.toString();
71
- } else defVal = "";
72
- }
67
+ if(!visible) {
68
+ return null;
73
69
  }
74
- mainFilterTitle +=(content.length?",":"")+"\n"+defaultStr(filter.label,filter.text,filter.field)+" : "+defVal+""
70
+ mainFilterTitle +=(counter?",":" :\n")+defaultStr(filter.label,filter.text,filter.field)//+" : "+defVal+""
71
+ counter++;
72
+ if(renderMenusOnly) return null;
75
73
  content.push(
76
74
  <Grid.Cell {...cellProps} key={key}>
77
75
  <Filter
78
76
  {...filter}
77
+ {...(isObj(valuesRefs.current[key]) ? valuesRefs.current[key] : {})}
79
78
  dynamicRendered
80
79
  isLoading = {isLoading && filteredRef.current[key] ? true : false}
81
80
  orOperator = {defaultBool(orOperator,filter.orOperator,true)}
82
81
  andOperator = {defaultBool(andOperator,filter.andOperator,true)}
83
82
  onChange = {(arg)=>{
83
+ if(!arg.action && !arg.operator || !arg.field) return;
84
+ //console.log("calling on change dddd",arg);
84
85
  const canHandle = canHandleFilter(arg);
86
+ valuesRefs.current[key] = arg;
85
87
  if(filteredRef.current[key] !== canHandle){
86
88
  if(canHandle){
87
89
  canHandlerFilterRef.current++;
@@ -101,12 +103,12 @@ const FiltersAccordionComponent = React.forwardRef((props,ref)=>{
101
103
  }
102
104
  })
103
105
  return {content,mainFilterTitle,colMenus}
104
- }/*,[filters])*/
106
+ }
105
107
  if(typeof children ==='function'){
106
- const ct = children({content,menus:colMenus});
108
+ const ct = children({});
107
109
  return React.isValidElement(ct)? ct : null;
108
110
  }
109
- const {content,mainFilterTitle,colMenus} = prepareContent(filters);
111
+ const {mainFilterTitle} = prepareContent(filters,true);
110
112
  const hasFilters = canHandlerFilterRef.current > 0 ? true : false;
111
113
  return <Content
112
114
  animateOnClose
@@ -116,35 +118,40 @@ const FiltersAccordionComponent = React.forwardRef((props,ref)=>{
116
118
  onDissmiss = {()=>{
117
119
  setState({...state,visible:false})
118
120
  }}
119
- anchor = {(props)=><Icon
120
- name={hasFilters?"filter-menu":"filter-plus"}
121
- color = {hasFilters?theme.colors.primaryOnSurface:undefined}
122
- {...props}
123
- title = {mainFilterTitle}
124
- />}
121
+ anchor = {(props)=><Tooltip title = {mainFilterTitle}Component={Pressable} {...props} style={[theme.styles.row]}>
122
+ <Icon
123
+ name={hasFilters?"filter-menu":"filter-plus"}
124
+ {...props}
125
+ color = {hasFilters?theme.colors.primaryOnSurface:undefined}
126
+ />
127
+ {React.isValidElement(label,true)?<Label style={[hasFilters && {color:theme.colors.primaryOnSurface}]} fontSize={16} textBold>{label}</Label>:null}
128
+ </Tooltip>}
125
129
  ref = {(el)=>{
126
130
  innerRef.current = el;
127
131
  React.setRef(ref,el);
128
132
  }}
129
133
  >
130
- <View style={[styles.wrapper]}>
131
- <View style = {[styles.menuWrapper]}>
132
- <Expandable
133
- left = {(props)=><Icon {...props} icon={content.length?'filter-plus':'filter-menu'}/>}
134
- style = {styles.expandable}
135
- title = {<Label>{filterTitle}</Label>}
136
- >
137
- <View style={[styles.menuContent]}>
138
- {colMenus}
139
- </View>
140
- </Expandable>
134
+ {({open,close})=>{
135
+ const {content,colMenus} = prepareContent(filters);
136
+ return <View style={[styles.wrapper]}>
137
+ <View style = {[styles.menuWrapper]}>
138
+ <Expandable
139
+ left = {(props)=><Icon {...props} icon={content.length?'filter-plus':'filter-menu'}/>}
140
+ style = {styles.expandable}
141
+ title = {<Label>{filterTitle}</Label>}
142
+ >
143
+ <View style={[styles.menuContent]}>
144
+ {colMenus}
145
+ </View>
146
+ </Expandable>
147
+ </View>
148
+ {content.length ? <View style={[theme.styles.w100]}>
149
+ <Grid style={theme.styles.w100}>
150
+ {content}
151
+ </Grid>
152
+ </View> : null}
141
153
  </View>
142
- {content.length ? <View style={[theme.styles.w100]}>
143
- <Grid style={theme.styles.w100}>
144
- {content}
145
- </Grid>
146
- </View> : null}
147
- </View>
154
+ }}
148
155
  </Content>
149
156
  })
150
157
 
@@ -303,6 +303,9 @@ const DatagridFactory = (Factory)=>{
303
303
  }
304
304
  return super.renderEmpty();
305
305
  }
306
+ getTestID(){
307
+ return defaultStr(this.props.testID,"RN_DatagridAccordion");
308
+ }
306
309
  render (){
307
310
  let {
308
311
  filters:customFilters,
@@ -333,7 +336,7 @@ const DatagridFactory = (Factory)=>{
333
336
  chartContainerProps = defaultObj(chartContainerProps);
334
337
  const canRenderChart = this.canRenderChart();
335
338
  const hasData = this.getStateDataSize(false) ? true : false;
336
- testID = defaultStr(testID,"RN_DatagridAccordion");
339
+ testID = this.getTestID();
337
340
  backToTopProps = defaultObj(backToTopProps);
338
341
  accordionProps = defaultObj(accordionProps);
339
342
  this.renderingItemsProps = {};
@@ -138,6 +138,14 @@ export const displayTypes = {
138
138
  },*/
139
139
  }
140
140
 
141
+ export const chartTypes = {};
142
+
143
+ Object.map(displayTypes,(c,k)=>{
144
+ if(c && c.isChart){
145
+ chartTypes[k] = c;
146
+ }
147
+ })
148
+
141
149
  export const arrayValueSeparator = ", ";
142
150
 
143
151
  const dataSourceArgs = {};
@@ -233,6 +241,7 @@ export default class CommonDatagridComponent extends AppComponent {
233
241
  aggregatorFunctions : {value : extendAggreagatorFunctions(this.props.aggregatorFunctions)},
234
242
  ///les types d'affichage
235
243
  displayTypes : {value : hasFoundDisplayTypes ? disTypes : Object.clone(displayTypes)},
244
+ dateFields : {value : {}},
236
245
  sectionListColumnsSize : {value : {current:0}}, //la taille du nombre d'éléments de section dans les colonnes
237
246
  })
238
247
  const sessionAggregator = defaultStr(this.getSessionData("aggregatorFunction")).trim();
@@ -262,16 +271,16 @@ export default class CommonDatagridComponent extends AppComponent {
262
271
  this.filters = {}
263
272
  Object.map(this.getFiltersProps(),(f,v)=>{
264
273
  if(isPlainObject(f)){
265
- this.filters[v] = f;
274
+ this.filters[v] = {...f};
266
275
  this.filters[v].field = defaultStr(f.field,v);
267
- this.filters[v].originValue = defaultVal(this.filters[v].originValue,f.defaultValue,f.value)
276
+ this.filters[v].originValue = defaultVal(f.originValue,f.defaultValue,f.value)
268
277
  } else {
269
278
  this.filters[v] = {
270
279
  originValue : f,
271
280
  field : v
272
281
  }
273
282
  }
274
- })
283
+ });
275
284
  this.state.filteredColumns = defaultObj(this.getSessionData("filteredColumns"+this.getSessionNameKey()),this.props.filters);
276
285
  this.filtersSelectors = {selector:this.getFilters()};
277
286
  const {sectionListColumns} = this.prepareColumns();
@@ -280,6 +289,9 @@ export default class CommonDatagridComponent extends AppComponent {
280
289
  this.state.columnsWidths = this.preparedColumns.widths;
281
290
  }
282
291
  this.state.chartConfig = extendObj({},this.getSessionData("chartConfig"),this.props.chartConfig);
292
+ if(!("sparkline" in this.state.chartConfig) && this.isDashboard()){
293
+ this.state.chartConfig.sparkline = true;
294
+ }
283
295
  const dType = defaultStr(this.props.displayType,this.getSessionData("displayType"),"table");
284
296
  this.state.displayType = this.displayTypes[dType] ? this.displayTypes[dType].code : "table" in this.displayTypes ? "table" : Object.keys(this.displayTypes)[0]?.code;
285
297
  this.state.displayOnlySectionListHeaders = defaultBool(this.getSessionData("displayOnlySectionListHeaders"),this.props.displayOnlySectionListHeaders,false)
@@ -330,6 +342,7 @@ export default class CommonDatagridComponent extends AppComponent {
330
342
  } else {
331
343
  this.currentDataSources = Object.toArray(this.currentDataSources);
332
344
  }
345
+ this.persistDisplayType(this.state.displayType);
333
346
  }
334
347
 
335
348
  /*** si une ligne peut être selectionable */
@@ -648,6 +661,14 @@ export default class CommonDatagridComponent extends AppComponent {
648
661
  isIndexColumn(columnDef,columnField){
649
662
  return isObj(columnDef) && defaultStr(columnDef.field,columnField) === this.getIndexColumnName();
650
663
  }
664
+ isDateField(column){
665
+ const field = isObj(column)? defaultStr(column.field,column.name) : defaultStr(column);
666
+ return field && field in this.dateFields ? true : false;
667
+ }
668
+ isFooterField(column){
669
+ const field = isObj(column)? defaultStr(column.field,column.name) : defaultStr(column);
670
+ return field && isObj(this.state.footers) && field in this.state.footers ? true : false;
671
+ }
651
672
  initColumnsCallback(){}
652
673
  initColumns (columns){
653
674
  this.state.columns = {};
@@ -689,6 +710,9 @@ export default class CommonDatagridComponent extends AppComponent {
689
710
  let header = {...headerCol};
690
711
  header.field = defaultStr(header.field, headerIndex)
691
712
  header.type = defaultStr(header.jsType,header.type,"text").toLowerCase();
713
+ if(header.type.contains("date")){
714
+ this.dateFields[header.field] = header;
715
+ }
692
716
  /**** pour ignorer une colonne du datagrid, il suffit de passer le paramètre datagrid à false */
693
717
  if(!isNonNullString(header.field) || header.datagrid === false) {
694
718
  return;
@@ -714,6 +738,9 @@ export default class CommonDatagridComponent extends AppComponent {
714
738
  hasFootersFields(){
715
739
  return Object.size(this.getFootersFields(),true) ? true : false;
716
740
  }
741
+ getFooters(){
742
+ return this.getFootersFields();
743
+ }
717
744
  getActionsArgs(selected){
718
745
  const r = isObj(selected)? selected : {};
719
746
  const ret = {
@@ -1333,9 +1360,12 @@ export default class CommonDatagridComponent extends AppComponent {
1333
1360
  if(!m.length) return null;
1334
1361
  return <Menu
1335
1362
  items = {m}
1336
- anchor = {(p)=><Icon name="material-functions" {...p}
1337
- title = "Fonctions d'aggrégations. Veuillez sélectionner la fonction à utiliser par défaut pour la totalisation des données des colonnes de type nombre"
1338
- />}
1363
+ anchor = {(props)=>{
1364
+ return <Pressable {...props} style={[theme.styles.row]}>
1365
+ <Icon {...props} name="material-functions" title = "Fonctions d'aggrégations. Veuillez sélectionner la fonction à utiliser par défaut pour la totalisation des données des colonnes de type nombre"></Icon>
1366
+ {this.isDashboard() && <Label splitText numberOfLines={1} textBold>Fonction d'aggrégation</Label>||null}
1367
+ </Pressable>
1368
+ }}
1339
1369
  />
1340
1370
  }
1341
1371
  configureChart(refreshChart){
@@ -1404,6 +1434,14 @@ export default class CommonDatagridComponent extends AppComponent {
1404
1434
  multiple : true,
1405
1435
  },
1406
1436
  stacked : stackSettings,
1437
+ sparkline : {
1438
+ type : 'switch',
1439
+ text : "Sparkline",
1440
+ checkedTooltip : "Le graphe a été définit pour être affiché dans un environnement petite surface",
1441
+ checkedValue : true,
1442
+ uncheckedValue : false,
1443
+ settingKey : "chart",
1444
+ },
1407
1445
  title : {
1408
1446
  text : "Titre du graphe",
1409
1447
  format :'hashtag',
@@ -1443,6 +1481,9 @@ export default class CommonDatagridComponent extends AppComponent {
1443
1481
  displayOnlySectionListHeaders : this.canDisplayOnlySectionListHeaders(),
1444
1482
  };
1445
1483
  }
1484
+ isDashboard(){
1485
+ return false;
1486
+ }
1446
1487
  renderDisplayTypes(){
1447
1488
  const m = [];
1448
1489
  let activeType = null,hasFoundChart = false,hasFoundTable = false;
@@ -1501,11 +1542,14 @@ export default class CommonDatagridComponent extends AppComponent {
1501
1542
  title = "Type d'affichage"
1502
1543
  items = {m}
1503
1544
  anchor = {(p)=>{
1504
- return <Icon
1505
- {...p}
1506
- name = {activeType.icon}
1507
- title = {"Les données s'affichent actuellement en {0}. Cliquez pour modifier le type d'affichage".sprintf(activeType.label)}
1508
- />
1545
+ return <Pressable {...p} style={[theme.styles.row]}>
1546
+ <Icon
1547
+ {...p}
1548
+ name = {activeType.icon}
1549
+ title = {"Les données s'affichent actuellement en {0}. Cliquez pour modifier le type d'affichage".sprintf(activeType.label)}
1550
+ />
1551
+ {this.isDashboard() && <Label textBold>Type d'affichage</Label>||null}
1552
+ </Pressable>
1509
1553
  }}
1510
1554
  />
1511
1555
  }
@@ -1720,7 +1764,6 @@ export default class CommonDatagridComponent extends AppComponent {
1720
1764
  chartProps[settingKey] = defaultObj(chartProps[settingKey]);
1721
1765
  chartProps[settingKey][key] = config[key];
1722
1766
  });
1723
-
1724
1767
  const chartOptions = {
1725
1768
  ...chartProps,
1726
1769
  title :extendObj(true,{}, {
@@ -1738,7 +1781,7 @@ export default class CommonDatagridComponent extends AppComponent {
1738
1781
  },
1739
1782
  },chartProps.title),
1740
1783
  series,
1741
- chart : extendObj(true,{},{height :350},chartProps.chart,{
1784
+ chart : extendObj(true,{},{height :this.isDashboard()?80:350},chartProps.chart,{
1742
1785
  type : chartType.type,
1743
1786
  })
1744
1787
  }
@@ -1765,9 +1808,26 @@ export default class CommonDatagridComponent extends AppComponent {
1765
1808
  if(!chartType.isDonut){
1766
1809
  delete chartOptions.labels;
1767
1810
  }
1811
+ if(this.isDashboard() && typeof chartOptions.chart.sparkline !=='boolean'){
1812
+ chartOptions.chart.sparkline = true;
1813
+ }
1814
+ if(chartOptions.chart.sparkline){
1815
+ chartOptions.chart.sparkline = {enabled: true}
1816
+ } else delete chartOptions.chart.sparkline;
1817
+ if(chartOptions.chart.sparkline && chartOptions.chart.sparkline.enabled){
1818
+ chartOptions.xaxis = defaultObj(chartOptions.xaxis);
1819
+ chartOptions.xaxis.labels = defaultObj(chartOptions.xaxis.labels);
1820
+ chartOptions.xaxis.labels.show = false;
1821
+
1822
+ chartOptions.yaxis = defaultObj(chartOptions.yaxis);
1823
+ chartOptions.yaxis.labels = defaultObj(chartOptions.yaxis.labels);
1824
+ chartOptions.yaxis.labels.show = false;
1825
+ chartOptions.legend = defaultObj(chartOptions.legend);
1826
+ chartOptions.legend.show = false;
1827
+ }
1768
1828
  return <Chart
1769
1829
  options = {chartOptions}
1770
- key = {chartOptions.chart.id}
1830
+ key = {chartOptions.chart.id+"-"+this.state.displayType}
1771
1831
  />
1772
1832
  }
1773
1833
  canShowFooters(){
@@ -1819,7 +1879,10 @@ export default class CommonDatagridComponent extends AppComponent {
1819
1879
  title = {"Grouper les données du tableau"}
1820
1880
  testID = {"RN_DatagridSectionListMenu"}
1821
1881
  anchor = {(props)=>{
1822
- return <Icon {...props} name='format-list-group' title={"Grouper les éléments du tableau"}></Icon>
1882
+ return <Pressable {...props} style={[theme.styles.row]}>
1883
+ <Icon {...props} color={hasList?theme.colors.primaryOnSurface:undefined} name='format-list-group' title={"Grouper les éléments du tableau"}></Icon>
1884
+ {this.isDashboard() && <Label style={[hasList && {color:theme.colors.primaryOnSurface}]} textBold>Grouper par</Label>||null}
1885
+ </Pressable>
1823
1886
  }}
1824
1887
  items = {[
1825
1888
  {
@@ -1829,7 +1892,7 @@ export default class CommonDatagridComponent extends AppComponent {
1829
1892
  divider : true,
1830
1893
  style : theme.styles.bold,
1831
1894
  },
1832
- this.canDisplayOnlySectionListHeaders() && {
1895
+ this.canDisplayOnlySectionListHeaders() && this.state.displayType =='table' && {
1833
1896
  text : "Afficher uniquement totaux",
1834
1897
  icon : this.state.displayOnlySectionListHeaders?"check":null,
1835
1898
  onPress : this.toggleDisplayOnlySectionListHeaders.bind(this)
@@ -2190,6 +2253,10 @@ export default class CommonDatagridComponent extends AppComponent {
2190
2253
  this.sectionListData[k] = sectionListData[k];
2191
2254
  return k;
2192
2255
  });
2256
+ } else {
2257
+ Object.map(sectionListData,(v,k)=>{
2258
+ this.sectionListData[k] = v;
2259
+ })
2193
2260
  }
2194
2261
  data = newData;
2195
2262
  }
@@ -2829,6 +2896,23 @@ export default class CommonDatagridComponent extends AppComponent {
2829
2896
  /**** permet d'afficher le menu item lié aux champs triables */
2830
2897
  getSortableMenuMenuItem(){
2831
2898
 
2899
+ }
2900
+ getTestID(){
2901
+ return defaultStr(this.props.testID,'RN_DatagridComponent');
2902
+ }
2903
+ renderTitle (){
2904
+ const testID = this.getTestID();
2905
+ const title = typeof this.props.title =="function"? this.props.title({context:this}) : this.props.title;
2906
+ const titleProps = defaultObj(this.props.titleProps);
2907
+ return React.isValidElement(title) ? <Label testID={testID+"_Title"} {...titleProps} style={[theme.styles.w100,titleProps.style]}>
2908
+ {title}
2909
+ </Label> : null
2910
+ }
2911
+ getInitialData (){
2912
+ return this.INITIAL_STATE.data;
2913
+ }
2914
+ getData(){
2915
+ return this.state.data;
2832
2916
  }
2833
2917
  renderSelectableCheckboxCell(props){
2834
2918
  const {containerProps} = props;
@@ -3071,6 +3155,12 @@ const chartDisplayType = PropTypes.oneOf(Object.keys(displayTypes).filter(type=>
3071
3155
  return typeof x =='object' && x && typeof x.disabled !== true && x.isChart === true && true || false;
3072
3156
  }));
3073
3157
  CommonDatagridComponent.propTypes = {
3158
+ title : PropTypes.oneOfType([
3159
+ PropTypes.func,
3160
+ PropTypes.string,
3161
+ PropTypes.node,
3162
+ PropTypes.element,
3163
+ ]),
3074
3164
  canMakePhoneCall : PropTypes.bool,//si l'on peut faire un appel sur la données sélectionnées
3075
3165
  makePhoneCallProps : PropTypes.oneOfType([
3076
3166
  PropTypes.object,
@@ -96,6 +96,7 @@ export default class CommonTableDatagrid extends CommonDatagrid{
96
96
  fetchOptions.selector = defaultObj(fetchOptions.selector);
97
97
  fetchOptions.dataSources = this.currentDataSources;
98
98
  fetchOptions = extendObj(true,true,{},fetchOptions,{selector : fetchFilters});
99
+ console.log(fetchOptions," is fetchOptions ",this.getFiltersProps(),fetchFilters)
99
100
  fetchOptions.dataSources = this.currentDataSources;
100
101
  fetchOptions.sort = this.getSort();
101
102
  let limit = this.getQueryLimit();
@@ -0,0 +1,143 @@
1
+ import {TableData} from "../Common";
2
+ import {defaultObj,defaultArray,defaultStr,defaultDecimal,isNonNullString} from "$utils";
3
+ import View from "$ecomponents/View";
4
+ import { StyleSheet,Dimensions,Pressable } from "react-native";
5
+ import Icon,{MENU_ICON} from "$ecomponents/Icon";
6
+ import React from "$react";
7
+ import {Menu as BottomSheetMenu} from "$ecomponents/BottomSheet";
8
+ import { chartTypes } from "../Common/Common";
9
+ import theme from "$theme";
10
+ import FiltersAccordionComponent from "../Accordion/Filters";
11
+
12
+ export default class DatagridDashboard extends TableData {
13
+ constructor(props){
14
+ super(props);
15
+ Object.map(this.displayTypes,(t,i)=>{
16
+ if(!t || !t.isChart) delete this.displayTypes[i];
17
+ });
18
+ if(!this.state.displayType.toLowerCase().contains("chart")){
19
+ this.state.displayType = chartTypes[Object.keys(chartTypes)[0]].code
20
+ }
21
+ this.persistDisplayType(this.state.displayType);
22
+ }
23
+ isDatagrid(){
24
+ return true;
25
+ }
26
+ canPaginateData(){
27
+ return false;
28
+ }
29
+ bindResizeEvents(){
30
+ return false;
31
+ }
32
+ isDashboard(){
33
+ return true;
34
+ }
35
+ renderMenu(){
36
+ const testID = this.getTestID();
37
+ const {filterOrOperator,filterAndOperator,} = this.props;
38
+ const {
39
+ visibleColumnsNames,
40
+ filteredColumns,
41
+ filters :headerFilters,
42
+ } = this.preparedColumns;
43
+ const menus = [
44
+ {
45
+ text : 'Rafraichir',
46
+ icon : "refresh",
47
+ onPress : this.refresh.bind(this)
48
+ },
49
+ this.isFilterable() ?<FiltersAccordionComponent
50
+ testID={testID+"_HeaderFilters"}
51
+ isLoading = {this.isLoading()}
52
+ filters = {headerFilters}
53
+ visibleColumns = {visibleColumnsNames}
54
+ filteredColumns = {filteredColumns}
55
+ orOperator = {filterOrOperator}
56
+ andOperator = {filterAndOperator}
57
+ context = {this}
58
+ label = {"Filtres"}
59
+ /> : null,
60
+ ...this.renderCustomMenu(),
61
+ this.renderDisplayTypes(),
62
+ this.renderSectionListMenu(),
63
+ this.renderAggregatorFunctionsMenu(),
64
+ ]
65
+ return menus;
66
+ }
67
+ getTestID(){
68
+ return defaultStr(this.props.testID,"RN_DatagridDashboard");
69
+ }
70
+ canHandleQueryLimit(){
71
+ return false;
72
+ }
73
+ render(){
74
+ let {
75
+ title,
76
+ testID,actions,
77
+ selectableMultiple,
78
+ sortable,
79
+ titleProps,
80
+ autoSort,
81
+ exportable,
82
+ selectable,pagin,showPagination,
83
+ sessionName,onMount,onUnmount,onFetchData,dataSourceSelector,dataSourceSelectorProps,queryLimit,
84
+ filters,
85
+ filterOrOperator,
86
+ filterAndOperator,
87
+ chartContainerProps,
88
+ accordion, //pour le rendu du header en accordion
89
+ ...rest
90
+ } = this.props;
91
+ const canRenderChart = this.canRenderChart();
92
+ if(!canRenderChart) return null;
93
+ chartContainerProps = defaultObj(chartContainerProps);
94
+ titleProps = Object.assign({},titleProps);
95
+ testID = this.getTestID();
96
+ rest = defaultObj(rest);
97
+ const _progressBar = this.getProgressBar();
98
+ const pointerEvents = this.getPointerEvents();
99
+ const maxHeight = 300;
100
+ return <View {...rest} testID={testID} style={[styles.container,{maxHeight},rest.style]} pointerEvents={pointerEvents}>
101
+ {this.renderTitle()}
102
+ {showPagination ? <View style={[styles.paginationContainer]}>
103
+ <BottomSheetMenu
104
+ anchor = {(props)=>{
105
+ return <Icon {...props} title={isMobile?"Actions":"Colonnes"} name={isMobile?MENU_ICON:'view-column'}></Icon>
106
+ }}
107
+ closeOnPress = {isMobile?undefined:false}
108
+ items = {this.renderMenu()}
109
+ />
110
+ </View> : null}
111
+ {<View testID={testID+"_ChartContainer"} {...chartContainerProps} style={[theme.styles.w100,chartContainerProps.style]}>
112
+ {_progressBar}
113
+ {this.renderChart()}
114
+ </View>}
115
+ </View>
116
+ }
117
+ }
118
+
119
+ DatagridDashboard.displayName = "DatagridDashboardComponent";
120
+
121
+ DatagridDashboard.propTypes = {
122
+ ...defaultObj(TableData.propTypes),
123
+ }
124
+
125
+
126
+ const styles = StyleSheet.create({
127
+ paginationContainer : {
128
+ flexDirection : 'row',
129
+ width : '100%'
130
+ },
131
+ paginationContent : {
132
+ flex : 1,
133
+ flexDirection : 'row',
134
+ width : '100%',
135
+ justifyContent : 'flex-end',
136
+ alignItems : 'center',
137
+ paddingHorizontal : 10,
138
+ },
139
+ container : {
140
+ width : '100%',
141
+ flex:1,
142
+ },
143
+ })
@@ -83,13 +83,13 @@ const formatValue = ({value,format,aggregatorFunction})=>{
83
83
  return (format === 'money' && aggregatorFunction != 'count')? value.formatMoney():value.formatNumber();
84
84
  }
85
85
  export default function DGGridFooterValue (props){
86
- let {label,text,displayLabel,style,aggregatorFunctions,aggregatorFunction,format,testID,anchorProps} = props;
86
+ let {label,text,displayLabel,withLabel,style,aggregatorFunctions,aggregatorFunction,format,testID,anchorProps} = props;
87
87
  aggregatorFunctions = defaultObj(aggregatorFunctions);
88
88
  anchorProps = defaultObj(anchorProps);
89
89
  testID = defaultStr(testID,"RN_DatagridFooterComponent");
90
90
  label = defaultVal(label,text);
91
91
  const defLabel = label;
92
- if(displayLabel !== false){
92
+ if(displayLabel !== false && withLabel !== false){
93
93
  if(!label || !React.isValidElement(label,true)) return null;
94
94
  } else label = undefined;
95
95
  const [active,setActive] = React.useState(isNonNullString(aggregatorFunction) && aggregatorFunction in aggregatorFunctions ? aggregatorFunction : aggregatorFunctions[Object.keys(aggregatorFunctions)[0]]?.code)
@@ -3,6 +3,7 @@
3
3
  // license that can be found in the LICENSE file.
4
4
 
5
5
  import Accordion,{ TableData as TableDataAccordion} from "./Accordion";
6
+ import Dashboard from "./Dashboard";
6
7
  import Table,{TableData as DatagridTableData} from "./Table";
7
8
  import {isDesktopMedia,isMobileMedia} from "$cplatform/dimensions";
8
9
  import {isFunction,defaultVal} from "$utils";
@@ -19,7 +20,10 @@ const DatagridMainComponent = React.forwardRef((props,ref)=>{
19
20
  let Component = TableComponent;
20
21
  const canRenderAccordion = (isFunction(props.accordion) || (isObj(props.accordionProps) && isFunction(props.accordionProps.accordion)) || props.accordion === true);
21
22
  let renderType = defaultStr(getRenderType(),isDesk? "table":'accordion').trim().toLowerCase()
22
- if(renderType == 'accordion' && canRenderAccordion){
23
+ if(false && (renderType ==="dashboard" || props.dashobard === true)){
24
+ Component = Dashboard;
25
+ delete props.dashobard;
26
+ } else if(renderType == 'accordion' && canRenderAccordion){
23
27
  Component = AccordionComponent;
24
28
  } else if(renderType =='table'){
25
29
  Component = TableComponent;
@@ -108,6 +108,9 @@ const DatagridFactory = (Factory)=>{
108
108
  this.listRef.current.scrollToIndex({index});
109
109
  }
110
110
  }
111
+ getTestID(){
112
+ return defaultStr(this.props.testID,"RN_DatagridTable");
113
+ }
111
114
  render(){
112
115
  let {title,testID,actions,
113
116
  selectableMultiple,
@@ -123,7 +126,7 @@ const DatagridFactory = (Factory)=>{
123
126
  } = this.props;
124
127
  const canRenderChart = this.canRenderChart();
125
128
  chartContainerProps = defaultObj(chartContainerProps);
126
- testID = defaultStr(testID,'RN_DatagridTableComponent');
129
+ testID = this.getTestID();
127
130
  rest = defaultObj(rest);
128
131
  let showDataSourceSelector = false;
129
132
  if(dataSourceSelector === true){
@@ -6,4 +6,5 @@ export * from "./Common";
6
6
 
7
7
  export {default} from "./IndexComponent";
8
8
 
9
- export {default as SWRDatagrid} from "./SWRDatagrid";
9
+ export {default as SWRDatagrid} from "./SWRDatagrid";
10
+ export {default as Dashboard} from "./Dashboard";
@@ -600,7 +600,6 @@ export default class Filter extends AppComponent {
600
600
  </>
601
601
  const containerProps = defaultObj(this.props.containerProps,rest.containerProps);
602
602
  delete rest.containerProps;
603
- rest.onValidate = this.onFilterValidate.bind(this);
604
603
  const Component = isBetweenAction ? FilterBetweenComponent : this.Component;
605
604
  const responsiveProps = Object.assign({},responsiveProps);
606
605
  responsiveProps.style = [theme.styles.w100,responsiveProps.style]
@@ -615,6 +614,8 @@ export default class Filter extends AppComponent {
615
614
  isFilter
616
615
  name = {this.name}
617
616
  testID = {testID}
617
+ onValidate = {this.onFilterValidate.bind(this)}
618
+ onChange = {x=>null}
618
619
  ref = {React.mergeRefs(this.searchFilter,ref)}
619
620
  />
620
621
  </View>
@@ -70,7 +70,7 @@ const MenuItemComponent = React.forwardRef(({
70
70
  const pointerEvents = disabled ? 'none' : 'auto';
71
71
  const disabledStyle = disabled ? {opacity:DISABLED_OPACITY} : null;
72
72
  const winW = Dimensions.get("window").width-30;
73
- const maxWidthStyle = isBottomSheetItem ? {width:winW,maxWidth:null} : undefined;
73
+ const maxWidthStyle = isBottomSheetItem ? {maxWidth:winW,paddingRight:10} : undefined;
74
74
  const maxWidthTextStyle = isBottomSheetItem ? {width:winW-50} : null;
75
75
  right = typeof right =='function'? right ({color:titleColor}) : right;
76
76
  if(!React.isValidElement(right)) right = null;
@@ -113,7 +113,7 @@ const MenuItemComponent = React.forwardRef(({
113
113
  testID={testID+"_Label"}
114
114
  selectable={false}
115
115
  numberOfLines={1}
116
- ellipsizeMode = {"tail"}
116
+ splitText
117
117
  {...labelProps}
118
118
  style={[styles.title, { color: titleColor }, labelStyle,styles.noMargin,!right ? maxWidthTextStyle : null]}
119
119
  >
@@ -16,7 +16,11 @@ export const renderItems = (props)=>{
16
16
  testID = defaultStr(testID,"RN_MenuComponents.Items")
17
17
  filter = defaultFunc(filter,x=> true);
18
18
  Object.map(items,(item,index,_index)=>{
19
- if(!isObj(item) || React.isValidElement(item) || !filter({items,item,_index,index})) return null;
19
+ if(!isObj(item)|| !filter({items,item,_index,index})) return null;
20
+ if(React.isValidElement(item)) {
21
+ _items.push(<React.Fragment key={index+"_"+_index}>{item}</React.Fragment>);
22
+ return
23
+ }
20
24
  if(item.divider === true && !item.title && !item.text && !item.label && !item.icon){
21
25
  _items.push(<Divider testID={testID+"_Divider"+index} {...item} key={index}/>);
22
26
  return
@@ -1,8 +1,9 @@
1
- import {defaultStr,isNonNullString} from "$utils";
1
+ import {defaultStr,defaultVal,isNonNullString,defaultNumber,defaultObj} from "$utils";
2
2
  import React from "$react";
3
3
  import CountUp from "$ecomponents/CountUp";
4
4
  import Avatar from "$ecomponents/Avatar";
5
- import { ActivityIndicator } from "react-native-paper";
5
+ import { Pressable } from "react-native";
6
+ import {ActivityIndicator} from "react-native-paper";
6
7
  import Item from "$ecomponents/Expandable/Item";
7
8
  import {navigateToTableDataList} from "$enavigation/utils";
8
9
  import theme from "$theme";
@@ -11,66 +12,174 @@ import PropTypes from "prop-types";
11
12
  import APP from "$capp/instance"
12
13
  import cActions from "$cactions";
13
14
  import {View} from "react-native";
15
+ import {Menu} from "$ecomponents/BottomSheet";
16
+ import Dashboard from "$ecomponents/Datagrid/Dashboard";
17
+ import fetch from "$capi/fetch";
18
+ import Auth from "$auth";
19
+ import Footer from "$ecomponents/Datagrid/Footer/Footer";
14
20
 
15
- export default function DatabaseStatisticContainer (props){
21
+ export default function DatabaseStatisticContainer ({dashboardProps,fetchDataProps,table,fetchCount,index,testID,title,icon,onPress:customOnPress,columns,fetchData,withDashboard,...props}){
22
+ dashboardProps = defaultObj(dashboardProps);
16
23
  const [count,setCount] = React.useState(0);
17
- const [isLoading,setIsLoading] = React.useState(true);
18
- let {table,fetchCount,index,testID,title,icon,onPress} = props;
24
+ const datagridRef = React.useRef(null);
25
+ let {} = props;
19
26
  title = defaultStr(title)
20
27
  table = defaultObj(table);
28
+ const dbStatistics = defaultObj(table.databaseStatistics,table.databaseStatisticsProps);
29
+ const databaseStatisticsFields = defaultObj(table.databaseStatisticsFields);
30
+ const hasDFields = Object.size(databaseStatisticsFields,true);
31
+ if(!withDashboard && hasDFields){
32
+ withDashboard = true;
33
+ }
34
+ const dFields = hasDFields ? databaseStatisticsFields : defaultObj(dbStatistics.fields,dbStatistics.columns);
35
+ if(typeof fetchData !=='function'){
36
+ fetchData = typeof dbStatistics.fetchData =='function'? dbStatistics.fetchData : undefined;
37
+ }
38
+ withDashboard = withDashboard && typeof fetchData == 'function'? true : false;
39
+ columns = Object.size(columns,true)? columns : Object.size(dFields,true)? dFields : table.fields;
21
40
  const tableName = defaultStr(table.tableName,table.table).toUpperCase();
22
41
  fetchCount = typeof table.fetchCount =='function'? table.fetchCount : typeof fetchCount =='function'? fetchCount : undefined;
23
- if(!fetchCount || !tableName) return null;
42
+ if((!fetchCount && !withDashboard) || !tableName) return null;
24
43
  const refreshingRef = React.useRef(null);
25
44
  const isMounted = React.useIsMounted();
45
+
46
+ const [isLoading,setIsLoading] = React.useState(withDashboard?false:true);
26
47
  const refresh = ()=>{
27
48
  if(refreshingRef.current || !isMounted()) return;
49
+ if(!fetchCount){
50
+ return;
51
+ }
28
52
  refreshingRef.current = true;
29
53
  setIsLoading(true);
30
54
  setTimeout(()=>{
31
- fetchCount().then((count)=>{
32
- setCount(count);
33
- setIsLoading(false);
34
- refreshingRef.current = false;
35
- }).catch((e)=>{
36
- setIsLoading(false);
37
- refreshingRef.current = false;
38
- });
39
- },100);
55
+ fetchCount({table,tableName}).then((count)=>{
56
+ setCount(count);
57
+ setIsLoading(false);
58
+ refreshingRef.current = false;
59
+ }).catch((e)=>{
60
+ setIsLoading(false);
61
+ refreshingRef.current = false;
62
+ });
63
+ },100);
40
64
  }
41
65
 
42
66
  React.useEffect(()=>{
43
- APP.on(cActions.upsert(tableName),refresh);
44
- APP.on(cActions.remove(tableName),refresh);
45
- refresh();
67
+ if(!withDashboard){
68
+ APP.on(cActions.upsert(tableName),refresh);
69
+ APP.on(cActions.remove(tableName),refresh);
70
+ refresh();
71
+ }
46
72
  return ()=>{
47
- APP.off(cActions.upsert(tableName),refresh);
48
- APP.off(cActions.remove(tableName),refresh);
73
+ if(!withDashboard){
74
+ APP.off(cActions.upsert(tableName),refresh);
75
+ APP.off(cActions.remove(tableName),refresh);
76
+ }
49
77
  }
50
78
  },[]);
51
- return <Item
52
- testID = {defaultStr(testID,"RN_DatabaseStatistic_"+table)}
53
- onPress = {(args)=>{
54
- if(onPress && onPress(args) === false) return;
55
- navigateToTableDataList(tableName,{
56
- tableName
57
- })
79
+ const progressBar = <View style={[theme.styles.justifyContentFlexStart,theme.styles.alignItemsFlexStart]}>
80
+ <ActivityIndicator color={theme.colors.primary}/>
81
+ </View>;
82
+ const isDatagridLoading = datagridRef.current && datagridRef.current.isLoading && datagridRef.current.isLoading();
83
+ testID = defaultStr(testID,"RN_DatabaseStatistic_"+table);
84
+ const onPress = (args)=>{
85
+ if(customOnPress && customOnPress(args) === false) return;
86
+ navigateToTableDataList(tableName,{
87
+ tableName
88
+ })
89
+ };
90
+ const fetchFields = React.useCallback(()=>{
91
+ const fetchFields = [];
92
+ Object.map(columns,(field,f)=>{
93
+ const ff = defaultStr(isObj(field)? field.field: undefined,f);
94
+ if(ff){
95
+ fetchFields.push(ff);
96
+ }
97
+ });
98
+ return fetchFields;
99
+ },[columns])();
100
+ const counUpStyle = {fontSize:20,fontWeight:'bold',color:theme.colors.secondaryOnSurface};
101
+ title = React.isValidElement(title,true)?<Label splitText numberOfLines={1} color={theme.colors.primaryOnSurface} style={[{fontSize:15}]}>{title}</Label>: null;
102
+ return withDashboard ? <Dashboard
103
+ chartProps = {{
104
+ stroke: {
105
+ curve: 'straight'
106
+ },
107
+ fill: {
108
+ type: 'solid',
109
+ opacity: 1,
110
+ },
58
111
  }}
59
- left = {(aProps)=>{
60
- return <Avatar suffix={index} {...aProps} icon= {icon} size={40} label={title}/>
112
+ sessionName = {tableName+"-database-statistics"}
113
+ {...props}
114
+ {...dbStatistics}
115
+ style = {[theme.styles.ph1,props.style]}
116
+ columns = {columns}
117
+ ref = {datagridRef}
118
+ progressBar = {isLoading?<View/>:<View style={[theme.styles.w100,theme.styles.alignItemsCenter,theme.styles.justifyContentCenter]}>{progressBar}</View>}
119
+ tableName = {tableName}
120
+ table = {table}
121
+ fetchData = {(options)=>{
122
+ return fetchData({...defaultObj(fetchDataProps),fields:fetchFields,table,tableName,fetch,Auth,...options});
61
123
  }}
62
- title = {<Label splitText numberOfLines={1} color={theme.colors.primaryOnSurface} style={[{fontSize:15}]}>{title}</Label>}
63
- titleProps = {{primary : true}}
64
- description = {isLoading?<View style={[theme.styles.justifyContentFlexStart,theme.styles.alignItemsFlexStart]}>
65
- <ActivityIndicator color={theme.colors.primary}/>
66
- </View>:<CountUp
124
+ title = {({context})=>{
125
+ if(!context || !context.state) return null;
126
+ const {state} = context;
127
+ const footers = context.getFooters && context.getFooters() || {};
128
+ const dataSize = context.getStateDataSize && context.getStateDataSize() || 0;
129
+ const footersValues = context.getFooterValues && context.getFooterValues() || {};
130
+ const y = defaultStr(state.chartConfig?.y);
131
+ const footerValue = y && isObj(footersValues) && footersValues[y] || null;
132
+ const format = defaultStr(isObj(footerValue) && isNonNullString(footerValue.format) && footerValue.format || undefined).toLowerCase();
133
+ const aggregatorFunction = isNonNullString(state.aggregatorFunction)? state.aggregatorFunction : undefined;
134
+ let value = isObj(footerValue) && aggregatorFunction && aggregatorFunction in footerValue ? footerValue[aggregatorFunction] : undefined;
135
+ if(typeof value !=='number'){
136
+ value = isObj(footerValue) && typeof footerValue.sum =='number'? footerValue.sum : 0;
137
+ }
138
+ return <Pressable onPress={onPress} testID={testID+"_TitleContainer"} style={[theme.styles.w100]}>
139
+ <View testID={testID+"_TitleCountUp"} style={[theme.styles.w100]}>
140
+ <View style={[theme.styles.w100,theme.styles.row,theme.styles.alignItemsCenter]}>
141
+ {title}
142
+ {typeof dataSize =='number' && dataSize && <Label>
143
+ {dataSize.formatNumber()}
144
+ </Label> || null}
145
+ </View>
146
+ <Menu
147
+ testID={testID+"_Menu"}
148
+ items = {context.renderMenu()}
149
+ anchor = {(p)=><Pressable {...p} testID={testID+"_MenuAnchor"}>
150
+ <Label
151
+ textCenter
152
+ style = {counUpStyle}
153
+ >{format =='money'?value.formatMoney():value.formatNumber()}</Label>
154
+ </Pressable>}
155
+ />
156
+ </View>
157
+ </Pressable>
158
+ }}
159
+ /> : <Item
160
+ onPress = {onPress}
161
+ title = {title}
162
+ //style = {[theme.styles.pv1]}
163
+ description = {isLoading || isDatagridLoading?progressBar:<CountUp
67
164
  from={0}
68
165
  to={count}
69
- style = {{fontSize:20,color:theme.colors.secondaryOnSurface}}
166
+ style = {counUpStyle}
70
167
  />}
71
- >
72
-
73
- </Item>
168
+ titleProps ={{primary : true}}
169
+ left = {(aProps)=>{
170
+ return <Menu
171
+ testID={testID+"_Menu"}
172
+ items = {[
173
+ {
174
+ icon : "refresh",
175
+ onPress : refresh,
176
+ text : "Actualiser"
177
+ }
178
+ ]}
179
+ anchor = {(p)=><Avatar suffix={index} {...aProps} {...p} icon= {icon} size={40} label={title}/>}
180
+ />
181
+ }}
182
+ />;
74
183
  }
75
184
 
76
185
  /*** DBSTAT, prend en paramètre le nom de la bd ainsi que celui de la table et affiche en statistic,:
@@ -78,6 +187,10 @@ export default function DatabaseStatisticContainer (props){
78
187
  */
79
188
  DatabaseStatisticContainer.propTypes = {
80
189
  ...Item.propTypes,
190
+ /*** les props supplémentaires à passer à la fonction fetchData */
191
+ fetchDataProps : PropTypes.oneOfType([
192
+ PropTypes.object,
193
+ ]),
81
194
  /*** La méthode fetchCount doit retourner une promèsse qui lorsqu'elle est résolue, résoue le nombre d'éléments de la table de données en bd */
82
195
  fetchCount : PropTypes.func,//la fonction permettant de counter les éléments de la table data
83
196
  table : PropTypes.shape({
@@ -7,7 +7,7 @@ import theme from "$theme";
7
7
  import PropTypes from "prop-types";
8
8
 
9
9
  export const title = 'Statistiques en BD';
10
- export default function DatabaseStatisticScreen ({withScreen,tableFilter,fetchCount,title:customTitle,contentProps,containerProps,tables,Component,...props}){
10
+ export default function DatabaseStatisticScreen ({withScreen,fetchDataProps,tableFilter,getTable,fetchCount,fetchData,title:customTitle,contentProps,containerProps,tables,Component,...props}){
11
11
  Component = React.isComponent(Component)? Component : Grid;
12
12
  containerProps = defaultObj(containerProps);
13
13
  const title = containerProps.title = defaultStr(containerProps.title,DatabaseStatisticScreen.title);
@@ -20,6 +20,10 @@ export default function DatabaseStatisticScreen ({withScreen,tableFilter,fetchCo
20
20
  let content = [];
21
21
  tableFilter = typeof tableFilter ==="function"? tableFilter : x=>true;
22
22
  Object.map(tables,(table,index,suffix)=>{
23
+ const t = typeof getTable =='function'?getTable(defaultStr(table?.tableName,table?.table,index)) : null;
24
+ if(isObj(t) && defaultStr(t.table,t.tableName)){
25
+ table = t;
26
+ }
23
27
  if(isObj(table) && table.databaseStatistic !== false && table.databaseStatistics !== false && tableFilter(table) !== false){
24
28
  content.push(
25
29
  <Cell elevation = {5} withSurface mobileSize={12} desktopSize={3} tabletSize={4} {...contentProps} key = {index} >
@@ -27,6 +31,8 @@ export default function DatabaseStatisticScreen ({withScreen,tableFilter,fetchCo
27
31
  icon = {table.icon}
28
32
  key = {index}
29
33
  table = {table}
34
+ fetchData = {fetchData}
35
+ fetchDataProps = {fetchDataProps}
30
36
  index = {suffix}
31
37
  title = {defaultStr(table.text,table.label,table.title)}
32
38
  fetchCount = {table.fetchCount|| typeof fetchCount =='function'? (a,b)=>{
@@ -51,6 +57,11 @@ export const screenName = DatabaseStatisticScreen.screenName = "DatabaseStatisti
51
57
  DatabaseStatisticScreen.title = title;
52
58
 
53
59
  DatabaseStatisticScreen.propTypes = {
60
+ /*** les props supplémentaires à passer à la fonction fetchData */
61
+ fetchDataProps : PropTypes.oneOfType([
62
+ PropTypes.object,
63
+ ]),
64
+ getTable : PropTypes.func,//la fonction permettant de récupérer la table à partir du nom
54
65
  tables : PropTypes.oneOfType([
55
66
  PropTypes.arrayOf(PropTypes.object),
56
67
  PropTypes.objectOf(PropTypes.object)