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,114 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = attachKeyHandlers;
var _KeyPressHandler = require("../../utils/KeyPressHandler");
var _cliTools = require("@react-native-community/cli-tools");
var _chalk = _interopRequireDefault(require("chalk"));
var _execa = _interopRequireDefault(require("execa"));
var _nodeFetch = _interopRequireDefault(require("node-fetch"));
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj };
}
/**
* 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.
*
*
* @format
* @oncall react_native
*/
const CTRL_C = "\u0003";
const CTRL_D = "\u0004";
function attachKeyHandlers({
cliConfig,
devServerUrl,
messageSocket,
experimentalDebuggerFrontend,
}) {
if (process.stdin.isTTY !== true) {
_cliTools.logger.debug(
"Interactive mode is not supported in this environment"
);
return;
}
const execaOptions = {
env: {
FORCE_COLOR: _chalk.default.supportsColor ? "true" : "false",
},
};
const keyPressHandler = new _KeyPressHandler.KeyPressHandler(async (key) => {
switch (key) {
case "r":
_cliTools.logger.info("Reloading connected app(s)...");
messageSocket.broadcast("reload", null);
break;
case "d":
_cliTools.logger.info("Opening Dev Menu...");
messageSocket.broadcast("devMenu", null);
break;
case "i":
_cliTools.logger.info("Opening app on iOS...");
(0, _execa.default)(
"npx",
[
"react-native",
"run-ios",
...(cliConfig.project.ios?.watchModeCommandParams ?? []),
],
execaOptions
).stdout?.pipe(process.stdout);
break;
case "a":
_cliTools.logger.info("Opening app on Android...");
(0, _execa.default)(
"npx",
[
"react-native",
"run-android",
...(cliConfig.project.android?.watchModeCommandParams ?? []),
],
execaOptions
).stdout?.pipe(process.stdout);
break;
case "j":
if (!experimentalDebuggerFrontend) {
return;
}
await (0, _nodeFetch.default)(devServerUrl + "/open-debugger", {
method: "POST",
});
break;
case CTRL_C:
case CTRL_D:
_cliTools.logger.info("Stopping server");
keyPressHandler.stopInterceptingKeyStrokes();
process.emit("SIGINT");
process.exit();
}
});
keyPressHandler.createInteractionListener();
keyPressHandler.startInterceptingKeyStrokes();
_cliTools.logger.log(
[
"",
`${_chalk.default.bold("i")} - run on iOS`,
`${_chalk.default.bold("a")} - run on Android`,
`${_chalk.default.bold("d")} - open Dev Menu`,
...(experimentalDebuggerFrontend
? [
`${_chalk.default.bold(
"j"
)} - open debugger (experimental, Hermes only)`,
]
: []),
`${_chalk.default.bold("r")} - reload app`,
"",
].join("\n")
);
}

View File

@@ -0,0 +1,22 @@
/**
* 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
* @oncall react_native
*/
import type { Config } from "@react-native-community/cli-types";
declare export default function attachKeyHandlers({
cliConfig: Config,
devServerUrl: string,
messageSocket: $ReadOnly<{
broadcast: (type: string, params?: Record<string, mixed> | null) => void,
...
}>,
experimentalDebuggerFrontend: boolean,
}): void;

View File

@@ -0,0 +1,112 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = void 0;
var _runServer = _interopRequireDefault(require("./runServer"));
var _path = _interopRequireDefault(require("path"));
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj };
}
/**
* 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.
*
*
* @format
* @oncall react_native
*/
const startCommand = {
name: "start",
func: _runServer.default,
description: "Start the React Native development server.",
options: [
{
name: "--port <number>",
parse: Number,
},
{
name: "--host <string>",
default: "",
},
{
name: "--projectRoot <path>",
description: "Path to a custom project root",
parse: (val) => _path.default.resolve(val),
},
{
name: "--watchFolders <list>",
description:
"Specify any additional folders to be added to the watch list",
parse: (val) =>
val.split(",").map((folder) => _path.default.resolve(folder)),
},
{
name: "--assetPlugins <list>",
description:
"Specify any additional asset plugins to be used by the packager by full filepath",
parse: (val) => val.split(","),
},
{
name: "--sourceExts <list>",
description:
"Specify any additional source extensions to be used by the packager",
parse: (val) => val.split(","),
},
{
name: "--max-workers <number>",
description:
"Specifies the maximum number of workers the worker-pool " +
"will spawn for transforming files. This defaults to the number of the " +
"cores available on your machine.",
parse: (workers) => Number(workers),
},
{
name: "--transformer <string>",
description: "Specify a custom transformer to be used",
},
{
name: "--reset-cache, --resetCache",
description: "Removes cached files",
},
{
name: "--custom-log-reporter-path, --customLogReporterPath <string>",
description:
"Path to a JavaScript file that exports a log reporter as a replacement for TerminalReporter",
},
{
name: "--https",
description: "Enables https connections to the server",
},
{
name: "--key <path>",
description: "Path to custom SSL key",
},
{
name: "--cert <path>",
description: "Path to custom SSL cert",
},
{
name: "--config <string>",
description: "Path to the CLI configuration file",
parse: (val) => _path.default.resolve(val),
},
{
name: "--no-interactive",
description: "Disables interactive mode",
},
{
name: "--experimental-debugger",
description:
"[Experimental] Enable the new debugger experience and 'j' to " +
"debug. This enables the new frontend experience only: connection " +
"reliability and some basic features are unstable in this release.",
},
],
};
var _default = startCommand;
exports.default = _default;

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
* @oncall react_native
*/
import type { Command } from "@react-native-community/cli-types";
export type { StartCommandArgs } from "./runServer";
declare const startCommand: Command;
declare export default typeof startCommand;

View File

@@ -0,0 +1,176 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = void 0;
var _isDevServerRunning = _interopRequireDefault(
require("../../utils/isDevServerRunning")
);
var _loadMetroConfig = _interopRequireDefault(
require("../../utils/loadMetroConfig")
);
var _attachKeyHandlers = _interopRequireDefault(require("./attachKeyHandlers"));
var _cliServerApi = require("@react-native-community/cli-server-api");
var _cliTools = require("@react-native-community/cli-tools");
var _devMiddleware = require("@react-native/dev-middleware");
var _chalk = _interopRequireDefault(require("chalk"));
var _metro = _interopRequireDefault(require("metro"));
var _metroCore = require("metro-core");
var _path = _interopRequireDefault(require("path"));
var _url = _interopRequireDefault(require("url"));
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj };
}
/**
* 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.
*
*
* @format
* @oncall react_native
*/
async function runServer(_argv, ctx, args) {
const metroConfig = await (0, _loadMetroConfig.default)(ctx, {
config: args.config,
maxWorkers: args.maxWorkers,
port: args.port ?? 8081,
resetCache: args.resetCache,
watchFolders: args.watchFolders,
projectRoot: args.projectRoot,
sourceExts: args.sourceExts,
});
const hostname = args.host?.length ? args.host : "localhost";
const {
projectRoot,
server: { port },
watchFolders,
} = metroConfig;
const protocol = args.https === true ? "https" : "http";
const devServerUrl = _url.default.format({
protocol,
hostname,
port,
});
_cliTools.logger.info(`Welcome to React Native v${ctx.reactNativeVersion}`);
const serverStatus = await (0, _isDevServerRunning.default)(
devServerUrl,
projectRoot
);
if (serverStatus === "matched_server_running") {
_cliTools.logger.info(
`A dev server is already running for this project on port ${port}. Exiting.`
);
return;
} else if (serverStatus === "port_taken") {
_cliTools.logger.error(
`Another process is running on port ${port}. Please terminate this ` +
'process and try again, or use another port with "--port".'
);
return;
}
_cliTools.logger.info(
`Starting dev server on port ${_chalk.default.bold(String(port))}...`
);
if (args.assetPlugins) {
// $FlowIgnore[cannot-write] Assigning to readonly property
metroConfig.transformer.assetPlugins = args.assetPlugins.map((plugin) =>
require.resolve(plugin)
);
}
const {
middleware: communityMiddleware,
websocketEndpoints: communityWebsocketEndpoints,
messageSocketEndpoint,
eventsSocketEndpoint,
} = (0, _cliServerApi.createDevServerMiddleware)({
host: hostname,
port,
watchFolders,
});
const { middleware, websocketEndpoints } = (0,
_devMiddleware.createDevMiddleware)({
projectRoot,
serverBaseUrl: devServerUrl,
logger: _cliTools.logger,
unstable_experiments: {
// NOTE: Only affects the /open-debugger endpoint
enableNewDebugger: args.experimentalDebugger,
},
});
let reportEvent;
const terminal = new _metroCore.Terminal(process.stdout);
const ReporterImpl = getReporterImpl(args.customLogReporterPath);
const terminalReporter = new ReporterImpl(terminal);
// $FlowIgnore[cannot-write] Assigning to readonly property
metroConfig.reporter = {
update(event) {
terminalReporter.update(event);
if (reportEvent) {
reportEvent(event);
}
if (args.interactive && event.type === "initialize_done") {
_cliTools.logger.info("Dev server ready");
(0, _attachKeyHandlers.default)({
cliConfig: ctx,
devServerUrl,
messageSocket: messageSocketEndpoint,
experimentalDebuggerFrontend: args.experimentalDebugger,
});
}
},
};
const serverInstance = await _metro.default.runServer(metroConfig, {
host: args.host,
secure: args.https,
secureCert: args.cert,
secureKey: args.key,
unstable_extraMiddleware: [
communityMiddleware,
_cliServerApi.indexPageMiddleware,
middleware,
],
websocketEndpoints: {
...communityWebsocketEndpoints,
...websocketEndpoints,
},
});
reportEvent = eventsSocketEndpoint.reportEvent;
// In Node 8, the default keep-alive for an HTTP connection is 5 seconds. In
// early versions of Node 8, this was implemented in a buggy way which caused
// some HTTP responses (like those containing large JS bundles) to be
// terminated early.
//
// As a workaround, arbitrarily increase the keep-alive from 5 to 30 seconds,
// which should be enough to send even the largest of JS bundles.
//
// For more info: https://github.com/nodejs/node/issues/13391
//
serverInstance.keepAliveTimeout = 30000;
await _cliTools.version.logIfUpdateAvailable(ctx.root);
}
function getReporterImpl(customLogReporterPath) {
if (customLogReporterPath == null) {
return require("metro/src/lib/TerminalReporter");
}
try {
// First we let require resolve it, so we can require packages in node_modules
// as expected. eg: require('my-package/reporter');
// $FlowIgnore[unsupported-syntax]
return require(customLogReporterPath);
} catch (e) {
if (e.code !== "MODULE_NOT_FOUND") {
throw e;
}
// If that doesn't work, then we next try relative to the cwd, eg:
// require('./reporter');
// $FlowIgnore[unsupported-syntax]
return require(_path.default.resolve(customLogReporterPath));
}
}
var _default = runServer;
exports.default = _default;

View File

@@ -0,0 +1,39 @@
/**
* 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
* @oncall react_native
*/
import type { Config } from "@react-native-community/cli-types";
export type StartCommandArgs = {
assetPlugins?: string[],
cert?: string,
customLogReporterPath?: string,
experimentalDebugger: boolean,
host?: string,
https?: boolean,
maxWorkers?: number,
key?: string,
platforms?: string[],
port?: number,
resetCache?: boolean,
sourceExts?: string[],
transformer?: string,
watchFolders?: string[],
config?: string,
projectRoot?: string,
interactive: boolean,
};
declare function runServer(
_argv: Array<string>,
ctx: Config,
args: StartCommandArgs
): void;
declare export default typeof runServer;