@fto-consult/expo-ui 2.11.4 → 2.12.1
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/babel.config.alias.js +4 -0
- package/package.json +2 -2
- package/src/components/Countries/SelectCountry.js +2 -1
- package/src/components/Datagrid/Common/Common.js +2 -2
- package/src/components/Datagrid/utils.js +4 -1
- package/src/components/Dropdown/index.js +5 -2
- package/src/components/Form/Fields/$extendFormFields.js +5 -0
- package/src/components/Form/Fields/Field.js +18 -2
- package/src/components/Form/Fields/SelectTableData/Component.js +234 -0
- package/src/components/Form/Fields/{SelectTableData.js → SelectTableData/index.js} +8 -2
- package/src/components/Form/Fields/index.js +18 -1
- package/src/components/Form/FormData/FormData.js +0 -1
- package/src/components/Form/FormData/FormDataActions.js +10 -11
- package/src/components/Form/FormData/componentsTypes.js +4 -10
- package/src/components/PhoneInput/PhoneNumber.js +8 -7
- package/src/layouts/Screen/TableData.js +56 -7
- package/src/layouts/Screen/utils.js +2 -0
package/babel.config.alias.js
CHANGED
|
@@ -38,6 +38,10 @@ module.exports = (opts)=>{
|
|
|
38
38
|
r["$expo"] = r["$expo-ui"] = expo;
|
|
39
39
|
r["$epreloader"] = path.resolve(expo,"components/Preloader");
|
|
40
40
|
r["$eform"] = path.resolve(expo,"components","Form");
|
|
41
|
+
|
|
42
|
+
//l'alias $extendFormFields, pour étendre les formFields qu'on veut définir
|
|
43
|
+
r["$extendFormFields"] = r["$extendFormFields"] || path.resolve(r["$eform"],"Fields","$extendFormFields")
|
|
44
|
+
|
|
41
45
|
r["$form"] = r["$form"] || r["$eform"];
|
|
42
46
|
r["$eform-data"] = r["$eformData"]= path.resolve(expo,"components","Form","FormData");
|
|
43
47
|
r["$formData"] = r["$formData"] || r["$eformData"];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fto-consult/expo-ui",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.12.1",
|
|
4
4
|
"description": "Bibliothèque de composants UI Expo,react-native",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"@expo/metro-config": "^0.4.0",
|
|
66
66
|
"@expo/vector-icons": "^13.0.0",
|
|
67
67
|
"@expo/webpack-config": "^0.17.2",
|
|
68
|
-
"@fto-consult/common": "^1.20.
|
|
68
|
+
"@fto-consult/common": "^1.20.2",
|
|
69
69
|
"@gorhom/portal": "^1.0.14",
|
|
70
70
|
"@react-native-async-storage/async-storage": "~1.17.3",
|
|
71
71
|
"@react-native-community/datetimepicker": "6.5.2",
|
|
@@ -20,7 +20,8 @@ export const getCountryFieldProps = (props)=>{
|
|
|
20
20
|
items : countries,
|
|
21
21
|
upper : true,
|
|
22
22
|
dialogProps : {title:'Sélectionner un pays'},
|
|
23
|
-
getItemValue : ({item})=>item.code,
|
|
23
|
+
getItemValue : ({item})=>item.code.toUpperCase(),
|
|
24
|
+
renderText : ({item})=>"[{0}] {1}".sprintf(item?.code?.toUpperCase(),item?.label),
|
|
24
25
|
compare : (a,b)=>{
|
|
25
26
|
return typeof a ==='string' && typeof b =='string' && a.toLowerCase() === b.toLowerCase() ? true : false;
|
|
26
27
|
},
|
|
@@ -19,7 +19,7 @@ import React from "$react";
|
|
|
19
19
|
import DateLib from "$lib/date";
|
|
20
20
|
import Filter,{canHandleFilter,prepareFilters} from "$ecomponents/Filter";
|
|
21
21
|
import {CHECKED_ICON_NAME} from "$ecomponents/Checkbox";
|
|
22
|
-
import { COLUMN_WIDTH,DATE_COLUMN_WIDTH } from "../utils";
|
|
22
|
+
import { COLUMN_WIDTH,DATE_COLUMN_WIDTH,willConvertFiltersToSQL } from "../utils";
|
|
23
23
|
import { StyleSheet,Dimensions,useWindowDimensions} from "react-native";
|
|
24
24
|
import Preloader from "$ecomponents/Preloader";
|
|
25
25
|
import Checkbox from "../Checkbox";
|
|
@@ -1262,7 +1262,7 @@ export default class CommonDatagridComponent extends AppComponent {
|
|
|
1262
1262
|
}
|
|
1263
1263
|
///si les filtres devront être convertis au format SQL
|
|
1264
1264
|
willConvertFiltersToSQL(){
|
|
1265
|
-
return !!defaultVal(this.props.convertFiltersToSQL,
|
|
1265
|
+
return !!defaultVal(this.props.convertFiltersToSQL,willConvertFiltersToSQL());;
|
|
1266
1266
|
}
|
|
1267
1267
|
getFilters(){
|
|
1268
1268
|
this.filters = extendObj(true,{},this.filteredValues,this.filters)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import theme,{LINE_HEIGHT} from "$theme";
|
|
2
2
|
import { StyleSheet } from "react-native";
|
|
3
3
|
import {get as getSession} from "./Common/session";
|
|
4
|
+
import appConfig from "$capp/config";
|
|
4
5
|
|
|
5
6
|
export const getRenderType = ()=>getSession("render-type");
|
|
6
7
|
|
|
@@ -182,4 +183,6 @@ export const getRowStyle = ({row,bordered,numColumns,rowData,isAccordion,isTable
|
|
|
182
183
|
style.push(styles.noHorizontalBorder);
|
|
183
184
|
}
|
|
184
185
|
return style;
|
|
185
|
-
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export const willConvertFiltersToSQL = x=>!!appConfig.get("convertDatagridFiltersToSQL");
|
|
@@ -709,6 +709,7 @@ class DropdownComponent extends AppComponent {
|
|
|
709
709
|
bindResizeEvents,
|
|
710
710
|
left,
|
|
711
711
|
right,
|
|
712
|
+
dialogProps,
|
|
712
713
|
...dropdownProps
|
|
713
714
|
} = this.props;
|
|
714
715
|
|
|
@@ -935,7 +936,7 @@ class DropdownComponent extends AppComponent {
|
|
|
935
936
|
r = React.isValidElement(t)? r = <>{t}{r}</> : r;
|
|
936
937
|
}
|
|
937
938
|
if(showAdd){
|
|
938
|
-
return <>{r}<Icon {..._addIconProps} {...props}/></>
|
|
939
|
+
return <>{r}<Icon {..._addIconProps} {...props} style={[theme.styles.noMargin,theme.styles.noPadding,_addIconProps.style,props.style]}/></>
|
|
939
940
|
}
|
|
940
941
|
return r;
|
|
941
942
|
}}
|
|
@@ -981,6 +982,7 @@ class DropdownComponent extends AppComponent {
|
|
|
981
982
|
const isBigList = this.isBigList;
|
|
982
983
|
const ListComponent = isBigList ? BigList : List;
|
|
983
984
|
const autoFocus = canAutoFocusSearchField({visible,items:renderingItems});
|
|
985
|
+
dialogProps = defaultObj(dialogProps);
|
|
984
986
|
return (
|
|
985
987
|
<Fragment>
|
|
986
988
|
{!withBottomSheet && isMob && anchor}
|
|
@@ -993,7 +995,8 @@ class DropdownComponent extends AppComponent {
|
|
|
993
995
|
onDismiss={this.hide.bind(this)}
|
|
994
996
|
contentStyle = {[{paddingVertical:0},restProps.contentStyle]}
|
|
995
997
|
anchor={anchor}
|
|
996
|
-
|
|
998
|
+
{...dialogProps}
|
|
999
|
+
title = {defaultStr(dialogProps.title,label,text)+"[ "+self.state.data.length.formatNumber()+" ]"}
|
|
997
1000
|
subtitle = {selectedText}
|
|
998
1001
|
style = {[restProps.style]}
|
|
999
1002
|
contentProps = {{style:{flex:1}}}
|
|
@@ -677,9 +677,20 @@ export default class Field extends AppComponent {
|
|
|
677
677
|
return true;
|
|
678
678
|
}
|
|
679
679
|
onBlurField(event){
|
|
680
|
-
if(isFunction(this.props.onBlur)){
|
|
681
|
-
|
|
680
|
+
if(isFunction(this.props.onBlur) && this.props.onBlur({event,context:this}) === false){
|
|
681
|
+
return;
|
|
682
682
|
}
|
|
683
|
+
if(this.isFilter()) return
|
|
684
|
+
const value = this.getValue();
|
|
685
|
+
if(isNonNullString(this.props.fieldToPopulateOnBlur) && isNonNullString(value)){
|
|
686
|
+
const context = this.getField(this.props.fieldToPopulateOnBlur.trim());
|
|
687
|
+
if(context && context.getValue){
|
|
688
|
+
const cVal = defaultStr(context.getValue());
|
|
689
|
+
if(cVal.length < value.length){
|
|
690
|
+
context.setValue(value);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
}
|
|
683
694
|
}
|
|
684
695
|
onFocusField(event){
|
|
685
696
|
if(isFunction(this.props.onFocus)){
|
|
@@ -772,6 +783,7 @@ export default class Field extends AppComponent {
|
|
|
772
783
|
width,
|
|
773
784
|
height,
|
|
774
785
|
jsType,
|
|
786
|
+
fieldToPopulateOnBlur,
|
|
775
787
|
...rest
|
|
776
788
|
} = this.props;
|
|
777
789
|
if(this.state.caughtAnError){
|
|
@@ -1043,6 +1055,10 @@ Field.propTypes = {
|
|
|
1043
1055
|
* Lorsqu'elle est définie alors le rendu lors du composant doit être de type filter
|
|
1044
1056
|
*/
|
|
1045
1057
|
renderfilter : PropTypes.string,
|
|
1058
|
+
/**** il s'agit d'un champ du même formulaire que la formField actuel, qui sera populated avec la valeur par défaut
|
|
1059
|
+
* de la formField cournat loreque la valeur du champ en question a une longueur très inférieure à celle de la valeur de la form courante.
|
|
1060
|
+
*/
|
|
1061
|
+
fieldToPopulateOnBlur : PropTypes.string,
|
|
1046
1062
|
/*** cette fonction doit retourner l'instance de la field
|
|
1047
1063
|
elle doit toujours être définie dans la classe qui hérite directement au composant Field
|
|
1048
1064
|
*/
|
|
@@ -0,0 +1,234 @@
|
|
|
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 Dropdown from "$ecomponents/Dropdown";
|
|
6
|
+
import {defaultStr,isFunction,defaultVal,isObjOrArray,defaultObj} from "$utils";
|
|
7
|
+
import PropTypes from "prop-types";
|
|
8
|
+
import actions from "$cactions";
|
|
9
|
+
import {navigateToTableData} from "$enavigation/utils";
|
|
10
|
+
import {getFetchOptions,prepareFilters} from "$cutils/filters";
|
|
11
|
+
import fetch from "$capi"
|
|
12
|
+
import {willConvertFiltersToSQL} from "$ecomponents/Datagrid/utils";
|
|
13
|
+
import React from "$react";
|
|
14
|
+
|
|
15
|
+
/*** la tabledataSelectField permet de faire des requêtes distantes pour rechercher les données
|
|
16
|
+
* Elle doit prendre en paramètre et de manière requis : les props suivante :
|
|
17
|
+
* foreignKeyColumn : La colonne dont le champ fait référence à la clé étrangère, ie fKeyTable dans laquelle faire les requêtes fetch
|
|
18
|
+
* foreignKeyTable : la tableData dans laquelle effectuer les donées de la requêtes
|
|
19
|
+
* foreignKeyLabel : Le libélé dans la table étrangère
|
|
20
|
+
*/
|
|
21
|
+
const TableDataSelectField = React.forwardRef((_props,ref)=>{
|
|
22
|
+
let {foreignKeyColumn,foreignKeyTable,foreignKeyLabel,dropdownActions,fields,fetchItems,convertFiltersToSQL,mutateFetchedItems,getForeignKeyTable,onFetchItems,isFilter,isUpdate,isDocEditing,items,onAddProps,fetchDataOpts,...props} = _props;
|
|
23
|
+
props = defaultObj(props);
|
|
24
|
+
props.data = defaultObj(props.data);
|
|
25
|
+
foreignKeyColumn = foreignKeyColumn.trim();
|
|
26
|
+
foreignKeyLabel = defaultStr(foreignKeyLabel).trim();
|
|
27
|
+
convertFiltersToSQL = defaultVal(convertFiltersToSQL,willConvertFiltersToSQL());
|
|
28
|
+
const fKeyTable = getForeignKeyTable(foreignKeyTable,props)
|
|
29
|
+
if(!isObj(fKeyTable) || !(defaultStr(fKeyTable.tableName,fKeyTable.table))){
|
|
30
|
+
console.error("type de données invalide pour la fKeyTable ",fKeyTable," composant SelectTableData",_props);
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
foreignKeyTable = defaultStr(fKeyTable.tableName,fKeyTable.table,foreignKeyTable).trim().toUpperCase();
|
|
34
|
+
const isMounted = React.useIsMounted();
|
|
35
|
+
const showAdd = isFilter ? false : React.useRef(Auth.isTableDataAllowed({foreignKeyTable,action:'create'}) ? defaultVal(props.showAdd,props.showAddBtn,true) : false).current;
|
|
36
|
+
const [state,setState] = React.useState({
|
|
37
|
+
items : [],isLoading : true,
|
|
38
|
+
});
|
|
39
|
+
fetchDataOpts = Object.clone(defaultObj(fetchDataOpts));
|
|
40
|
+
fetchItems = typeof fetchItems =='function' ? fetchItems : typeof fKeyTable.queryPath =='string' ? (opts)=>{
|
|
41
|
+
return fetch(fKeyTable.queryPath,opts);
|
|
42
|
+
} : undefined;
|
|
43
|
+
isUpdate = defaultBool(isUpdate,typeof isDocEditing ==='function' && isDocEditing({data:props.data,fKeyTable,foreignKeyTable}));
|
|
44
|
+
if(isFilter){
|
|
45
|
+
isUpdate = false;
|
|
46
|
+
}
|
|
47
|
+
const defaultFields = [foreignKeyColumn];
|
|
48
|
+
if(foreignKeyLabel){
|
|
49
|
+
defaultFields.push(foreignKeyLabel);
|
|
50
|
+
}
|
|
51
|
+
if(fetchDataOpts.fields !== 'all' && (!Array.isArray(fetchDataOpts.fields) || !fetchDataOpts.fields.length)){
|
|
52
|
+
fetchDataOpts.fields = defaultFields;
|
|
53
|
+
}
|
|
54
|
+
const foreignKeyColumnValue = props.defaultValue;
|
|
55
|
+
let isDisabled = defaultBool(props.disabled,props.readOnly,false);
|
|
56
|
+
if(!isDisabled && props.editable === false){
|
|
57
|
+
isDisabled = true;
|
|
58
|
+
}
|
|
59
|
+
if(isUpdate && isNonNullString(foreignKeyColumnValue) && (isDisabled)){
|
|
60
|
+
fetchDataOpts.selector = defaultObj(fetchDataOpts.selector);
|
|
61
|
+
fetchDataOpts.selector.$and = defaultArray(fetchDataOpts.selector.$and);
|
|
62
|
+
let hasF = false;
|
|
63
|
+
for(let i in fetchDataOpts.selector.$and){
|
|
64
|
+
const cFilter = fetchDataOpts.selector.$and[i];
|
|
65
|
+
if(isObj(cFilter)) {
|
|
66
|
+
if(cFilter[foreignKeyColumn] === foreignKeyColumnValue){
|
|
67
|
+
hasF = true;
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if(!hasF){
|
|
73
|
+
fetchDataOpts.selector.$and.push({[foreignKeyColumn] : foreignKeyColumnValue})
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
React.useEffect(()=>{
|
|
77
|
+
const onUpsertData = ()=>{return isMounted()?context.refresh():undefined};
|
|
78
|
+
APP.on(actions.upsert(foreignKeyTable),onUpsertData);
|
|
79
|
+
APP.on(actions.onRemove(foreignKeyTable),onUpsertData);
|
|
80
|
+
context.refresh();
|
|
81
|
+
return ()=>{
|
|
82
|
+
APP.off(actions.upsert(foreignKeyTable),onUpsertData);
|
|
83
|
+
APP.off(actions.onRemove(foreignKeyTable),onUpsertData);
|
|
84
|
+
};
|
|
85
|
+
},[]);
|
|
86
|
+
|
|
87
|
+
let dat = isNonNullString(foreignKeyColumnValue)? {
|
|
88
|
+
[foreignKeyColumn]:foreignKeyColumnValue,
|
|
89
|
+
...(foreignKeyLabel ? {[foreignKeyLabel]:foreignKeyColumnValue+", introuvable dans le système"}:{})
|
|
90
|
+
} : null;
|
|
91
|
+
|
|
92
|
+
const context = {
|
|
93
|
+
refresh : (force,cb)=>{
|
|
94
|
+
if(!isMounted()) return;
|
|
95
|
+
if(typeof beforeFetchItems ==='function' && beforeFetchItems(fetchDataOpts) === false) return;
|
|
96
|
+
let opts = Object.clone(fetchDataOpts);
|
|
97
|
+
opts.selector = prepareFilters(fetchDataOpts.selector,{convertToSQL:convertFiltersToSQL});
|
|
98
|
+
opts = getFetchOptions(opts);
|
|
99
|
+
const r = fetchItems && fetchItems(opts);
|
|
100
|
+
if(r === false) return;
|
|
101
|
+
if(isPromise(r)){
|
|
102
|
+
r.then((args)=>{
|
|
103
|
+
if(Array.isArray(args)){
|
|
104
|
+
args = {data : args};
|
|
105
|
+
}
|
|
106
|
+
let items = args.items = args.data = Array.isArray(args.items) ? args.items : Array.isArray(args.data) ? args.data : [];
|
|
107
|
+
if(dat && isUpdate){
|
|
108
|
+
if(isFunction(mutateFetchedItems)){
|
|
109
|
+
items = mutateFetchedItems(items);
|
|
110
|
+
}
|
|
111
|
+
let hasFound = false;
|
|
112
|
+
if(!isObjOrArray(items)) items = [];
|
|
113
|
+
for(let i in items){
|
|
114
|
+
if(isObj(items[i]) && items[i][foreignKeyColumn] == foreignKeyColumnValue){
|
|
115
|
+
hasFound = true;
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if(!hasFound){
|
|
120
|
+
if(Array.isArray(items)){
|
|
121
|
+
items.push(dat);
|
|
122
|
+
} else {
|
|
123
|
+
items[foreignKeyColumnValue] = dat;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
setState({...state,items,isLoading:false})
|
|
128
|
+
if(onFetchItems){
|
|
129
|
+
onFetchItems({data:items,items,context,props});
|
|
130
|
+
}
|
|
131
|
+
}).catch((e)=>{
|
|
132
|
+
console.log(e," fetching list of data select table data ",foreignKeyColumn,foreignKeyTable)
|
|
133
|
+
})
|
|
134
|
+
} else {
|
|
135
|
+
setState({...state, isLoading : false})
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const prevIsUpdate = React.usePrevious(isUpdate);
|
|
140
|
+
const prevDefaultValue = React.usePrevious(foreignKeyColumnValue);
|
|
141
|
+
if(!isFilter){
|
|
142
|
+
React.useEffect(()=>{
|
|
143
|
+
if(prevIsUpdate === isUpdate && JSON.stringify(prevDefaultValue) === JSON.stringify(foreignKeyColumnValue)) return;
|
|
144
|
+
context.refresh();
|
|
145
|
+
},[isUpdate,foreignKeyColumnValue])
|
|
146
|
+
}
|
|
147
|
+
dropdownActions = isObj(dropdownActions)? {...dropdownActions} : isArray(dropdownActions)? [...dropdownActions] : []
|
|
148
|
+
const isDropdonwsActionsArray = isArray(dropdownActions);
|
|
149
|
+
const refreshItem = {
|
|
150
|
+
text : 'Rafraichir',
|
|
151
|
+
onPress : context.refresh,
|
|
152
|
+
icon : 'refresh',
|
|
153
|
+
}
|
|
154
|
+
if(isDropdonwsActionsArray){
|
|
155
|
+
dropdownActions.push(refreshItem)
|
|
156
|
+
} else {
|
|
157
|
+
dropdownActions.trefreshItem = refreshItem;
|
|
158
|
+
}
|
|
159
|
+
const rItem = (p)=>{
|
|
160
|
+
if(!isObj(p) || !isObj(p.item)) return null;
|
|
161
|
+
const itemLabel = defaultStr(foreignKeyLabel && p.item[foreignKeyLabel]), itemCode = defaultStr(p.item[foreignKeyColumn]);
|
|
162
|
+
return (itemLabel !== itemCode ? ((isNonNullString(itemCode)?("["+itemCode+"] "):"")+itemLabel):itemLabel);
|
|
163
|
+
}
|
|
164
|
+
const dialogProps = defaultObj(props.dialogProps);
|
|
165
|
+
dialogProps.title = defaultStr(dialogProps.title,fKeyTable.text,fKeyTable.label)
|
|
166
|
+
return <Dropdown
|
|
167
|
+
{...props}
|
|
168
|
+
isFilter = {isFilter}
|
|
169
|
+
showAdd = {!isFilter && showAdd}
|
|
170
|
+
{...React.setProps(Dropdown,fKeyTable,{})}
|
|
171
|
+
{...state}
|
|
172
|
+
dialogProps = {dialogProps}
|
|
173
|
+
ref = {ref}
|
|
174
|
+
defaultValue = {foreignKeyColumnValue}
|
|
175
|
+
dropdownActions = {dropdownActions}
|
|
176
|
+
context = {context}
|
|
177
|
+
itemValue = {(p) => {
|
|
178
|
+
if(typeof props.itemValue ==='function'){
|
|
179
|
+
return props.itemValue(p);
|
|
180
|
+
}
|
|
181
|
+
return p.item[foreignKeyColumn];
|
|
182
|
+
}}
|
|
183
|
+
renderItem = {(p) => {
|
|
184
|
+
if(typeof props.renderItem ==='function'){
|
|
185
|
+
return props.renderItem(p);
|
|
186
|
+
}
|
|
187
|
+
if(typeof props.renderText =='function'){
|
|
188
|
+
return props.renderText(p);
|
|
189
|
+
}
|
|
190
|
+
return rItem(p);
|
|
191
|
+
}}
|
|
192
|
+
renderText = {(p) => {
|
|
193
|
+
if(typeof props.renderText ==='function'){
|
|
194
|
+
return props.renderText(p);
|
|
195
|
+
}
|
|
196
|
+
if(typeof props.renderItem ==='function'){
|
|
197
|
+
return props.renderItem(p);
|
|
198
|
+
}
|
|
199
|
+
return rItem(p);
|
|
200
|
+
}}
|
|
201
|
+
hideOnAdd
|
|
202
|
+
onAdd = {({onGoBack})=>{
|
|
203
|
+
onAddProps = defaultObj(isFunction(onAddProps)? onAddProps.call(context,{context,foreignKeyTable,dbName,props}) : onAddProps);
|
|
204
|
+
return navigateToTableData({...onAddProps,tableName : foreignKeyTable,onGoBack})
|
|
205
|
+
}}
|
|
206
|
+
/>
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
TableDataSelectField.propTypes = {
|
|
210
|
+
...Dropdown.propTypes,
|
|
211
|
+
mutateFetchedItems : PropTypes.func, //la fonction permettant d'effectuer une mutation sur l'ensemble des donnéees récupérées à distance
|
|
212
|
+
fetchItems : PropTypes.func,//la fonction de rappel à utiliser pour faire une requête fetch permettant de selectionner les données à distance
|
|
213
|
+
beforeFetchItems : PropTypes.func, //appelée immédiatement avant l'exécution de la requête fetch
|
|
214
|
+
foreignKeyColumn : PropTypes.string.isRequired,//le nom de la clé étrangère à laquelle fait référence la colone dans la fKeyTable
|
|
215
|
+
foreignKeyLabel : PropTypes.string,
|
|
216
|
+
getForeignKeyTable : PropTypes.func.isRequired, //la fonction permettant de récupérer la fKeyTable data dont fait référence le champ
|
|
217
|
+
foreignKeyTable : PropTypes.string, //le nom de la fKeyTable data à laquelle se reporte le champ
|
|
218
|
+
onFetchItems : PropTypes.func,
|
|
219
|
+
fetchDataOpts : PropTypes.shape({
|
|
220
|
+
fields : PropTypes.oneOfType([
|
|
221
|
+
PropTypes.string,
|
|
222
|
+
PropTypes.bool,
|
|
223
|
+
PropTypes.array,
|
|
224
|
+
])
|
|
225
|
+
}),
|
|
226
|
+
itemValue : PropTypes.func,
|
|
227
|
+
renderItem : PropTypes.func,
|
|
228
|
+
renderText : PropTypes.func,
|
|
229
|
+
convertFiltersToSQL : PropTypes.func,// si l'on doit convertir les selecteurs de filtres au format SQl
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export default TableDataSelectField;
|
|
233
|
+
|
|
234
|
+
TableDataSelectField.displayName = "TableDataSelectField";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import SelectField from "
|
|
2
|
-
import SelectTableData from "
|
|
1
|
+
import SelectField from "../SelectField";
|
|
2
|
+
import SelectTableData from "./Component";
|
|
3
3
|
|
|
4
4
|
export default class FormSelectTableDataField extends SelectField{
|
|
5
5
|
_render(props){
|
|
@@ -11,6 +11,12 @@ export default class FormSelectTableDataField extends SelectField{
|
|
|
11
11
|
this.props.onChange(args);
|
|
12
12
|
}
|
|
13
13
|
}}
|
|
14
|
+
beforeFetchItems = {(opts)=>{
|
|
15
|
+
if(typeof props.beforeFetchItems =='function'){
|
|
16
|
+
return props.beforeFetchItems({...opts,context:this,dropdownContext : this._field})
|
|
17
|
+
}
|
|
18
|
+
return true;
|
|
19
|
+
}}
|
|
14
20
|
ref = {(el)=>{
|
|
15
21
|
this._field = el;
|
|
16
22
|
}}
|
|
@@ -3,6 +3,7 @@ import TextField from "./TextField";
|
|
|
3
3
|
import SelectField from "./SelectField";
|
|
4
4
|
import Switch from "./Switch";
|
|
5
5
|
import Checkbox from "./Checkbox";
|
|
6
|
+
import SelectTableData from "./SelectTableData";
|
|
6
7
|
//import IdField from "./IdField";
|
|
7
8
|
//import PieceField from "./PieceField";
|
|
8
9
|
import Slider from "./Slider";
|
|
@@ -14,11 +15,17 @@ import Image from "./Image";
|
|
|
14
15
|
import Tel from "./Tel";
|
|
15
16
|
import SelectCountry from "./SelectCountry";
|
|
16
17
|
import Html from "./Html";
|
|
18
|
+
import * as eFormFields from "$extendFormFields";
|
|
19
|
+
import "$utils";
|
|
20
|
+
import React from "$react";
|
|
17
21
|
|
|
18
|
-
export
|
|
22
|
+
export * from "$extendFormFields";
|
|
23
|
+
|
|
24
|
+
const defFormFields = {
|
|
19
25
|
Field,
|
|
20
26
|
TextField,
|
|
21
27
|
SelectField,
|
|
28
|
+
SelectTableData,
|
|
22
29
|
SelectCountry
|
|
23
30
|
,Switch
|
|
24
31
|
,Checkbox
|
|
@@ -34,10 +41,20 @@ export default {
|
|
|
34
41
|
,Html
|
|
35
42
|
}
|
|
36
43
|
|
|
44
|
+
//pour étendre les FormFields par défaut
|
|
45
|
+
Object.map(eFormFields,(F,i)=>{
|
|
46
|
+
if(React.isComponent(F)){
|
|
47
|
+
defFormFields[i] = F;
|
|
48
|
+
}
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
export default defFormFields;
|
|
52
|
+
|
|
37
53
|
export {
|
|
38
54
|
Field,
|
|
39
55
|
TextField,
|
|
40
56
|
SelectField,
|
|
57
|
+
SelectTableData,
|
|
41
58
|
SelectCountry
|
|
42
59
|
,Switch
|
|
43
60
|
,Checkbox
|
|
@@ -31,18 +31,12 @@ export default class FormDataActionComponent extends FormData {
|
|
|
31
31
|
const mainProps = this.getMainProps();
|
|
32
32
|
const data = this.getDataProp();
|
|
33
33
|
const appBarProps = Object.assign({},mainProps.appBarProps);
|
|
34
|
-
const indexField = this.getIndexFieldProps();
|
|
35
34
|
const isEditing = this.isDocEditing(data);
|
|
36
35
|
let subtitle = (isEditing?'Modifier':this.getNewElementLabel());
|
|
37
36
|
let title = React.getTextContent(defaultVal(appBarProps.title,mainProps.title,this.props.title));
|
|
38
37
|
if(isEditing){
|
|
39
|
-
let _title =
|
|
40
|
-
if(isNonNullString(
|
|
41
|
-
_title = defaultStr(data[indexField]);
|
|
42
|
-
} else if(isNonNullString(data.code)){
|
|
43
|
-
_title = data.code;
|
|
44
|
-
}
|
|
45
|
-
if(_title){
|
|
38
|
+
let _title = this.getPrimaryKeysFieldsValueText(data);
|
|
39
|
+
if(isNonNullString(_title)){
|
|
46
40
|
subtitle+= " ["+_title+"]"
|
|
47
41
|
}
|
|
48
42
|
} else {
|
|
@@ -88,6 +82,12 @@ export default class FormDataActionComponent extends FormData {
|
|
|
88
82
|
canCreateNew(){
|
|
89
83
|
return true;
|
|
90
84
|
}
|
|
85
|
+
/*** permet de récupérer le contenu textuel associé à la données en cours de modification, pour les clés primaires */
|
|
86
|
+
getPrimaryKeysFieldsValueText(data){
|
|
87
|
+
data = defaultObj(data);
|
|
88
|
+
const indexField = this.getIndexFieldProps();
|
|
89
|
+
return isNonNullString(indexField) && isNonNullString(data[indexField])? data[indexField] : isNonNullString(data.code)? data.code : undefined;
|
|
90
|
+
}
|
|
91
91
|
getAppBarActionsProps(props){
|
|
92
92
|
props = defaultObj(props,this.getMainProps(),this.props);
|
|
93
93
|
let {actions,save2NewAction,save2printAction,save2closeAction,saveAction,newAction} = props;
|
|
@@ -102,10 +102,9 @@ export default class FormDataActionComponent extends FormData {
|
|
|
102
102
|
const isEditing = this.isDocEditing(data);
|
|
103
103
|
let textSave = isEditing ? "Modifier": 'Enregistrer';
|
|
104
104
|
const newElementLabel = defaultStr(props.newElementLabel,this.props.newElementLabel,"Nouvel Element");
|
|
105
|
-
const indexField = this.getIndexFieldProps();
|
|
106
105
|
if(isEditing){
|
|
107
|
-
const t =
|
|
108
|
-
if(t){
|
|
106
|
+
const t = this.getPrimaryKeysFieldsValueText(data);
|
|
107
|
+
if(isNonNullString(t)){
|
|
109
108
|
textSave+="["+t+"]"
|
|
110
109
|
}
|
|
111
110
|
}
|
|
@@ -7,6 +7,8 @@ import React from "$react";
|
|
|
7
7
|
const componentTypes = {
|
|
8
8
|
...Fields,
|
|
9
9
|
id : Fields.IdField,
|
|
10
|
+
selecttabledata : Fields.SelectTableData,
|
|
11
|
+
select_tabledata : Fields.SelectTableData,
|
|
10
12
|
idfield : Fields.IdField,
|
|
11
13
|
piecefield : Fields.PieceField,
|
|
12
14
|
piece : Fields.PieceField,
|
|
@@ -78,17 +80,9 @@ export const getFilterComponentProps = (_props)=>{
|
|
|
78
80
|
component = Fields.SelectField;
|
|
79
81
|
if(type =='select_country' || type =='selectcountry'){
|
|
80
82
|
component = Fields.SelectCountry;
|
|
83
|
+
} else if(type =='select_tabledata' || type =='selecttabledata'){
|
|
84
|
+
component = Fields.SelectTableData;
|
|
81
85
|
}
|
|
82
|
-
/*if(type !== 'select'){
|
|
83
|
-
if(type === 'selectstructdata') {
|
|
84
|
-
dbName = 'structData';
|
|
85
|
-
component = StructDataSelectField;
|
|
86
|
-
} else if(type === 'selecttabledata'){
|
|
87
|
-
component = TableDataSelectField;
|
|
88
|
-
}
|
|
89
|
-
props.tableName = tableName;
|
|
90
|
-
props.dbName = dbName;
|
|
91
|
-
}*/
|
|
92
86
|
type = "select";
|
|
93
87
|
} else if(type == 'switch' || type =='radio' || type ==='checkbox') {
|
|
94
88
|
type = 'select';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
2
|
import libPhoneNumber from 'google-libphonenumber';
|
|
3
|
+
import {isNonNullString,defaultStr} from "$cutils";
|
|
3
4
|
|
|
4
5
|
import Country from './country';
|
|
5
6
|
import numberType from './numberType.json'; // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
@@ -10,7 +11,7 @@ const asYouTypeFormatter = libPhoneNumber.AsYouTypeFormatter;
|
|
|
10
11
|
|
|
11
12
|
export const parse = (number,iso2)=>{
|
|
12
13
|
try {
|
|
13
|
-
return phoneUtil.parse(number, iso2);
|
|
14
|
+
return phoneUtil.parse(number, defaultStr(iso2).toLowerCase());
|
|
14
15
|
} catch (err) {
|
|
15
16
|
console.log(`Exception was thrown on parsing phone number : ${err.toString()}`);
|
|
16
17
|
return null;
|
|
@@ -18,7 +19,7 @@ export const parse = (number,iso2)=>{
|
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
export const isValidNumber = (number,iso2)=>{
|
|
21
|
-
const phoneInfo = parse(number, iso2);
|
|
22
|
+
const phoneInfo = parse(number, defaultStr(iso2).toLowerCase());
|
|
22
23
|
if (phoneInfo) {
|
|
23
24
|
return phoneUtil.isValidNumber(phoneInfo);
|
|
24
25
|
}
|
|
@@ -83,16 +84,16 @@ class PhoneNumber {
|
|
|
83
84
|
|
|
84
85
|
// eslint-disable-next-line class-methods-use-this
|
|
85
86
|
parse(number, iso2) {
|
|
86
|
-
return parse(number,iso2);
|
|
87
|
+
return parse(number,defaultStr(iso2).toLowerCase());
|
|
87
88
|
}
|
|
88
89
|
|
|
89
90
|
isValidNumber(number, iso2) {
|
|
90
|
-
return this.isValidNumber(number,iso2);
|
|
91
|
+
return this.isValidNumber(number,defaultStr(iso2).toLowerCase());
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
// eslint-disable-next-line class-methods-use-this
|
|
94
95
|
format(number, iso2) {
|
|
95
|
-
const formatter = new asYouTypeFormatter(iso2); // eslint-disable-line new-cap
|
|
96
|
+
const formatter = new asYouTypeFormatter(defaultStr(iso2).toLowerCase()); // eslint-disable-line new-cap
|
|
96
97
|
let formatted;
|
|
97
98
|
number.replace(/-/g, '')
|
|
98
99
|
.replace(/ /g, '')
|
|
@@ -107,14 +108,14 @@ class PhoneNumber {
|
|
|
107
108
|
}
|
|
108
109
|
|
|
109
110
|
getNumberType(number, iso2) {
|
|
110
|
-
const phoneInfo = this.parse(number, iso2);
|
|
111
|
+
const phoneInfo = this.parse(number, defaultStr(iso2).toLowerCase());
|
|
111
112
|
const typeIndex = phoneInfo ? phoneUtil.getNumberType(phoneInfo) : -1;
|
|
112
113
|
return _.findKey(numberType, (noType) => noType === typeIndex);
|
|
113
114
|
}
|
|
114
115
|
|
|
115
116
|
// eslint-disable-next-line class-methods-use-this
|
|
116
117
|
getCountryDataByCode(iso2) {
|
|
117
|
-
return Country.getCountryDataByCode(iso2);
|
|
118
|
+
return Country.getCountryDataByCode(defaultStr(iso2).toLowerCase());
|
|
118
119
|
}
|
|
119
120
|
}
|
|
120
121
|
|
|
@@ -20,6 +20,8 @@ import {renderTabsContent,renderActions} from "./utils";
|
|
|
20
20
|
import theme from "$theme";
|
|
21
21
|
import cActions from "$cactions";
|
|
22
22
|
import APP from "$capp/instance";
|
|
23
|
+
import { generatedColumnsProperties } from "./utils";
|
|
24
|
+
|
|
23
25
|
|
|
24
26
|
const HIDE_PRELOADER_TIMEOUT = 300;
|
|
25
27
|
|
|
@@ -43,10 +45,13 @@ export default class TableDataScreenComponent extends FormDataScreen{
|
|
|
43
45
|
data : hasManyData ? defaultObj(cDatas[0]) : isObj(mainProps.data)? mainProps.data : {}
|
|
44
46
|
});
|
|
45
47
|
const table = defaultObj(mainProps.table);
|
|
46
|
-
const fields = {};
|
|
48
|
+
const fields = {},primaryKeyFields = {};
|
|
47
49
|
Object.map(table.fields,(field,i)=>{
|
|
48
50
|
if(isObj(field) && field.form !== false){
|
|
49
51
|
fields[i] = Object.clone(field);
|
|
52
|
+
if(field.primary === true){
|
|
53
|
+
primaryKeyFields[field.field || i] = true;
|
|
54
|
+
}
|
|
50
55
|
} else {
|
|
51
56
|
fields[i] = field;
|
|
52
57
|
}
|
|
@@ -63,7 +68,13 @@ export default class TableDataScreenComponent extends FormDataScreen{
|
|
|
63
68
|
titleProp : {value : mainProps.title},
|
|
64
69
|
closeOnSaveProp : {value : mainProps.closeOnSave || mainProps.closeAfterSave },
|
|
65
70
|
newActionProp : {value : mainProps.newAction},
|
|
66
|
-
|
|
71
|
+
//la liste des champ de type clé primaire associés à la table
|
|
72
|
+
primaryKeyFields : {value : primaryKeyFields},
|
|
73
|
+
cloneProp : {value : typeof mainProps.clone =='function' && mainProps.clone || undefined},
|
|
74
|
+
printProp : {value : typeof mainProps.print =='function' && mainProps.print || undefined},
|
|
75
|
+
archiveProp : {value : typeof mainProps.archive =='function' && mainProps.archive || undefined },
|
|
76
|
+
testIDProp : {value : defaultStr(mainProps.testID)},
|
|
77
|
+
isDocEditingProp : {value : typeof mainProps.isDocEditing =='function'? mainProps.isDocEditing : typeof mainProps.isDocUpdate =='function'? mainProps.isDocUpdate : undefined}
|
|
67
78
|
});
|
|
68
79
|
this.hidePreloader = this.hidePreloader.bind(this);
|
|
69
80
|
this.showPreloader = this.showPreloader.bind(this);
|
|
@@ -155,6 +166,13 @@ export default class TableDataScreenComponent extends FormDataScreen{
|
|
|
155
166
|
Object.map(this.fields,(field,i)=>{
|
|
156
167
|
if(isObj(field)){
|
|
157
168
|
fields[i] = Object.clone(field);
|
|
169
|
+
generatedColumnsProperties.map((f)=>{
|
|
170
|
+
//on affiche les champs générés uniquement en cas de mise à jour
|
|
171
|
+
if(field[f] === true){
|
|
172
|
+
fields[f].visible = isUpdated ? true : false;
|
|
173
|
+
fields[i].readOnly = true;
|
|
174
|
+
}
|
|
175
|
+
});
|
|
158
176
|
if(isUpdated){
|
|
159
177
|
//la props readOnlyOnEditing permet de rendre le champ readOnly en cas de mise à jour de la tableData
|
|
160
178
|
if((field.readOnlyOnEditing === true)){
|
|
@@ -164,6 +182,7 @@ export default class TableDataScreenComponent extends FormDataScreen{
|
|
|
164
182
|
fields[i].disabled = true;
|
|
165
183
|
}
|
|
166
184
|
}
|
|
185
|
+
|
|
167
186
|
} else {
|
|
168
187
|
fields[i] = field;
|
|
169
188
|
}
|
|
@@ -256,7 +275,7 @@ export default class TableDataScreenComponent extends FormDataScreen{
|
|
|
256
275
|
const restProps = this.getCurrentEditingProps();
|
|
257
276
|
delete restProps.tabs;
|
|
258
277
|
let {tabProps,firstTabProps,tabsProps,withScrollView} = restProps;
|
|
259
|
-
let testID =
|
|
278
|
+
let testID = this.testIDProp;
|
|
260
279
|
tabsProps = defaultObj(tabsProps);
|
|
261
280
|
tabsProps.tabItemsProps = defaultObj(tabsProps.tabItemsProps);
|
|
262
281
|
if(typeof withScrollView =='boolean' && typeof tabsProps.withScrollView !=='boolean'){
|
|
@@ -385,10 +404,40 @@ export default class TableDataScreenComponent extends FormDataScreen{
|
|
|
385
404
|
isPrintable(){
|
|
386
405
|
return false;
|
|
387
406
|
}
|
|
407
|
+
/*** retourne la liste des valeurs de clé primarire associés à la table data pour la données en cours de modification
|
|
408
|
+
* Elle permet d'afficher dans la barre de titre, les identifiants de la table de données en cours de modification
|
|
409
|
+
*/
|
|
410
|
+
getPrimaryKeysFieldsValueText(data){
|
|
411
|
+
data = defaultObj(data,this.getCurrentData());
|
|
412
|
+
const v = [];
|
|
413
|
+
Object.map(this.primaryKeyFields,(vv,f)=>{
|
|
414
|
+
if(typeof data[f] =='number' || (typeof data[f] =='string' && data[f])){
|
|
415
|
+
v.push(data[f]);
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
return v.join(" ");
|
|
419
|
+
}
|
|
420
|
+
isDocEditing(data){
|
|
421
|
+
data = defaultObj(data);
|
|
422
|
+
if(!this.isDocEditingProp){
|
|
423
|
+
let hasPrimaryFields = false;
|
|
424
|
+
let hasValidated = true;
|
|
425
|
+
Object.map(this.primaryKeyFields,(v,f)=>{
|
|
426
|
+
hasPrimaryFields = true;
|
|
427
|
+
if(!(f in data) || (data[f] == null) || (!data[f] && typeof data !=='number')){
|
|
428
|
+
hasValidated = false;
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
if(hasPrimaryFields){
|
|
432
|
+
return hasValidated;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
return super.isDocEditing(data);
|
|
436
|
+
}
|
|
388
437
|
print(data){
|
|
389
|
-
if(!this.isPrintable() && typeof this.
|
|
438
|
+
if(!this.isPrintable() && typeof this.printProp!=='function') return;
|
|
390
439
|
data = this.isDocEditing(data)? data : isObj(data) && this.isDocEditing(data.data)? data.data : {};
|
|
391
|
-
return this.
|
|
440
|
+
return this.printProp(data,this);
|
|
392
441
|
}
|
|
393
442
|
isClonable(){
|
|
394
443
|
return true;
|
|
@@ -396,10 +445,10 @@ export default class TableDataScreenComponent extends FormDataScreen{
|
|
|
396
445
|
clone (data){
|
|
397
446
|
if(!this._isMounted() || !this.isClonable())return data;
|
|
398
447
|
data = {...this.getCurrentEditingData(data)};
|
|
399
|
-
if(
|
|
448
|
+
if(this.cloneProp && this.cloneProp(data,this) === false) return data;
|
|
400
449
|
this.showPreloader();
|
|
401
450
|
delete data.approved;
|
|
402
|
-
Object.map(['_rev','_id','code','
|
|
451
|
+
Object.map(['_rev',...generatedColumnsProperties,'_id','code','updateBy','updatedDate','createBy','updatedHour','createdHour','createdDate'],(idx)=>{
|
|
403
452
|
data[idx] = undefined;
|
|
404
453
|
delete data[idx];
|
|
405
454
|
});
|
|
@@ -5,6 +5,8 @@ import {Colors} from "$theme";
|
|
|
5
5
|
import { COPY_ICON,PRINT_ICON} from "$ecomponents/Icon";
|
|
6
6
|
import {getTableDataRouteName} from "$enavigation/utils";
|
|
7
7
|
|
|
8
|
+
//@seee : https://github.com/typeorm/typeorm/blob/master/src/entity-schema/EntitySchemaColumnOptions.ts
|
|
9
|
+
export const generatedColumnsProperties = ["createDate","updateDate","deleteDate","createBy","updateBy","deleteBy"]
|
|
8
10
|
|
|
9
11
|
export const renderTabsContent = ({tabs,context,data,sessionName,isMobile,props,firstTab,tabKey,tabProps,tabsProps})=>{
|
|
10
12
|
let tabsArr = [],hasFirstTab = false;
|