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,56 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import Platform from '../Utilities/Platform';
let isEnabled = false;
if (Platform.OS === 'web') {
const canUseDOM = Boolean(
typeof window !== 'undefined' &&
window.document &&
window.document.createElement,
);
if (canUseDOM) {
/**
* Web browsers emulate mouse events (and hover states) after touch events.
* This code infers when the currently-in-use modality supports hover
* (including for multi-modality devices) and considers "hover" to be enabled
* if a mouse movement occurs more than 1 second after the last touch event.
* This threshold is long enough to account for longer delays between the
* browser firing touch and mouse events on low-powered devices.
*/
const HOVER_THRESHOLD_MS = 1000;
let lastTouchTimestamp = 0;
const enableHover = () => {
if (isEnabled || Date.now() - lastTouchTimestamp < HOVER_THRESHOLD_MS) {
return;
}
isEnabled = true;
};
const disableHover = () => {
lastTouchTimestamp = Date.now();
if (isEnabled) {
isEnabled = false;
}
};
document.addEventListener('touchstart', disableHover, true);
document.addEventListener('touchmove', disableHover, true);
document.addEventListener('mousemove', enableHover, true);
}
}
export function isHoverEnabled(): boolean {
return isEnabled;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,85 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import type {ColorValue} from '../StyleSheet/StyleSheet';
import View from '../Components/View/View';
import normalizeColor from '../StyleSheet/normalizeColor';
import {type RectOrSize, normalizeRect} from '../StyleSheet/Rect';
import * as React from 'react';
type Props = $ReadOnly<{|
color: ColorValue,
hitSlop: ?RectOrSize,
|}>;
/**
* Displays a debug overlay to visualize press targets when enabled via the
* React Native Inspector. Calls to this module should be guarded by `__DEV__`,
* for example:
*
* return (
* <View>
* {children}
* {__DEV__ ? (
* <PressabilityDebugView color="..." hitSlop={props.hitSlop} />
* ) : null}
* </View>
* );
*
*/
export function PressabilityDebugView(props: Props): React.Node {
if (__DEV__) {
if (isEnabled()) {
const normalizedColor = normalizeColor(props.color);
if (typeof normalizedColor !== 'number') {
return null;
}
const baseColor =
'#' + (normalizedColor ?? 0).toString(16).padStart(8, '0');
const hitSlop = normalizeRect(props.hitSlop);
return (
<View
pointerEvents="none"
style={
// eslint-disable-next-line react-native/no-inline-styles
{
backgroundColor: baseColor.slice(0, -2) + '0F', // 15%
borderColor: baseColor.slice(0, -2) + '55', // 85%
borderStyle: 'dashed',
borderWidth: 1,
bottom: -(hitSlop?.bottom ?? 0),
left: -(hitSlop?.left ?? 0),
position: 'absolute',
right: -(hitSlop?.right ?? 0),
top: -(hitSlop?.top ?? 0),
}
}
/>
);
}
}
return null;
}
let isDebugEnabled = false;
export function isEnabled(): boolean {
if (__DEV__) {
return isDebugEnabled;
}
return false;
}
export function setEnabled(value: boolean): void {
if (__DEV__) {
isDebugEnabled = value;
}
}

View File

@@ -0,0 +1,49 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import {type PressabilityTouchSignal as TouchSignal} from './PressabilityTypes.js';
export type PressabilityPerformanceEvent = $ReadOnly<{|
signal: TouchSignal,
nativeTimestamp: number,
|}>;
export type PressabilityPerformanceEventListener =
PressabilityPerformanceEvent => void;
class PressabilityPerformanceEventEmitter {
_listeners: Array<PressabilityPerformanceEventListener> = [];
constructor() {}
addListener(listener: PressabilityPerformanceEventListener): void {
this._listeners.push(listener);
}
removeListener(listener: PressabilityPerformanceEventListener): void {
const index = this._listeners.indexOf(listener);
if (index > -1) {
this._listeners.splice(index, 1);
}
}
emitEvent(constructEvent: () => PressabilityPerformanceEvent): void {
if (this._listeners.length === 0) {
return;
}
const event = constructEvent();
this._listeners.forEach(listener => listener(event));
}
}
const PressabilityPerformanceEventEmitterSingleton: PressabilityPerformanceEventEmitter =
new PressabilityPerformanceEventEmitter();
export default PressabilityPerformanceEventEmitterSingleton;

View File

@@ -0,0 +1,18 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
export type PressabilityTouchSignal =
| 'DELAY'
| 'RESPONDER_GRANT'
| 'RESPONDER_RELEASE'
| 'RESPONDER_TERMINATED'
| 'ENTER_PRESS_RECT'
| 'LEAVE_PRESS_RECT'
| 'LONG_PRESS_DETECTED';

View File

@@ -0,0 +1,57 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import Pressability, {
type EventHandlers,
type PressabilityConfig,
} from './Pressability';
import {useEffect, useRef} from 'react';
/**
* Creates a persistent instance of `Pressability` that automatically configures
* itself and resets. Accepts null `config` to support lazy initialization. Once
* initialized, will not un-initialize until the component has been unmounted.
*
* In order to use `usePressability`, do the following:
*
* const config = useMemo(...);
* const eventHandlers = usePressability(config);
* const pressableView = <View {...eventHandlers} />;
*
*/
export default function usePressability(
config: ?PressabilityConfig,
): ?EventHandlers {
const pressabilityRef = useRef<?Pressability>(null);
if (config != null && pressabilityRef.current == null) {
pressabilityRef.current = new Pressability(config);
}
const pressability = pressabilityRef.current;
// On the initial mount, this is a no-op. On updates, `pressability` will be
// re-configured to use the new configuration.
useEffect(() => {
if (config != null && pressability != null) {
pressability.configure(config);
}
}, [config, pressability]);
// On unmount, reset pending state and timers inside `pressability`. This is
// a separate effect because we do not want to reset when `config` changes.
useEffect(() => {
if (pressability != null) {
return () => {
pressability.reset();
};
}
}, [pressability]);
return pressability == null ? null : pressability.getEventHandlers();
}