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,3 @@
declare const _default: any;
export default _default;
//# sourceMappingURL=ExpoKeepAwake.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ExpoKeepAwake.d.ts","sourceRoot":"","sources":["../src/ExpoKeepAwake.ts"],"names":[],"mappings":";AACA,wBAAoD"}

View File

@@ -0,0 +1,3 @@
import { requireNativeModule } from 'expo-modules-core';
export default requireNativeModule('ExpoKeepAwake');
//# sourceMappingURL=ExpoKeepAwake.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ExpoKeepAwake.js","sourceRoot":"","sources":["../src/ExpoKeepAwake.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,eAAe,mBAAmB,CAAC,eAAe,CAAC,CAAC","sourcesContent":["import { requireNativeModule } from 'expo-modules-core';\nexport default requireNativeModule('ExpoKeepAwake');\n"]}

View File

@@ -0,0 +1,11 @@
import { Subscription } from 'expo-modules-core';
import { KeepAwakeListener } from './KeepAwake.types';
/** Wraps the webWakeLock API https://developer.mozilla.org/en-US/docs/Web/API/Screen_Wake_Lock_API */
declare const _default: {
isAvailableAsync(): Promise<boolean>;
activate(tag: string): Promise<void>;
deactivate(tag: string): Promise<void>;
addListenerForTag(tag: string, listener: KeepAwakeListener): Subscription;
};
export default _default;
//# sourceMappingURL=ExpoKeepAwake.web.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ExpoKeepAwake.web.d.ts","sourceRoot":"","sources":["../src/ExpoKeepAwake.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEvE,OAAO,EAAuB,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAoB3E,sGAAsG;;;kBAQhF,MAAM;oBAOJ,MAAM;2BAcL,MAAM,YAAY,iBAAiB,GAAG,YAAY;;AA5B3E,wBAqDE"}

View File

@@ -0,0 +1,59 @@
import { CodedError, Platform } from 'expo-modules-core';
import { KeepAwakeEventState } from './KeepAwake.types';
const wakeLockMap = {};
/** Wraps the webWakeLock API https://developer.mozilla.org/en-US/docs/Web/API/Screen_Wake_Lock_API */
export default {
async isAvailableAsync() {
if (Platform.isDOMAvailable) {
return 'wakeLock' in navigator;
}
return false;
},
async activate(tag) {
if (!Platform.isDOMAvailable) {
return;
}
const wakeLock = await navigator.wakeLock.request('screen');
wakeLockMap[tag] = wakeLock;
},
async deactivate(tag) {
if (!Platform.isDOMAvailable) {
return;
}
if (wakeLockMap[tag]) {
wakeLockMap[tag].release?.();
delete wakeLockMap[tag];
}
else {
throw new CodedError('ERR_KEEP_AWAKE_TAG_INVALID', `The wake lock with tag ${tag} has not activated yet`);
}
},
addListenerForTag(tag, listener) {
const eventListener = () => {
listener({ state: KeepAwakeEventState.RELEASE });
};
const sentinel = wakeLockMap[tag];
if (sentinel) {
if ('addEventListener' in sentinel) {
sentinel.addEventListener?.('release', eventListener);
}
else {
sentinel.onrelease = eventListener;
}
}
return {
remove: () => {
const sentinel = wakeLockMap[tag];
if (sentinel) {
if (sentinel.removeEventListener) {
sentinel.removeEventListener('release', eventListener);
}
else {
sentinel.onrelease = null;
}
}
},
};
},
};
//# sourceMappingURL=ExpoKeepAwake.web.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ExpoKeepAwake.web.js","sourceRoot":"","sources":["../src/ExpoKeepAwake.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAgB,MAAM,mBAAmB,CAAC;AAEvE,OAAO,EAAE,mBAAmB,EAAqB,MAAM,mBAAmB,CAAC;AAE3E,MAAM,WAAW,GAAqC,EAAE,CAAC;AAkBzD,sGAAsG;AACtG,eAAe;IACb,KAAK,CAAC,gBAAgB;QACpB,IAAI,QAAQ,CAAC,cAAc,EAAE;YAC3B,OAAO,UAAU,IAAI,SAAS,CAAC;SAChC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,KAAK,CAAC,QAAQ,CAAC,GAAW;QACxB,IAAI,CAAC,QAAQ,CAAC,cAAc,EAAE;YAC5B,OAAO;SACR;QACD,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC5D,WAAW,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC;IAC9B,CAAC;IACD,KAAK,CAAC,UAAU,CAAC,GAAW;QAC1B,IAAI,CAAC,QAAQ,CAAC,cAAc,EAAE;YAC5B,OAAO;SACR;QACD,IAAI,WAAW,CAAC,GAAG,CAAC,EAAE;YACpB,WAAW,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;YAC7B,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC;SACzB;aAAM;YACL,MAAM,IAAI,UAAU,CAClB,4BAA4B,EAC5B,0BAA0B,GAAG,wBAAwB,CACtD,CAAC;SACH;IACH,CAAC;IACD,iBAAiB,CAAC,GAAW,EAAE,QAA2B;QACxD,MAAM,aAAa,GAAG,GAAG,EAAE;YACzB,QAAQ,CAAC,EAAE,KAAK,EAAE,mBAAmB,CAAC,OAAO,EAAE,CAAC,CAAC;QACnD,CAAC,CAAC;QACF,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,QAAQ,EAAE;YACZ,IAAI,kBAAkB,IAAI,QAAQ,EAAE;gBAClC,QAAQ,CAAC,gBAAgB,EAAE,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;aACvD;iBAAM;gBACL,QAAQ,CAAC,SAAS,GAAG,aAAa,CAAC;aACpC;SACF;QACD,OAAO;YACL,MAAM,EAAE,GAAG,EAAE;gBACX,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;gBAClC,IAAI,QAAQ,EAAE;oBACZ,IAAI,QAAQ,CAAC,mBAAmB,EAAE;wBAChC,QAAQ,CAAC,mBAAmB,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;qBACxD;yBAAM;wBACL,QAAQ,CAAC,SAAS,GAAG,IAAI,CAAC;qBAC3B;iBACF;YACH,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC","sourcesContent":["import { CodedError, Platform, Subscription } from 'expo-modules-core';\n\nimport { KeepAwakeEventState, KeepAwakeListener } from './KeepAwake.types';\n\nconst wakeLockMap: Record<string, WakeLockSentinel> = {};\n\ntype WakeLockSentinel = {\n onrelease: null | ((event: any) => void);\n released: boolean;\n type: 'screen';\n release?: Function;\n\n addEventListener?: (event: string, listener: (event: any) => void) => void;\n removeEventListener?: (event: string, listener: (event: any) => void) => void;\n};\n\ndeclare const navigator: {\n wakeLock: {\n request(type: 'screen'): Promise<WakeLockSentinel>;\n };\n};\n\n/** Wraps the webWakeLock API https://developer.mozilla.org/en-US/docs/Web/API/Screen_Wake_Lock_API */\nexport default {\n async isAvailableAsync() {\n if (Platform.isDOMAvailable) {\n return 'wakeLock' in navigator;\n }\n return false;\n },\n async activate(tag: string) {\n if (!Platform.isDOMAvailable) {\n return;\n }\n const wakeLock = await navigator.wakeLock.request('screen');\n wakeLockMap[tag] = wakeLock;\n },\n async deactivate(tag: string) {\n if (!Platform.isDOMAvailable) {\n return;\n }\n if (wakeLockMap[tag]) {\n wakeLockMap[tag].release?.();\n delete wakeLockMap[tag];\n } else {\n throw new CodedError(\n 'ERR_KEEP_AWAKE_TAG_INVALID',\n `The wake lock with tag ${tag} has not activated yet`\n );\n }\n },\n addListenerForTag(tag: string, listener: KeepAwakeListener): Subscription {\n const eventListener = () => {\n listener({ state: KeepAwakeEventState.RELEASE });\n };\n const sentinel = wakeLockMap[tag];\n if (sentinel) {\n if ('addEventListener' in sentinel) {\n sentinel.addEventListener?.('release', eventListener);\n } else {\n sentinel.onrelease = eventListener;\n }\n }\n return {\n remove: () => {\n const sentinel = wakeLockMap[tag];\n if (sentinel) {\n if (sentinel.removeEventListener) {\n sentinel.removeEventListener('release', eventListener);\n } else {\n sentinel.onrelease = null;\n }\n }\n },\n };\n },\n};\n"]}

View File

@@ -0,0 +1,21 @@
export type KeepAwakeEvent = {
/** Keep awake state. */
state: KeepAwakeEventState;
};
export declare enum KeepAwakeEventState {
RELEASE = "release"
}
export type KeepAwakeListener = (event: KeepAwakeEvent) => void;
export type KeepAwakeOptions = {
/**
* The call will throw an unhandled promise rejection on Android when the original Activity is dead or deactivated.
* Set the value to `true` for suppressing the uncaught exception.
*/
suppressDeactivateWarnings?: boolean;
/**
* A callback that is invoked when the keep-awake state changes.
* @platform web
*/
listener?: KeepAwakeListener;
};
//# sourceMappingURL=KeepAwake.types.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"KeepAwake.types.d.ts","sourceRoot":"","sources":["../src/KeepAwake.types.ts"],"names":[],"mappings":"AACA,MAAM,MAAM,cAAc,GAAG;IAC3B,wBAAwB;IACxB,KAAK,EAAE,mBAAmB,CAAC;CAC5B,CAAC;AAGF,oBAAY,mBAAmB;IAC7B,OAAO,YAAY;CACpB;AAGD,MAAM,MAAM,iBAAiB,GAAG,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;AAEhE,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;;OAGG;IACH,0BAA0B,CAAC,EAAE,OAAO,CAAC;IAErC;;;OAGG;IACH,QAAQ,CAAC,EAAE,iBAAiB,CAAC;CAC9B,CAAC"}

View File

@@ -0,0 +1,6 @@
// @needsAudit
export var KeepAwakeEventState;
(function (KeepAwakeEventState) {
KeepAwakeEventState["RELEASE"] = "release";
})(KeepAwakeEventState || (KeepAwakeEventState = {}));
//# sourceMappingURL=KeepAwake.types.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"KeepAwake.types.js","sourceRoot":"","sources":["../src/KeepAwake.types.ts"],"names":[],"mappings":"AAMA,cAAc;AACd,MAAM,CAAN,IAAY,mBAEX;AAFD,WAAY,mBAAmB;IAC7B,0CAAmB,CAAA;AACrB,CAAC,EAFW,mBAAmB,KAAnB,mBAAmB,QAE9B","sourcesContent":["// @needsAudit\nexport type KeepAwakeEvent = {\n /** Keep awake state. */\n state: KeepAwakeEventState;\n};\n\n// @needsAudit\nexport enum KeepAwakeEventState {\n RELEASE = 'release',\n}\n\n// @needsAudit\nexport type KeepAwakeListener = (event: KeepAwakeEvent) => void;\n\nexport type KeepAwakeOptions = {\n /**\n * The call will throw an unhandled promise rejection on Android when the original Activity is dead or deactivated.\n * Set the value to `true` for suppressing the uncaught exception.\n */\n suppressDeactivateWarnings?: boolean;\n\n /**\n * A callback that is invoked when the keep-awake state changes.\n * @platform web\n */\n listener?: KeepAwakeListener;\n};\n"]}

View File

@@ -0,0 +1,64 @@
import { Subscription } from 'expo-modules-core';
import { KeepAwakeListener, KeepAwakeOptions } from './KeepAwake.types';
/** Default tag, used when no tag has been specified in keep awake method calls. */
export declare const ExpoKeepAwakeTag = "ExpoKeepAwakeDefaultTag";
/** @returns `true` on all platforms except [unsupported web browsers](https://caniuse.com/wake-lock). */
export declare function isAvailableAsync(): Promise<boolean>;
/**
* A React hook to keep the screen awake for as long as the owner component is mounted.
* The optionally provided `tag` argument is used when activating and deactivating the keep-awake
* feature. If unspecified, an ID unique to the owner component is used. See the documentation for
* `activateKeepAwakeAsync` below to learn more about the `tag` argument.
*
* @param tag Tag to lock screen sleep prevention. If not provided, an ID unique to the owner component is used.
* @param options Additional options for the keep awake hook.
*/
export declare function useKeepAwake(tag?: string, options?: KeepAwakeOptions): void;
/**
* Prevents the screen from sleeping until `deactivateKeepAwake` is called with the same `tag` value.
*
* If the `tag` argument is specified, the screen will not sleep until you call `deactivateKeepAwake`
* with the same `tag` argument. When using multiple `tags` for activation you'll have to deactivate
* each one in order to re-enable screen sleep. If tag is unspecified, the default `tag` is used.
*
* Web support [is limited](https://caniuse.com/wake-lock).
*
* @param tag Tag to lock screen sleep prevention. If not provided, the default tag is used.
* @deprecated use `activateKeepAwakeAsync` instead.
*/
export declare function activateKeepAwake(tag?: string): Promise<void>;
/**
* Prevents the screen from sleeping until `deactivateKeepAwake` is called with the same `tag` value.
*
* If the `tag` argument is specified, the screen will not sleep until you call `deactivateKeepAwake`
* with the same `tag` argument. When using multiple `tags` for activation you'll have to deactivate
* each one in order to re-enable screen sleep. If tag is unspecified, the default `tag` is used.
*
* Web support [is limited](https://caniuse.com/wake-lock).
*
* @param tag Tag to lock screen sleep prevention. If not provided, the default tag is used.
*/
export declare function activateKeepAwakeAsync(tag?: string): Promise<void>;
/**
* Releases the lock on screen-sleep prevention associated with the given `tag` value. If `tag`
* is unspecified, it defaults to the same default tag that `activateKeepAwake` uses.
*
* @param tag Tag to release the lock on screen sleep prevention. If not provided,
* the default tag is used.
*/
export declare function deactivateKeepAwake(tag?: string): Promise<void>;
/**
* Observe changes to the keep awake timer.
* On web, this changes when navigating away from the active window/tab. No-op on native.
* @platform web
*
* @example
* ```ts
* KeepAwake.addListener(({ state }) => {
* // ...
* });
* ```
*/
export declare function addListener(tagOrListener: string | KeepAwakeListener, listener?: KeepAwakeListener): Subscription;
export * from './KeepAwake.types';
//# sourceMappingURL=index.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,mBAAmB,CAAC;AAItE,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAExE,mFAAmF;AACnF,eAAO,MAAM,gBAAgB,4BAA4B,CAAC;AAE1D,yGAAyG;AACzG,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC,CAKzD;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAoB3E;AAGD;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,MAAyB,GAAG,OAAO,CAAC,IAAI,CAAC,CAG/E;AAGD;;;;;;;;;;GAUG;AACH,wBAAsB,sBAAsB,CAAC,GAAG,GAAE,MAAyB,GAAG,OAAO,CAAC,IAAI,CAAC,CAE1F;AAGD;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CAAC,GAAG,GAAE,MAAyB,GAAG,OAAO,CAAC,IAAI,CAAC,CAEvF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CACzB,aAAa,EAAE,MAAM,GAAG,iBAAiB,EACzC,QAAQ,CAAC,EAAE,iBAAiB,GAC3B,YAAY,CAUd;AAED,cAAc,mBAAmB,CAAC"}

View File

@@ -0,0 +1,108 @@
import { UnavailabilityError } from 'expo-modules-core';
import { useEffect, useId } from 'react';
import ExpoKeepAwake from './ExpoKeepAwake';
/** Default tag, used when no tag has been specified in keep awake method calls. */
export const ExpoKeepAwakeTag = 'ExpoKeepAwakeDefaultTag';
/** @returns `true` on all platforms except [unsupported web browsers](https://caniuse.com/wake-lock). */
export async function isAvailableAsync() {
if (ExpoKeepAwake.isAvailableAsync) {
return await ExpoKeepAwake.isAvailableAsync();
}
return true;
}
/**
* A React hook to keep the screen awake for as long as the owner component is mounted.
* The optionally provided `tag` argument is used when activating and deactivating the keep-awake
* feature. If unspecified, an ID unique to the owner component is used. See the documentation for
* `activateKeepAwakeAsync` below to learn more about the `tag` argument.
*
* @param tag Tag to lock screen sleep prevention. If not provided, an ID unique to the owner component is used.
* @param options Additional options for the keep awake hook.
*/
export function useKeepAwake(tag, options) {
const defaultTag = useId();
const tagOrDefault = tag ?? defaultTag;
useEffect(() => {
let isMounted = true;
activateKeepAwakeAsync(tagOrDefault).then(() => {
if (isMounted && ExpoKeepAwake.addListenerForTag && options?.listener) {
addListener(tagOrDefault, options.listener);
}
});
return () => {
isMounted = false;
if (options?.suppressDeactivateWarnings) {
deactivateKeepAwake(tagOrDefault).catch(() => { });
}
else {
deactivateKeepAwake(tagOrDefault);
}
};
}, [tagOrDefault]);
}
// @needsAudit
/**
* Prevents the screen from sleeping until `deactivateKeepAwake` is called with the same `tag` value.
*
* If the `tag` argument is specified, the screen will not sleep until you call `deactivateKeepAwake`
* with the same `tag` argument. When using multiple `tags` for activation you'll have to deactivate
* each one in order to re-enable screen sleep. If tag is unspecified, the default `tag` is used.
*
* Web support [is limited](https://caniuse.com/wake-lock).
*
* @param tag Tag to lock screen sleep prevention. If not provided, the default tag is used.
* @deprecated use `activateKeepAwakeAsync` instead.
*/
export function activateKeepAwake(tag = ExpoKeepAwakeTag) {
console.warn('`activateKeepAwake` is deprecated. Use `activateKeepAwakeAsync` instead.');
return activateKeepAwakeAsync(tag);
}
// @needsAudit
/**
* Prevents the screen from sleeping until `deactivateKeepAwake` is called with the same `tag` value.
*
* If the `tag` argument is specified, the screen will not sleep until you call `deactivateKeepAwake`
* with the same `tag` argument. When using multiple `tags` for activation you'll have to deactivate
* each one in order to re-enable screen sleep. If tag is unspecified, the default `tag` is used.
*
* Web support [is limited](https://caniuse.com/wake-lock).
*
* @param tag Tag to lock screen sleep prevention. If not provided, the default tag is used.
*/
export async function activateKeepAwakeAsync(tag = ExpoKeepAwakeTag) {
await ExpoKeepAwake.activate?.(tag);
}
// @needsAudit
/**
* Releases the lock on screen-sleep prevention associated with the given `tag` value. If `tag`
* is unspecified, it defaults to the same default tag that `activateKeepAwake` uses.
*
* @param tag Tag to release the lock on screen sleep prevention. If not provided,
* the default tag is used.
*/
export async function deactivateKeepAwake(tag = ExpoKeepAwakeTag) {
await ExpoKeepAwake.deactivate?.(tag);
}
/**
* Observe changes to the keep awake timer.
* On web, this changes when navigating away from the active window/tab. No-op on native.
* @platform web
*
* @example
* ```ts
* KeepAwake.addListener(({ state }) => {
* // ...
* });
* ```
*/
export function addListener(tagOrListener, listener) {
// Assert so the type is non-nullable.
if (!ExpoKeepAwake.addListenerForTag) {
throw new UnavailabilityError('ExpoKeepAwake', 'addListenerForTag');
}
const tag = typeof tagOrListener === 'string' ? tagOrListener : ExpoKeepAwakeTag;
const _listener = typeof tagOrListener === 'function' ? tagOrListener : listener;
return ExpoKeepAwake.addListenerForTag(tag, _listener);
}
export * from './KeepAwake.types';
//# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long