@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 +2 -2
- package/src/components/BackToTop/index.js +3 -2
- package/src/components/BottomSheet/Menu.js +7 -7
- package/src/components/CountUp/index.js +8 -1
- package/src/components/Datagrid/Accordion/FilterContent.js +24 -0
- package/src/components/Datagrid/Accordion/Filters.js +47 -40
- package/src/components/Datagrid/Accordion/index.js +4 -1
- package/src/components/Datagrid/Common/Common.js +106 -16
- package/src/components/Datagrid/Common/TableData.js +1 -0
- package/src/components/Datagrid/Dashboard/index.js +143 -0
- package/src/components/Datagrid/Footer/Footer.js +2 -2
- package/src/components/Datagrid/IndexComponent.js +5 -1
- package/src/components/Datagrid/Table/index.js +4 -1
- package/src/components/Datagrid/index.js +2 -1
- package/src/components/Filter/index.js +2 -1
- package/src/components/Menu/Item.js +2 -2
- package/src/components/Menu/utils.js +5 -1
- package/src/layouts/DatabaseStatistics/DatabaseStatistic.js +151 -38
- package/src/layouts/DatabaseStatistics/index.js +12 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fto-consult/expo-ui",
|
|
3
|
-
"version": "2.
|
|
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.
|
|
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
|
-
|
|
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,
|
|
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)
|
|
64
|
-
|
|
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 +=(
|
|
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
|
-
}
|
|
106
|
+
}
|
|
105
107
|
if(typeof children ==='function'){
|
|
106
|
-
const ct = children({
|
|
108
|
+
const ct = children({});
|
|
107
109
|
return React.isValidElement(ct)? ct : null;
|
|
108
110
|
}
|
|
109
|
-
const {
|
|
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)=><
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
-
|
|
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 =
|
|
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(
|
|
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 = {(
|
|
1337
|
-
|
|
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 <
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
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 <
|
|
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
|
|
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 =
|
|
129
|
+
testID = this.getTestID();
|
|
127
130
|
rest = defaultObj(rest);
|
|
128
131
|
let showDataSourceSelector = false;
|
|
129
132
|
if(dataSourceSelector === true){
|
|
@@ -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 ? {
|
|
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
|
-
|
|
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)
|
|
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 {
|
|
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
|
|
18
|
-
let {
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
67
|
+
if(!withDashboard){
|
|
68
|
+
APP.on(cActions.upsert(tableName),refresh);
|
|
69
|
+
APP.on(cActions.remove(tableName),refresh);
|
|
70
|
+
refresh();
|
|
71
|
+
}
|
|
46
72
|
return ()=>{
|
|
47
|
-
|
|
48
|
-
|
|
73
|
+
if(!withDashboard){
|
|
74
|
+
APP.off(cActions.upsert(tableName),refresh);
|
|
75
|
+
APP.off(cActions.remove(tableName),refresh);
|
|
76
|
+
}
|
|
49
77
|
}
|
|
50
78
|
},[]);
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
60
|
-
|
|
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 = {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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 = {
|
|
166
|
+
style = {counUpStyle}
|
|
70
167
|
/>}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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)
|