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,26 @@
apply plugin: 'com.android.library'
group = 'host.exp.exponent'
version = '16.0.2'
apply from: "../scripts/get-app-config-android.gradle"
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
apply from: expoModulesCorePlugin
applyKotlinExpoModulesCorePlugin()
useCoreDependencies()
useDefaultAndroidSdkVersions()
useExpoPublishing()
android {
namespace "expo.modules.constants"
defaultConfig {
versionCode 33
versionName "16.0.2"
}
}
dependencies {
api "androidx.annotation:annotation:1.0.0"
implementation "commons-io:commons-io:2.6"
}

View File

@@ -0,0 +1,3 @@
<manifest>
</manifest>

View File

@@ -0,0 +1,20 @@
// Copyright 2015-present 650 Industries. All rights reserved.
package expo.modules.constants
import expo.modules.kotlin.modules.Module
import expo.modules.kotlin.modules.ModuleDefinition
class ConstantsModule : Module() {
override fun definition() = ModuleDefinition {
Name("ExponentConstants")
Constants {
return@Constants appContext.constants?.constants ?: emptyMap()
}
AsyncFunction<String?>("getWebViewUserAgentAsync") {
return@AsyncFunction System.getProperty("http.agent")
}
}
}

View File

@@ -0,0 +1,11 @@
package expo.modules.constants
import android.content.Context
import expo.modules.core.BasePackage
import expo.modules.core.interfaces.InternalModule
class ConstantsPackage : BasePackage() {
override fun createInternalModules(context: Context): List<InternalModule> =
listOf(ConstantsService(context))
}

View File

@@ -0,0 +1,96 @@
package expo.modules.constants
import org.apache.commons.io.IOUtils
import expo.modules.core.interfaces.InternalModule
import expo.modules.interfaces.constants.ConstantsInterface
import android.os.Build
import android.util.Log
import android.content.Context
import java.io.FileNotFoundException
import java.lang.Exception
import java.nio.charset.StandardCharsets
import java.util.*
private val TAG = ConstantsService::class.java.simpleName
private const val CONFIG_FILE_NAME = "app.config"
open class ConstantsService(private val context: Context) : InternalModule, ConstantsInterface {
var statusBarHeightInternal = context.resources.getIdentifier("status_bar_height", "dimen", "android")
.takeIf { it > 0 }
?.let { (context.resources::getDimensionPixelSize)(it) }
?.let { pixels -> convertPixelsToDp(pixels.toFloat(), context) }
?: 0
private val sessionId = UUID.randomUUID().toString()
enum class ExecutionEnvironment(val string: String) {
BARE("bare"),
STANDALONE("standalone"),
STORE_CLIENT("storeClient")
}
override fun getExportedInterfaces(): List<Class<*>> = listOf(ConstantsInterface::class.java)
override fun getConstants(): Map<String, Any?> {
return mutableMapOf(
"sessionId" to sessionId,
"executionEnvironment" to ExecutionEnvironment.BARE.string,
"statusBarHeight" to statusBarHeightInternal,
"deviceName" to deviceName,
"systemFonts" to systemFonts,
"systemVersion" to systemVersion,
"manifest" to appConfig,
"platform" to mapOf<String, Map<String, Any>>("android" to emptyMap())
)
}
// Just use package name in vanilla React Native apps.
override fun getAppScopeKey(): String? = context.packageName
override fun getDeviceName(): String = Build.MODEL
override fun getStatusBarHeight() = statusBarHeightInternal
override fun getSystemVersion(): String = Build.VERSION.RELEASE
// From https://github.com/dabit3/react-native-fonts
override fun getSystemFonts() = listOf(
"normal",
"notoserif",
"sans-serif",
"sans-serif-light",
"sans-serif-thin",
"sans-serif-condensed",
"sans-serif-medium",
"serif",
"Roboto",
"monospace"
)
private val appConfig: String?
get() {
try {
context.assets.open(CONFIG_FILE_NAME).use {
stream ->
return IOUtils.toString(stream, StandardCharsets.UTF_8)
}
} catch (e: FileNotFoundException) {
// do nothing, expected in managed apps
} catch (e: Exception) {
Log.e(TAG, "Error reading embedded app config", e)
}
return null
}
companion object {
private fun convertPixelsToDp(px: Float, context: Context): Int {
val resources = context.resources
val metrics = resources.displayMetrics
val dp = px / (metrics.densityDpi / 160f)
return dp.toInt()
}
}
}