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,221 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
'use strict';
const Collection = require('../Collection');
const NodeCollection = require('./Node');
const assert = require('assert');
const once = require('../utils/once');
const recast = require('recast');
const requiresModule = require('./VariableDeclarator').filters.requiresModule;
const types = recast.types.namedTypes;
const JSXElement = types.JSXElement;
const JSXAttribute = types.JSXAttribute;
const Literal = types.Literal;
/**
* Contains filter methods and mutation methods for processing JSXElements.
* @mixin
*/
const globalMethods = {
/**
* Finds all JSXElements optionally filtered by name
*
* @param {string} name
* @return {Collection}
*/
findJSXElements: function(name) {
const nameFilter = name && {openingElement: {name: {name: name}}};
return this.find(JSXElement, nameFilter);
},
/**
* Finds all JSXElements by module name. Given
*
* var Bar = require('Foo');
* <Bar />
*
* findJSXElementsByModuleName('Foo') will find <Bar />, without having to
* know the variable name.
*/
findJSXElementsByModuleName: function(moduleName) {
assert.ok(
moduleName && typeof moduleName === 'string',
'findJSXElementsByModuleName(...) needs a name to look for'
);
return this.find(types.VariableDeclarator)
.filter(requiresModule(moduleName))
.map(function(path) {
const id = path.value.id.name;
if (id) {
return Collection.fromPaths([path])
.closestScope()
.findJSXElements(id)
.paths();
}
});
}
};
const filterMethods = {
/**
* Filter method for attributes.
*
* @param {Object} attributeFilter
* @return {function}
*/
hasAttributes: function(attributeFilter) {
const attributeNames = Object.keys(attributeFilter);
return function filter(path) {
if (!JSXElement.check(path.value)) {
return false;
}
const elementAttributes = Object.create(null);
path.value.openingElement.attributes.forEach(function(attr) {
if (!JSXAttribute.check(attr) ||
!(attr.name.name in attributeFilter)) {
return;
}
elementAttributes[attr.name.name] = attr;
});
return attributeNames.every(function(name) {
if (!(name in elementAttributes) ){
return false;
}
const value = elementAttributes[name].value;
const expected = attributeFilter[name];
// Only when value is truthy access it's properties
const actual = !value
? value
: Literal.check(value)
? value.value
: value.expression;
if (typeof expected === 'function') {
return expected(actual);
}
// Literal attribute values are always strings
return String(expected) === actual;
});
};
},
/**
* Filter elements which contain a specific child type
*
* @param {string} name
* @return {function}
*/
hasChildren: function(name) {
return function filter(path) {
return JSXElement.check(path.value) &&
path.value.children.some(
child => JSXElement.check(child) &&
child.openingElement.name.name === name
);
};
}
};
/**
* @mixin
*/
const traversalMethods = {
/**
* Returns all child nodes, including literals and expressions.
*
* @return {Collection}
*/
childNodes: function() {
const paths = [];
this.forEach(function(path) {
const children = path.get('children');
const l = children.value.length;
for (let i = 0; i < l; i++) {
paths.push(children.get(i));
}
});
return Collection.fromPaths(paths, this);
},
/**
* Returns all children that are JSXElements.
*
* @return {JSXElementCollection}
*/
childElements: function() {
const paths = [];
this.forEach(function(path) {
const children = path.get('children');
const l = children.value.length;
for (let i = 0; i < l; i++) {
if (types.JSXElement.check(children.value[i])) {
paths.push(children.get(i));
}
}
});
return Collection.fromPaths(paths, this, JSXElement);
},
/**
* Returns all children that are of jsxElementType.
*
* @return {Collection<jsxElementType>}
*/
childNodesOfType: function(jsxChildElementType) {
const paths = [];
this.forEach(function(path) {
const children = path.get('children');
const l = children.value.length;
for (let i = 0; i < l; i++) {
if (jsxChildElementType.check(children.value[i])) {
paths.push(children.get(i));
}
}
});
return Collection.fromPaths(paths, this, jsxChildElementType);
},
};
const mappingMethods = {
/**
* Given a JSXElement, returns its "root" name. E.g. it would return "Foo" for
* both <Foo /> and <Foo.Bar />.
*
* @param {NodePath} path
* @return {string}
*/
getRootName: function(path) {
let name = path.value.openingElement.name;
while (types.JSXMemberExpression.check(name)) {
name = name.object;
}
return name && name.name || null;
}
};
function register() {
NodeCollection.register();
Collection.registerMethods(globalMethods, types.Node);
Collection.registerMethods(traversalMethods, JSXElement);
}
exports.register = once(register);
exports.filters = filterMethods;
exports.mappings = mappingMethods;

View File

@@ -0,0 +1,187 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
'use strict';
const Collection = require('../Collection');
const matchNode = require('../matchNode');
const once = require('../utils/once');
const recast = require('recast');
const Node = recast.types.namedTypes.Node;
var types = recast.types.namedTypes;
/**
* @mixin
*/
const traversalMethods = {
/**
* Find nodes of a specific type within the nodes of this collection.
*
* @param {type}
* @param {filter}
* @return {Collection}
*/
find: function(type, filter) {
const paths = [];
const visitorMethodName = 'visit' + type;
const visitor = {};
function visit(path) {
/*jshint validthis:true */
if (!filter || matchNode(path.value, filter)) {
paths.push(path);
}
this.traverse(path);
}
this.__paths.forEach(function(p, i) {
const self = this;
visitor[visitorMethodName] = function(path) {
if (self.__paths[i] === path) {
this.traverse(path);
} else {
return visit.call(this, path);
}
};
recast.visit(p, visitor);
}, this);
return Collection.fromPaths(paths, this, type);
},
/**
* Returns a collection containing the paths that create the scope of the
* currently selected paths. Dedupes the paths.
*
* @return {Collection}
*/
closestScope: function() {
return this.map(path => path.scope && path.scope.path);
},
/**
* Traverse the AST up and finds the closest node of the provided type.
*
* @param {Collection}
* @param {filter}
* @return {Collection}
*/
closest: function(type, filter) {
return this.map(function(path) {
let parent = path.parent;
while (
parent &&
!(
type.check(parent.value) &&
(!filter || matchNode(parent.value, filter))
)
) {
parent = parent.parent;
}
return parent || null;
});
},
/**
* Finds the declaration for each selected path. Useful for member expressions
* or JSXElements. Expects a callback function that maps each path to the name
* to look for.
*
* If the callback returns a falsey value, the element is skipped.
*
* @param {function} nameGetter
*
* @return {Collection}
*/
getVariableDeclarators: function(nameGetter) {
return this.map(function(path) {
/*jshint curly:false*/
let scope = path.scope;
if (!scope) return;
const name = nameGetter.apply(path, arguments);
if (!name) return;
scope = scope.lookup(name);
if (!scope) return;
const bindings = scope.getBindings()[name];
if (!bindings) return;
const decl = Collection.fromPaths(bindings)
.closest(types.VariableDeclarator);
if (decl.length === 1) {
return decl.paths()[0];
}
}, types.VariableDeclarator);
},
};
function toArray(value) {
return Array.isArray(value) ? value : [value];
}
/**
* @mixin
*/
const mutationMethods = {
/**
* Simply replaces the selected nodes with the provided node. If a function
* is provided it is executed for every node and the node is replaced with the
* functions return value.
*
* @param {Node|Array<Node>|function} nodes
* @return {Collection}
*/
replaceWith: function(nodes) {
return this.forEach(function(path, i) {
const newNodes =
(typeof nodes === 'function') ? nodes.call(path, path, i) : nodes;
path.replace.apply(path, toArray(newNodes));
});
},
/**
* Inserts a new node before the current one.
*
* @param {Node|Array<Node>|function} insert
* @return {Collection}
*/
insertBefore: function(insert) {
return this.forEach(function(path, i) {
const newNodes =
(typeof insert === 'function') ? insert.call(path, path, i) : insert;
path.insertBefore.apply(path, toArray(newNodes));
});
},
/**
* Inserts a new node after the current one.
*
* @param {Node|Array<Node>|function} insert
* @return {Collection}
*/
insertAfter: function(insert) {
return this.forEach(function(path, i) {
const newNodes =
(typeof insert === 'function') ? insert.call(path, path, i) : insert;
path.insertAfter.apply(path, toArray(newNodes));
});
},
remove: function() {
return this.forEach(path => path.prune());
}
};
function register() {
Collection.registerMethods(traversalMethods, Node);
Collection.registerMethods(mutationMethods, Node);
Collection.setDefaultCollectionType(Node);
}
exports.register = once(register);

View File

@@ -0,0 +1,201 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
'use strict';
const Collection = require('../Collection');
const NodeCollection = require('./Node');
const once = require('../utils/once');
const recast = require('recast');
const astNodesAreEquivalent = recast.types.astNodesAreEquivalent;
const b = recast.types.builders;
var types = recast.types.namedTypes;
const VariableDeclarator = recast.types.namedTypes.VariableDeclarator;
/**
* @mixin
*/
const globalMethods = {
/**
* Finds all variable declarators, optionally filtered by name.
*
* @param {string} name
* @return {Collection}
*/
findVariableDeclarators: function(name) {
const filter = name ? {id: {name: name}} : null;
return this.find(VariableDeclarator, filter);
}
};
const filterMethods = {
/**
* Returns a function that returns true if the provided path is a variable
* declarator and requires one of the specified module names.
*
* @param {string|Array} names A module name or an array of module names
* @return {Function}
*/
requiresModule: function(names) {
if (names && !Array.isArray(names)) {
names = [names];
}
const requireIdentifier = b.identifier('require');
return function(path) {
const node = path.value;
if (!VariableDeclarator.check(node) ||
!types.CallExpression.check(node.init) ||
!astNodesAreEquivalent(node.init.callee, requireIdentifier)) {
return false;
}
return !names ||
names.some(
n => astNodesAreEquivalent(node.init.arguments[0], b.literal(n))
);
};
}
};
/**
* @mixin
*/
const transformMethods = {
/**
* Renames a variable and all its occurrences.
*
* @param {string} newName
* @return {Collection}
*/
renameTo: function(newName) {
// TODO: Include JSXElements
return this.forEach(function(path) {
const node = path.value;
const oldName = node.id.name;
const rootScope = path.scope;
const rootPath = rootScope.path;
Collection.fromPaths([rootPath])
.find(types.Identifier, {name: oldName})
.filter(function(path) { // ignore non-variables
const parent = path.parent.node;
if (
types.MemberExpression.check(parent) &&
parent.property === path.node &&
!parent.computed
) {
// obj.oldName
return false;
}
if (
types.Property.check(parent) &&
parent.key === path.node &&
!parent.computed
) {
// { oldName: 3 }
return false;
}
if (
types.ObjectProperty.check(parent) &&
parent.key === path.node &&
!parent.computed
) {
// { oldName: 3 }
return false;
}
if (
types.ObjectMethod.check(parent) &&
parent.key === path.node &&
!parent.computed
) {
// { oldName() {} }
return false;
}
if (
types.MethodDefinition.check(parent) &&
parent.key === path.node &&
!parent.computed
) {
// class A { oldName() {} }
return false;
}
if (
types.ClassMethod.check(parent) &&
parent.key === path.node &&
!parent.computed
) {
// class A { oldName() {} }
return false;
}
if (
types.ClassProperty.check(parent) &&
parent.key === path.node &&
!parent.computed
) {
// class A { oldName = 3 }
return false;
}
if (
types.JSXAttribute.check(parent) &&
parent.name === path.node &&
!parent.computed
) {
// <Foo oldName={oldName} />
return false;
}
return true;
})
.forEach(function(path) {
let scope = path.scope;
while (scope && scope !== rootScope) {
if (scope.declares(oldName)) {
return;
}
scope = scope.parent;
}
if (scope) { // identifier must refer to declared variable
// It may look like we filtered out properties,
// but the filter only ignored property "keys", not "value"s
// In shorthand properties, "key" and "value" both have an
// Identifier with the same structure.
const parent = path.parent.node;
if (
types.Property.check(parent) &&
parent.shorthand &&
!parent.method
) {
path.parent.get('shorthand').replace(false);
}
path.get('name').replace(newName);
}
});
});
}
};
function register() {
NodeCollection.register();
Collection.registerMethods(globalMethods);
Collection.registerMethods(transformMethods, VariableDeclarator);
}
exports.register = once(register);
exports.filters = filterMethods;

View File

@@ -0,0 +1,13 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
module.exports = {
Node: require('./Node'),
JSXElement: require('./JSXElement'),
VariableDeclarator: require('./VariableDeclarator'),
};