@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.
@@ -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.11.4",
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.0",
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,appConfig.get("convertDatagridFiltersToSQL"));;
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
- title = {defaultStr(label,text)+"[ "+self.state.data.length.formatNumber()+" ]"}
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}}}
@@ -0,0 +1,5 @@
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
+ /**** pour étendre les formFields, ils suffit d'override cet alias*/
@@ -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
- this.props.onBlur({event,context:this})
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 "./SelectField";
2
- import SelectTableData from "$containers/TableData/SelectField";
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 default {
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
@@ -149,7 +149,6 @@ export default class FormDataComponent extends AppComponent{
149
149
  return this.props.isDocUpdate({data,context:this}) ? true : false;
150
150
  }
151
151
  return false;
152
- //return isDocUpdate(data) ? true : false;
153
152
  }
154
153
  canBindResizeEvents(){
155
154
  return false;
@@ -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(indexField)){
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 = isNonNullString(indexField) && isNonNullString(data[indexField])? data[indexField] : isNonNullString(data.code)? data.code : undefined;
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 = defaultStr(this.props.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.props.print!=='function') return;
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.props.print(data,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(typeof this.props.clone =='function' && this.props.clone(data,this) === false) return data;
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','updatedBy','updatedDate','createdBy','updatedHour','createdHour','createdDate'],(idx)=>{
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;