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,211 @@
"use strict";
const getDefaultConfig = require("./defaults");
const validConfig = require("./defaults/validConfig");
const cosmiconfig = require("cosmiconfig");
const fs = require("fs");
const { validate } = require("jest-validate");
const MetroCache = require("metro-cache");
const path = require("path");
const { dirname, join } = require("path");
function overrideArgument(arg) {
if (arg == null) {
return arg;
}
if (Array.isArray(arg)) {
return arg[arg.length - 1];
}
return arg;
}
const explorer = cosmiconfig("metro", {
searchPlaces: [
"metro.config.js",
"metro.config.cjs",
"metro.config.json",
"package.json",
],
loaders: {
".json": cosmiconfig.loadJson,
".yaml": cosmiconfig.loadYaml,
".yml": cosmiconfig.loadYaml,
".js": cosmiconfig.loadJs,
".cjs": cosmiconfig.loadJs,
".es6": cosmiconfig.loadJs,
noExt: cosmiconfig.loadYaml,
},
});
const isFile = (filePath) =>
fs.existsSync(filePath) && !fs.lstatSync(filePath).isDirectory();
const resolve = (filePath) => {
try {
return require.resolve(filePath);
} catch (error) {
if (path.isAbsolute(filePath) || error.code !== "MODULE_NOT_FOUND") {
throw error;
}
}
const possiblePath = path.resolve(process.cwd(), filePath);
return isFile(possiblePath) ? possiblePath : filePath;
};
async function resolveConfig(filePath, cwd) {
if (filePath) {
return explorer.load(resolve(filePath));
}
const result = await explorer.search(cwd);
if (result == null) {
return {
isEmpty: true,
filepath: join(cwd || process.cwd(), "metro.config.stub.js"),
config: {},
};
}
return result;
}
function mergeConfig(defaultConfig, ...configs) {
return configs.reduce(
(totalConfig, nextConfig) => ({
...totalConfig,
...nextConfig,
cacheStores:
nextConfig.cacheStores != null
? typeof nextConfig.cacheStores === "function"
? nextConfig.cacheStores(MetroCache)
: nextConfig.cacheStores
: totalConfig.cacheStores,
resolver: {
...totalConfig.resolver,
...(nextConfig.resolver || {}),
dependencyExtractor:
nextConfig.resolver && nextConfig.resolver.dependencyExtractor != null
? resolve(nextConfig.resolver.dependencyExtractor)
: totalConfig.resolver.dependencyExtractor,
hasteImplModulePath:
nextConfig.resolver && nextConfig.resolver.hasteImplModulePath != null
? resolve(nextConfig.resolver.hasteImplModulePath)
: totalConfig.resolver.hasteImplModulePath,
},
serializer: {
...totalConfig.serializer,
...(nextConfig.serializer || {}),
},
transformer: {
...totalConfig.transformer,
...(nextConfig.transformer || {}),
babelTransformerPath:
nextConfig.transformer &&
nextConfig.transformer.babelTransformerPath != null
? resolve(nextConfig.transformer.babelTransformerPath)
: totalConfig.transformer.babelTransformerPath,
},
server: {
...totalConfig.server,
...(nextConfig.server || {}),
},
symbolicator: {
...totalConfig.symbolicator,
...(nextConfig.symbolicator || {}),
},
watcher: {
...totalConfig.watcher,
...nextConfig.watcher,
watchman: {
...totalConfig.watcher?.watchman,
...nextConfig.watcher?.watchman,
},
healthCheck: {
...totalConfig.watcher?.healthCheck,
...nextConfig.watcher?.healthCheck,
},
},
}),
defaultConfig
);
}
async function loadMetroConfigFromDisk(path, cwd, defaultConfigOverrides) {
const resolvedConfigResults = await resolveConfig(path, cwd);
const { config: configModule, filepath } = resolvedConfigResults;
const rootPath = dirname(filepath);
const defaults = await getDefaultConfig(rootPath);
const defaultConfig = mergeConfig(defaults, defaultConfigOverrides);
if (typeof configModule === "function") {
const resultedConfig = await configModule(defaultConfig);
return mergeConfig(defaultConfig, resultedConfig);
}
return mergeConfig(defaultConfig, configModule);
}
function overrideConfigWithArguments(config, argv) {
const output = {
resolver: {},
serializer: {},
server: {},
transformer: {},
};
if (argv.port != null) {
output.server.port = Number(argv.port);
}
if (argv.projectRoot != null) {
output.projectRoot = argv.projectRoot;
}
if (argv.watchFolders != null) {
output.watchFolders = argv.watchFolders;
}
if (argv.assetExts != null) {
output.resolver.assetExts = argv.assetExts;
}
if (argv.sourceExts != null) {
output.resolver.sourceExts = argv.sourceExts;
}
if (argv.platforms != null) {
output.resolver.platforms = argv.platforms;
}
if (argv["max-workers"] != null || argv.maxWorkers != null) {
output.maxWorkers = Number(argv["max-workers"] || argv.maxWorkers);
}
if (argv.transformer != null) {
output.transformer.babelTransformerPath = argv.transformer;
}
if (argv["reset-cache"] != null) {
output.resetCache = argv["reset-cache"];
}
if (argv.resetCache != null) {
output.resetCache = argv.resetCache;
}
if (argv.verbose === false) {
output.reporter = {
update: () => {},
};
}
return mergeConfig(config, output);
}
async function loadConfig(argvInput = {}, defaultConfigOverrides = {}) {
const argv = {
...argvInput,
config: overrideArgument(argvInput.config),
};
const configuration = await loadMetroConfigFromDisk(
argv.config,
argv.cwd,
defaultConfigOverrides
);
validate(configuration, {
exampleConfig: await validConfig(),
recursiveDenylist: ["reporter", "resolver", "transformer"],
deprecatedConfig: {
blacklistRE:
() => `Warning: Metro config option \`blacklistRE\` is deprecated.
Please use \`blockList\` instead.`,
},
});
const configWithArgs = overrideConfigWithArguments(configuration, argv);
const overriddenConfig = {};
overriddenConfig.watchFolders = [
configWithArgs.projectRoot,
...configWithArgs.watchFolders,
];
return mergeConfig(configWithArgs, overriddenConfig);
}
module.exports = {
loadConfig,
resolveConfig,
mergeConfig,
};