feat(smart-app): implement complete mobile app MVP

- App.tsx: full navigation (Auth stack + Main tabs with 5 screens)
- Auth: LoginScreen, RegisterScreen, ForgotPasswordScreen
- HomeScreen: dashboard with IoT metrics, weather widget, alerts, quick actions, sensors
- MapScreen: interactive map with layer toggles (6 layers)
- MarketplaceScreen: categories (6), products (5), search
- ChatScreen: AI chat with quick prompts (4), bot responses
- ProfileScreen: user info, stats, menu (9 items), logout
- AlertsScreen: alert list with severity, acknowledge
- SensorsScreen: sensor list with type filters (6 types), search
- ZonesScreen: zone cards with stats
- SettingsScreen: language picker (FR/EN/ES/DE), privacy, about
- Stores: iotStore (sensors, zones, alerts), notificationStore, uiStore + i18n
- Hooks: useSensors, useAlerts, useNotifications, useLocation
- Components: Card, Button, LoadingSpinner, ErrorBoundary, Header
- Services: iotService, notificationService (with axios API client)
- Utils: formatters (temp, AQI, noise, dates), validators (email, password, IBAN)
- Theme: colors.ts with full design system (Blue Ocean palette)
- Ditto: fixed MongoDB connection, new JWT secrets, official gateway image
This commit is contained in:
Eric FELIXINE
2026-06-01 18:00:35 -04:00
parent 08ca495bde
commit e30ae8ed09
35578 changed files with 3703534 additions and 43 deletions

View File

@@ -0,0 +1,103 @@
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
import * as React from 'react';
import { StyleSheet } from 'react-native';
import color from 'color';
import { ToggleButtonGroupContext } from './ToggleButtonGroup';
import { getToggleButtonColor } from './utils';
import { useInternalTheme } from '../../core/theming';
import { black, white } from '../../styles/themes/v2/colors';
import { forwardRef } from '../../utils/forwardRef';
import IconButton from '../IconButton/IconButton';
/**
* Toggle buttons can be used to group related options. To emphasize groups of related toggle buttons,
* a group should share a common container.
*
* ## Usage
* ```js
* import * as React from 'react';
* import { ToggleButton } from 'react-native-paper';
*
* const ToggleButtonExample = () => {
* const [status, setStatus] = React.useState('checked');
*
* const onButtonToggle = value => {
* setStatus(status === 'checked' ? 'unchecked' : 'checked');
* };
*
* return (
* <ToggleButton
* icon="bluetooth"
* value="bluetooth"
* status={status}
* onPress={onButtonToggle}
* />
* );
* };
*
* export default ToggleButtonExample;
*
* ```
*/
const ToggleButton = forwardRef(({
icon,
size,
theme: themeOverrides,
accessibilityLabel,
disabled,
style,
value,
status,
onPress,
rippleColor,
...rest
}, ref) => {
const theme = useInternalTheme(themeOverrides);
const borderRadius = theme.roundness;
return /*#__PURE__*/React.createElement(ToggleButtonGroupContext.Consumer, null, context => {
const checked = context && context.value === value || status === 'checked';
const backgroundColor = getToggleButtonColor({
theme,
checked
});
const borderColor = theme.isV3 ? theme.colors.outline : color(theme.dark ? white : black).alpha(0.29).rgb().string();
return /*#__PURE__*/React.createElement(IconButton, _extends({
borderless: false,
icon: icon,
onPress: e => {
if (onPress) {
onPress(e);
}
if (context) {
context.onValueChange(!checked ? value : null);
}
},
size: size,
accessibilityLabel: accessibilityLabel,
accessibilityState: {
disabled,
selected: checked
},
disabled: disabled,
style: [styles.content, {
backgroundColor,
borderRadius,
borderColor
}, style],
ref: ref,
theme: theme,
rippleColor: rippleColor
}, rest));
});
});
const styles = StyleSheet.create({
content: {
width: 42,
height: 42,
margin: 0
}
});
export default ToggleButton;
// @component-docs ignore-next-line
export { ToggleButton };
//# sourceMappingURL=ToggleButton.js.map

View File

@@ -0,0 +1 @@
{"version":3,"names":["React","StyleSheet","color","ToggleButtonGroupContext","getToggleButtonColor","useInternalTheme","black","white","forwardRef","IconButton","ToggleButton","icon","size","theme","themeOverrides","accessibilityLabel","disabled","style","value","status","onPress","rippleColor","rest","ref","borderRadius","roundness","createElement","Consumer","context","checked","backgroundColor","borderColor","isV3","colors","outline","dark","alpha","rgb","string","_extends","borderless","e","onValueChange","accessibilityState","selected","styles","content","create","width","height","margin"],"sourceRoot":"../../../../src","sources":["components/ToggleButton/ToggleButton.tsx"],"mappings":";AAAA,OAAO,KAAKA,KAAK,MAAM,OAAO;AAC9B,SAGEC,UAAU,QAKL,cAAc;AAErB,OAAOC,KAAK,MAAM,OAAO;AAEzB,SAASC,wBAAwB,QAAQ,qBAAqB;AAC9D,SAASC,oBAAoB,QAAQ,SAAS;AAC9C,SAASC,gBAAgB,QAAQ,oBAAoB;AACrD,SAASC,KAAK,EAAEC,KAAK,QAAQ,+BAA+B;AAE5D,SAASC,UAAU,QAAQ,wBAAwB;AAEnD,OAAOC,UAAU,MAAM,0BAA0B;AAmDjD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,YAAY,GAAGF,UAAU,CAC7B,CACE;EACEG,IAAI;EACJC,IAAI;EACJC,KAAK,EAAEC,cAAc;EACrBC,kBAAkB;EAClBC,QAAQ;EACRC,KAAK;EACLC,KAAK;EACLC,MAAM;EACNC,OAAO;EACPC,WAAW;EACX,GAAGC;AACE,CAAC,EACRC,GAAG,KACA;EACH,MAAMV,KAAK,GAAGR,gBAAgB,CAACS,cAAc,CAAC;EAC9C,MAAMU,YAAY,GAAGX,KAAK,CAACY,SAAS;EAEpC,oBACEzB,KAAA,CAAA0B,aAAA,CAACvB,wBAAwB,CAACwB,QAAQ,QAE9BC,OAAiE,IAC9D;IACH,MAAMC,OAAuB,GAC1BD,OAAO,IAAIA,OAAO,CAACV,KAAK,KAAKA,KAAK,IAAKC,MAAM,KAAK,SAAS;IAE9D,MAAMW,eAAe,GAAG1B,oBAAoB,CAAC;MAAES,KAAK;MAAEgB;IAAQ,CAAC,CAAC;IAChE,MAAME,WAAW,GAAGlB,KAAK,CAACmB,IAAI,GAC1BnB,KAAK,CAACoB,MAAM,CAACC,OAAO,GACpBhC,KAAK,CAACW,KAAK,CAACsB,IAAI,GAAG5B,KAAK,GAAGD,KAAK,CAAC,CAC9B8B,KAAK,CAAC,IAAI,CAAC,CACXC,GAAG,CAAC,CAAC,CACLC,MAAM,CAAC,CAAC;IAEf,oBACEtC,KAAA,CAAA0B,aAAA,CAACjB,UAAU,EAAA8B,QAAA;MACTC,UAAU,EAAE,KAAM;MAClB7B,IAAI,EAAEA,IAAK;MACXS,OAAO,EAAGqB,CAAkC,IAAK;QAC/C,IAAIrB,OAAO,EAAE;UACXA,OAAO,CAACqB,CAAC,CAAC;QACZ;QAEA,IAAIb,OAAO,EAAE;UACXA,OAAO,CAACc,aAAa,CAAC,CAACb,OAAO,GAAGX,KAAK,GAAG,IAAI,CAAC;QAChD;MACF,CAAE;MACFN,IAAI,EAAEA,IAAK;MACXG,kBAAkB,EAAEA,kBAAmB;MACvC4B,kBAAkB,EAAE;QAAE3B,QAAQ;QAAE4B,QAAQ,EAAEf;MAAQ,CAAE;MACpDb,QAAQ,EAAEA,QAAS;MACnBC,KAAK,EAAE,CACL4B,MAAM,CAACC,OAAO,EACd;QACEhB,eAAe;QACfN,YAAY;QACZO;MACF,CAAC,EACDd,KAAK,CACL;MACFM,GAAG,EAAEA,GAAI;MACTV,KAAK,EAAEA,KAAM;MACbQ,WAAW,EAAEA;IAAY,GACrBC,IAAI,CACT,CAAC;EAEN,CACiC,CAAC;AAExC,CACF,CAAC;AAED,MAAMuB,MAAM,GAAG5C,UAAU,CAAC8C,MAAM,CAAC;EAC/BD,OAAO,EAAE;IACPE,KAAK,EAAE,EAAE;IACTC,MAAM,EAAE,EAAE;IACVC,MAAM,EAAE;EACV;AACF,CAAC,CAAC;AAEF,eAAexC,YAAY;;AAE3B;AACA,SAASA,YAAY","ignoreList":[]}

View File

@@ -0,0 +1,48 @@
import * as React from 'react';
export const ToggleButtonGroupContext =
/*#__PURE__*/
//@ts-expect-error: TS can't ensure the type from Group to children
React.createContext(null);
/**
* Toggle group allows to control a group of toggle buttons.</br>
* It doesn't change the appearance of the toggle buttons. If you want to group them in a row, check out [ToggleButton.Row](ToggleButtonRow).
*
* ## Usage
* ```js
* import * as React from 'react';
* import { ToggleButton } from 'react-native-paper';
*
* const MyComponent = () => {
* const [value, setValue] = React.useState('left');
*
* return (
* <ToggleButton.Group
* onValueChange={value => setValue(value)}
* value={value}
* >
* <ToggleButton icon="format-align-left" value="left" />
* <ToggleButton icon="format-align-right" value="right" />
* </ToggleButton.Group>
* );
* };
*
* export default MyComponent;
*```
*/
const ToggleButtonGroup = ({
value,
onValueChange,
children
}) => /*#__PURE__*/React.createElement(ToggleButtonGroupContext.Provider, {
value: {
value,
onValueChange
}
}, children);
ToggleButtonGroup.displayName = 'ToggleButton.Group';
export default ToggleButtonGroup;
// @component-docs ignore-next-line
export { ToggleButtonGroup };
//# sourceMappingURL=ToggleButtonGroup.js.map

View File

@@ -0,0 +1 @@
{"version":3,"names":["React","ToggleButtonGroupContext","createContext","ToggleButtonGroup","value","onValueChange","children","createElement","Provider","displayName"],"sourceRoot":"../../../../src","sources":["components/ToggleButton/ToggleButtonGroup.tsx"],"mappings":"AAAA,OAAO,KAAKA,KAAK,MAAM,OAAO;AAsB9B,OAAO,MAAMC,wBAAwB;AAAA;AACnC;AACAD,KAAK,CAACE,aAAa,CAA0B,IAAW,CAAC;;AAE3D;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,iBAAiB,GAAGA,CAAkB;EAC1CC,KAAK;EACLC,aAAa;EACbC;AACY,CAAC,kBACbN,KAAA,CAAAO,aAAA,CAACN,wBAAwB,CAACO,QAAQ;EAChCJ,KAAK,EAAE;IACLA,KAAK;IACLC;EACF;AAAE,GAEDC,QACgC,CACpC;AAEDH,iBAAiB,CAACM,WAAW,GAAG,oBAAoB;AAEpD,eAAeN,iBAAiB;;AAEhC;AACA,SAASA,iBAAiB","ignoreList":[]}

View File

@@ -0,0 +1,79 @@
import * as React from 'react';
import { StyleSheet, View } from 'react-native';
import ToggleButton from './ToggleButton';
import ToggleButtonGroup from './ToggleButtonGroup';
/**
* Toggle button row renders a group of toggle buttons in a row.
*
* ## Usage
* ```js
* import * as React from 'react';
* import { ToggleButton } from 'react-native-paper';
*
* const MyComponent = () => {
* const [value, setValue] = React.useState('left');
*
* return (
* <ToggleButton.Row onValueChange={value => setValue(value)} value={value}>
* <ToggleButton icon="format-align-left" value="left" />
* <ToggleButton icon="format-align-right" value="right" />
* </ToggleButton.Row>
* );
* };
*
* export default MyComponent;
*
*```
*/
const ToggleButtonRow = ({
value,
onValueChange,
children,
style
}) => {
const count = React.Children.count(children);
return /*#__PURE__*/React.createElement(ToggleButtonGroup, {
value: value,
onValueChange: onValueChange
}, /*#__PURE__*/React.createElement(View, {
style: [styles.row, style]
}, React.Children.map(children, (child, i) => {
// @ts-expect-error: TypeScript complains about child.type but it doesn't matter
if (child && child.type === ToggleButton) {
// @ts-expect-error: We're sure that child is a React Element
return /*#__PURE__*/React.cloneElement(child, {
style: [styles.button, i === 0 ? styles.first : i === count - 1 ? styles.last : styles.middle,
// @ts-expect-error: We're sure that child is a React Element
child.props.style]
});
}
return child;
})));
};
ToggleButtonRow.displayName = 'ToggleButton.Row';
const styles = StyleSheet.create({
row: {
flexDirection: 'row'
},
button: {
borderWidth: StyleSheet.hairlineWidth
},
first: {
borderTopRightRadius: 0,
borderBottomRightRadius: 0
},
middle: {
borderRadius: 0,
borderLeftWidth: 0
},
last: {
borderLeftWidth: 0,
borderTopLeftRadius: 0,
borderBottomLeftRadius: 0
}
});
export default ToggleButtonRow;
// @component-docs ignore-next-line
export { ToggleButtonRow };
//# sourceMappingURL=ToggleButtonRow.js.map

View File

@@ -0,0 +1 @@
{"version":3,"names":["React","StyleSheet","View","ToggleButton","ToggleButtonGroup","ToggleButtonRow","value","onValueChange","children","style","count","Children","createElement","styles","row","map","child","i","type","cloneElement","button","first","last","middle","props","displayName","create","flexDirection","borderWidth","hairlineWidth","borderTopRightRadius","borderBottomRightRadius","borderRadius","borderLeftWidth","borderTopLeftRadius","borderBottomLeftRadius"],"sourceRoot":"../../../../src","sources":["components/ToggleButton/ToggleButtonRow.tsx"],"mappings":"AAAA,OAAO,KAAKA,KAAK,MAAM,OAAO;AAC9B,SAASC,UAAU,EAAEC,IAAI,QAA8B,cAAc;AAErE,OAAOC,YAAY,MAAM,gBAAgB;AACzC,OAAOC,iBAAiB,MAAM,qBAAqB;AAkBnD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,eAAe,GAAGA,CAAC;EAAEC,KAAK;EAAEC,aAAa;EAAEC,QAAQ;EAAEC;AAAa,CAAC,KAAK;EAC5E,MAAMC,KAAK,GAAGV,KAAK,CAACW,QAAQ,CAACD,KAAK,CAACF,QAAQ,CAAC;EAE5C,oBACER,KAAA,CAAAY,aAAA,CAACR,iBAAiB;IAACE,KAAK,EAAEA,KAAM;IAACC,aAAa,EAAEA;EAAc,gBAC5DP,KAAA,CAAAY,aAAA,CAACV,IAAI;IAACO,KAAK,EAAE,CAACI,MAAM,CAACC,GAAG,EAAEL,KAAK;EAAE,GAC9BT,KAAK,CAACW,QAAQ,CAACI,GAAG,CAACP,QAAQ,EAAE,CAACQ,KAAK,EAAEC,CAAC,KAAK;IAC1C;IACA,IAAID,KAAK,IAAIA,KAAK,CAACE,IAAI,KAAKf,YAAY,EAAE;MACxC;MACA,oBAAOH,KAAK,CAACmB,YAAY,CAACH,KAAK,EAAE;QAC/BP,KAAK,EAAE,CACLI,MAAM,CAACO,MAAM,EACbH,CAAC,KAAK,CAAC,GACHJ,MAAM,CAACQ,KAAK,GACZJ,CAAC,KAAKP,KAAK,GAAG,CAAC,GACfG,MAAM,CAACS,IAAI,GACXT,MAAM,CAACU,MAAM;QACjB;QACAP,KAAK,CAACQ,KAAK,CAACf,KAAK;MAErB,CAAC,CAAC;IACJ;IAEA,OAAOO,KAAK;EACd,CAAC,CACG,CACW,CAAC;AAExB,CAAC;AAEDX,eAAe,CAACoB,WAAW,GAAG,kBAAkB;AAEhD,MAAMZ,MAAM,GAAGZ,UAAU,CAACyB,MAAM,CAAC;EAC/BZ,GAAG,EAAE;IACHa,aAAa,EAAE;EACjB,CAAC;EACDP,MAAM,EAAE;IACNQ,WAAW,EAAE3B,UAAU,CAAC4B;EAC1B,CAAC;EAEDR,KAAK,EAAE;IACLS,oBAAoB,EAAE,CAAC;IACvBC,uBAAuB,EAAE;EAC3B,CAAC;EAEDR,MAAM,EAAE;IACNS,YAAY,EAAE,CAAC;IACfC,eAAe,EAAE;EACnB,CAAC;EAEDX,IAAI,EAAE;IACJW,eAAe,EAAE,CAAC;IAClBC,mBAAmB,EAAE,CAAC;IACtBC,sBAAsB,EAAE;EAC1B;AACF,CAAC,CAAC;AAEF,eAAe9B,eAAe;;AAE9B;AACA,SAASA,eAAe","ignoreList":[]}

View File

@@ -0,0 +1,13 @@
import ToggleButtonComponent from './ToggleButton';
import ToggleButtonGroup from './ToggleButtonGroup';
import ToggleButtonRow from './ToggleButtonRow';
const ToggleButton = Object.assign(
// @component ./ToggleButton.tsx
ToggleButtonComponent, {
// @component ./ToggleButtonGroup.tsx
Group: ToggleButtonGroup,
// @component ./ToggleButtonRow.tsx
Row: ToggleButtonRow
});
export default ToggleButton;
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"names":["ToggleButtonComponent","ToggleButtonGroup","ToggleButtonRow","ToggleButton","Object","assign","Group","Row"],"sourceRoot":"../../../../src","sources":["components/ToggleButton/index.ts"],"mappings":"AAAA,OAAOA,qBAAqB,MAAM,gBAAgB;AAClD,OAAOC,iBAAiB,MAAM,qBAAqB;AACnD,OAAOC,eAAe,MAAM,mBAAmB;AAE/C,MAAMC,YAAY,GAAGC,MAAM,CAACC,MAAM;AAChC;AACAL,qBAAqB,EACrB;EACE;EACAM,KAAK,EAAEL,iBAAiB;EACxB;EACAM,GAAG,EAAEL;AACP,CACF,CAAC;AAED,eAAeC,YAAY","ignoreList":[]}

View File

@@ -0,0 +1,18 @@
import color from 'color';
import { tokens } from '../../styles/themes/v3/tokens';
export const getToggleButtonColor = ({
theme,
checked
}) => {
if (checked) {
if (theme.isV3) {
return color(theme.colors.onSecondaryContainer).alpha(tokens.md.ref.opacity.level2).rgb().string();
}
if (theme.dark) {
return 'rgba(255, 255, 255, .12)';
}
return 'rgba(0, 0, 0, .08)';
}
return 'transparent';
};
//# sourceMappingURL=utils.js.map

View File

@@ -0,0 +1 @@
{"version":3,"names":["color","tokens","getToggleButtonColor","theme","checked","isV3","colors","onSecondaryContainer","alpha","md","ref","opacity","level2","rgb","string","dark"],"sourceRoot":"../../../../src","sources":["components/ToggleButton/utils.ts"],"mappings":"AAAA,OAAOA,KAAK,MAAM,OAAO;AAEzB,SAASC,MAAM,QAAQ,+BAA+B;AAGtD,OAAO,MAAMC,oBAAoB,GAAGA,CAAC;EACnCC,KAAK;EACLC;AAIF,CAAC,KAAK;EACJ,IAAIA,OAAO,EAAE;IACX,IAAID,KAAK,CAACE,IAAI,EAAE;MACd,OAAOL,KAAK,CAACG,KAAK,CAACG,MAAM,CAACC,oBAAoB,CAAC,CAC5CC,KAAK,CAACP,MAAM,CAACQ,EAAE,CAACC,GAAG,CAACC,OAAO,CAACC,MAAM,CAAC,CACnCC,GAAG,CAAC,CAAC,CACLC,MAAM,CAAC,CAAC;IACb;IACA,IAAIX,KAAK,CAACY,IAAI,EAAE;MACd,OAAO,0BAA0B;IACnC;IACA,OAAO,oBAAoB;EAC7B;EACA,OAAO,aAAa;AACtB,CAAC","ignoreList":[]}