Files
Eric FELIXINE e30ae8ed09 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
2026-06-01 18:00:35 -04:00

144 lines
2.5 KiB
JavaScript
Executable File

'use strict';
const Assert = require('@hapi/hoek/lib/assert');
const Clone = require('@hapi/hoek/lib/clone');
const Common = require('./common');
const internals = {
max: 1000,
supported: new Set(['undefined', 'boolean', 'number', 'string'])
};
exports.provider = {
provision(options) {
return new internals.Cache(options);
}
};
// Least Recently Used (LRU) Cache
internals.Cache = class {
constructor(options = {}) {
Common.assertOptions(options, ['max']);
Assert(options.max === undefined || options.max && options.max > 0 && isFinite(options.max), 'Invalid max cache size');
this._max = options.max || internals.max;
this._map = new Map(); // Map of nodes by key
this._list = new internals.List(); // List of nodes (most recently used in head)
}
get length() {
return this._map.size;
}
set(key, value) {
if (key !== null &&
!internals.supported.has(typeof key)) {
return;
}
let node = this._map.get(key);
if (node) {
node.value = value;
this._list.first(node);
return;
}
node = this._list.unshift({ key, value });
this._map.set(key, node);
this._compact();
}
get(key) {
const node = this._map.get(key);
if (node) {
this._list.first(node);
return Clone(node.value);
}
}
_compact() {
if (this._map.size > this._max) {
const node = this._list.pop();
this._map.delete(node.key);
}
}
};
internals.List = class {
constructor() {
this.tail = null;
this.head = null;
}
unshift(node) {
node.next = null;
node.prev = this.head;
if (this.head) {
this.head.next = node;
}
this.head = node;
if (!this.tail) {
this.tail = node;
}
return node;
}
first(node) {
if (node === this.head) {
return;
}
this._remove(node);
this.unshift(node);
}
pop() {
return this._remove(this.tail);
}
_remove(node) {
const { next, prev } = node;
next.prev = prev;
if (prev) {
prev.next = next;
}
if (node === this.tail) {
this.tail = next;
}
node.prev = null;
node.next = null;
return node;
}
};