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,10 @@
import { IFilter, Entry } from '../types';
export default class IosFilter implements IFilter {
private readonly minPriority;
private filter;
constructor(minPriority?: number);
setFilterByTag(tags: string[]): void;
setFilterByMatch(regexes: RegExp[]): void;
shouldInclude(entry: Entry): boolean;
}
//# sourceMappingURL=IosFilter.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"IosFilter.d.ts","sourceRoot":"","sources":["../../src/ios/IosFilter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAI1C,MAAM,CAAC,OAAO,OAAO,SAAU,YAAW,OAAO;IAC/C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,MAAM,CAAS;gBAEX,WAAW,GAAE,MAAU;IAQnC,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE;IAU7B,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE;IAalC,aAAa,CAAC,KAAK,EAAE,KAAK;CAG3B"}

View File

@@ -0,0 +1,42 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
class IosFilter {
constructor(minPriority = 0) {
_defineProperty(this, "minPriority", void 0);
_defineProperty(this, "filter", void 0);
this.minPriority = minPriority; // Default filter by all
this.filter = entry => {
return entry.priority >= this.minPriority;
};
}
setFilterByTag(tags) {
this.filter = entry => {
return Boolean(entry.priority >= this.minPriority && entry.tag && tags.indexOf(entry.tag) > -1);
};
}
setFilterByMatch(regexes) {
this.filter = entry => {
return entry.priority >= this.minPriority && Boolean(regexes.find(reg => Boolean(entry.messages.find(message => reg.test(message)))));
};
}
shouldInclude(entry) {
return this.filter(entry);
}
}
exports.default = IosFilter;
//# sourceMappingURL=IosFilter.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../src/ios/IosFilter.ts"],"names":["IosFilter","constructor","minPriority","filter","entry","priority","setFilterByTag","tags","Boolean","tag","indexOf","setFilterByMatch","regexes","find","reg","messages","message","test","shouldInclude"],"mappings":";;;;;;;;;AAIe,MAAMA,SAAN,CAAmC;AAIhDC,EAAAA,WAAW,CAACC,WAAmB,GAAG,CAAvB,EAA0B;AAAA;;AAAA;;AACnC,SAAKA,WAAL,GAAmBA,WAAnB,CADmC,CAEnC;;AACA,SAAKC,MAAL,GAAeC,KAAD,IAAkB;AAC9B,aAAOA,KAAK,CAACC,QAAN,IAAkB,KAAKH,WAA9B;AACD,KAFD;AAGD;;AAEDI,EAAAA,cAAc,CAACC,IAAD,EAAiB;AAC7B,SAAKJ,MAAL,GAAeC,KAAD,IAAkB;AAC9B,aAAOI,OAAO,CACZJ,KAAK,CAACC,QAAN,IAAkB,KAAKH,WAAvB,IACEE,KAAK,CAACK,GADR,IAEEF,IAAI,CAACG,OAAL,CAAaN,KAAK,CAACK,GAAnB,IAA0B,CAAC,CAHjB,CAAd;AAKD,KAND;AAOD;;AAEDE,EAAAA,gBAAgB,CAACC,OAAD,EAAoB;AAClC,SAAKT,MAAL,GAAeC,KAAD,IAAkB;AAC9B,aACEA,KAAK,CAACC,QAAN,IAAkB,KAAKH,WAAvB,IACAM,OAAO,CACLI,OAAO,CAACC,IAAR,CAAcC,GAAD,IACXN,OAAO,CAACJ,KAAK,CAACW,QAAN,CAAeF,IAAf,CAAqBG,OAAD,IAAqBF,GAAG,CAACG,IAAJ,CAASD,OAAT,CAAzC,CAAD,CADT,CADK,CAFT;AAQD,KATD;AAUD;;AAEDE,EAAAA,aAAa,CAACd,KAAD,EAAe;AAC1B,WAAO,KAAKD,MAAL,CAAYC,KAAZ,CAAP;AACD;;AArC+C","sourcesContent":["import { IFilter, Entry } from '../types';\n\ntype Filter = (entry: Entry) => boolean;\n\nexport default class IosFilter implements IFilter {\n private readonly minPriority: number;\n private filter: Filter;\n\n constructor(minPriority: number = 0) {\n this.minPriority = minPriority;\n // Default filter by all\n this.filter = (entry: Entry) => {\n return entry.priority >= this.minPriority;\n };\n }\n\n setFilterByTag(tags: string[]) {\n this.filter = (entry: Entry) => {\n return Boolean(\n entry.priority >= this.minPriority &&\n entry.tag &&\n tags.indexOf(entry.tag) > -1\n );\n };\n }\n\n setFilterByMatch(regexes: RegExp[]) {\n this.filter = (entry: Entry) => {\n return (\n entry.priority >= this.minPriority &&\n Boolean(\n regexes.find((reg: RegExp) =>\n Boolean(entry.messages.find((message: string) => reg.test(message)))\n )\n )\n );\n };\n }\n\n shouldInclude(entry: Entry) {\n return this.filter(entry);\n }\n}\n"],"file":"IosFilter.js"}

View File

@@ -0,0 +1,8 @@
import { IParser, Entry } from '../types';
export default class IosParser implements IParser {
static timeRegex: RegExp;
static headerRegex: RegExp;
splitMessages(raw: string): string[];
parseMessages(messages: string[]): Entry[];
}
//# sourceMappingURL=IosParser.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"IosParser.d.ts","sourceRoot":"","sources":["../../src/ios/IosParser.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAG1C,MAAM,CAAC,OAAO,OAAO,SAAU,YAAW,OAAO;IAC/C,MAAM,CAAC,SAAS,EAAE,MAAM,CAAmD;IAC3E,MAAM,CAAC,WAAW,EAAE,MAAM,CAAgE;IAE1F,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE;IAepC,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE;CA0C3C"}

View File

@@ -0,0 +1,69 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _dayjs = _interopRequireDefault(require("dayjs"));
var _constants = require("./constants");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
class IosParser {
splitMessages(raw) {
const messages = [];
let data = raw.toString();
let match = data.match(IosParser.timeRegex);
while (match) {
const timeMatch = match[0];
data = data.slice((match.index || 0) + timeMatch.length);
const nextMatch = data.match(IosParser.timeRegex);
const body = nextMatch ? data.slice(0, nextMatch.index) : data;
messages.push(`${timeMatch} ${body}`);
match = nextMatch;
}
return messages;
}
parseMessages(messages) {
return messages.map(rawMessage => {
const timeMatch = rawMessage.match(IosParser.timeRegex);
if (!timeMatch) {
throw new Error(`Time regex was not matched in message: ${rawMessage}`);
}
const headerMatch = rawMessage.slice(timeMatch[0].length).match(IosParser.headerRegex) || ['', 'Default', '-1', 'unknown'];
const [, priority, pid, tag] = headerMatch;
return {
platform: 'ios',
date: (0, _dayjs.default)(timeMatch[0]).set('millisecond', 0),
pid: parseInt(pid.trim(), 10) || 0,
priority: _constants.Priority.fromName(priority),
tag,
messages: [rawMessage.slice(timeMatch[0].length + headerMatch[0].length).trim()]
};
}).reduce((acc, entry) => {
if (acc.length > 0 && acc[acc.length - 1].date.isSame(entry.date) && acc[acc.length - 1].appId === entry.appId && acc[acc.length - 1].pid === entry.pid && acc[acc.length - 1].priority === entry.priority) {
acc[acc.length - 1].messages.push(...entry.messages);
return acc;
}
return [...acc, entry];
}, []);
}
}
exports.default = IosParser;
_defineProperty(IosParser, "timeRegex", /\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\.[\d+]+/m);
_defineProperty(IosParser, "headerRegex", /^\s+[a-z0-9]+\s+(\w+)\s+[a-z0-9]+\s+(\d+)\s+\d+\s+([^:]+):/);
//# sourceMappingURL=IosParser.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,19 @@
declare const codes: {
DEBUG: number;
DEFAULT: number;
INFO: number;
ERROR: number;
};
export declare type PriorityNames = keyof typeof codes;
export declare const Priority: {
fromName(name: "DEBUG" | "INFO" | "ERROR" | "DEFAULT"): number;
toName(code: number): "DEBUG" | "INFO" | "ERROR" | "DEFAULT";
fromLetter(letter: string): number;
toLetter(code: number): string;
DEBUG: number;
DEFAULT: number;
INFO: number;
ERROR: number;
};
export {};
//# sourceMappingURL=constants.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/ios/constants.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,KAAK;;;;;CAKV,CAAC;AAEF,oBAAY,aAAa,GAAG,MAAM,OAAO,KAAK,CAAC;AAE/C,eAAO,MAAM,QAAQ;;;;;;;;;CAuBpB,CAAC"}

View File

@@ -0,0 +1,34 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Priority = void 0;
const codes = {
DEBUG: 0,
DEFAULT: 1,
INFO: 2,
ERROR: 3
};
const Priority = { ...codes,
fromName(name) {
const value = codes[name.toUpperCase()];
return value ? value : 0;
},
toName(code) {
return Object.keys(codes).find(key => codes[key] === code) || 'DEFAULT';
},
fromLetter(letter) {
return codes[Object.keys(codes).find(key => key[0] === letter.toUpperCase()) || 'DEFAULT'];
},
toLetter(code) {
return Priority.toName(code)[0];
}
};
exports.Priority = Priority;
//# sourceMappingURL=constants.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../src/ios/constants.ts"],"names":["codes","DEBUG","DEFAULT","INFO","ERROR","Priority","fromName","name","value","toUpperCase","toName","code","Object","keys","find","key","fromLetter","letter","toLetter"],"mappings":";;;;;;AAAA,MAAMA,KAAK,GAAG;AACZC,EAAAA,KAAK,EAAE,CADK;AAEZC,EAAAA,OAAO,EAAE,CAFG;AAGZC,EAAAA,IAAI,EAAE,CAHM;AAIZC,EAAAA,KAAK,EAAE;AAJK,CAAd;AASO,MAAMC,QAAQ,GAAG,EACtB,GAAGL,KADmB;;AAEtBM,EAAAA,QAAQ,CAACC,IAAD,EAA8B;AACpC,UAAMC,KAAK,GAAGR,KAAK,CAACO,IAAI,CAACE,WAAL,EAAD,CAAnB;AACA,WAAOD,KAAK,GAAGA,KAAH,GAAW,CAAvB;AACD,GALqB;;AAMtBE,EAAAA,MAAM,CAACC,IAAD,EAA8B;AAClC,WACGC,MAAM,CAACC,IAAP,CAAYb,KAAZ,CAAD,CAAwCc,IAAxC,CACGC,GAAD,IAAwBf,KAAK,CAACe,GAAD,CAAL,KAAeJ,IADzC,KAEK,SAHP;AAKD,GAZqB;;AAatBK,EAAAA,UAAU,CAACC,MAAD,EAAyB;AACjC,WAAOjB,KAAK,CACTY,MAAM,CAACC,IAAP,CAAYb,KAAZ,CAAD,CAAwCc,IAAxC,CACGC,GAAD,IAAwBA,GAAG,CAAC,CAAD,CAAH,KAAWE,MAAM,CAACR,WAAP,EADrC,KAEK,SAHK,CAAZ;AAKD,GAnBqB;;AAoBtBS,EAAAA,QAAQ,CAACP,IAAD,EAAuB;AAC7B,WAAON,QAAQ,CAACK,MAAT,CAAgBC,IAAhB,EAAsB,CAAtB,CAAP;AACD;;AAtBqB,CAAjB","sourcesContent":["const codes = {\n DEBUG: 0,\n DEFAULT: 1,\n INFO: 2,\n ERROR: 3,\n};\n\nexport type PriorityNames = keyof typeof codes;\n\nexport const Priority = {\n ...codes,\n fromName(name: PriorityNames): number {\n const value = codes[name.toUpperCase() as PriorityNames];\n return value ? value : 0;\n },\n toName(code: number): PriorityNames {\n return (\n (Object.keys(codes) as PriorityNames[]).find(\n (key: PriorityNames) => codes[key] === code\n ) || 'DEFAULT'\n );\n },\n fromLetter(letter: string): number {\n return codes[\n (Object.keys(codes) as PriorityNames[]).find(\n (key: PriorityNames) => key[0] === letter.toUpperCase()\n ) || 'DEFAULT'\n ];\n },\n toLetter(code: number): string {\n return Priority.toName(code)[0];\n },\n};\n"],"file":"constants.js"}

View File

@@ -0,0 +1,4 @@
/// <reference types="node" />
import { ChildProcess } from 'child_process';
export declare function runSimulatorLoggingProcess(): ChildProcess;
//# sourceMappingURL=simulator.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"simulator.d.ts","sourceRoot":"","sources":["../../src/ios/simulator.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,YAAY,EAAS,MAAM,eAAe,CAAC;AAGpD,wBAAgB,0BAA0B,IAAI,YAAY,CAsBzD"}

View File

@@ -0,0 +1,21 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.runSimulatorLoggingProcess = runSimulatorLoggingProcess;
var _child_process = require("child_process");
var _errors = require("../errors");
function runSimulatorLoggingProcess() {
try {
return (0, _child_process.spawn)('xcrun', ['simctl', 'spawn', 'booted', 'log', 'stream', '--type', 'log', '--level', 'debug'], {
stdio: 'pipe'
});
} catch (error) {
throw new _errors.CodeError(_errors.ERR_IOS_CANNOT_START_SYSLOG, error.message);
}
}
//# sourceMappingURL=simulator.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../src/ios/simulator.ts"],"names":["runSimulatorLoggingProcess","stdio","error","CodeError","ERR_IOS_CANNOT_START_SYSLOG","message"],"mappings":";;;;;;;AAAA;;AACA;;AAEO,SAASA,0BAAT,GAAoD;AACzD,MAAI;AACF,WAAO,0BACL,OADK,EAEL,CACE,QADF,EAEE,OAFF,EAGE,QAHF,EAIE,KAJF,EAKE,QALF,EAME,QANF,EAOE,KAPF,EAQE,SARF,EASE,OATF,CAFK,EAaL;AACEC,MAAAA,KAAK,EAAE;AADT,KAbK,CAAP;AAiBD,GAlBD,CAkBE,OAAOC,KAAP,EAAc;AACd,UAAM,IAAIC,iBAAJ,CAAcC,mCAAd,EAA4CF,KAAD,CAAiBG,OAA5D,CAAN;AACD;AACF","sourcesContent":["import { ChildProcess, spawn } from 'child_process';\nimport { CodeError, ERR_IOS_CANNOT_START_SYSLOG } from '../errors';\n\nexport function runSimulatorLoggingProcess(): ChildProcess {\n try {\n return spawn(\n 'xcrun',\n [\n 'simctl',\n 'spawn',\n 'booted',\n 'log',\n 'stream',\n '--type',\n 'log',\n '--level',\n 'debug',\n ],\n {\n stdio: 'pipe',\n }\n );\n } catch (error) {\n throw new CodeError(ERR_IOS_CANNOT_START_SYSLOG, (error as Error).message);\n }\n}\n"],"file":"simulator.js"}