Compare commits
10 Commits
5b2a2e7ddb
...
6515d8348d
| Author | SHA1 | Date | |
|---|---|---|---|
| 6515d8348d | |||
| d0045a7d61 | |||
| 0074c28dcd | |||
| 4cba9ce74b | |||
| 89610b807c | |||
| 4633805dbd | |||
| 7f95b8a2ef | |||
| 8eb5d93514 | |||
| 15fcbcc9eb | |||
| 85ad36880c |
Generated
+3
-3
@@ -1,3 +1,3 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
|
||||
Generated
+1
@@ -0,0 +1 @@
|
||||
\\192.168.1.50\cesizen\
|
||||
Generated
+51
-51
@@ -1,52 +1,52 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/linux/flutter/ephemeral/.plugin_symlinks/flutter_secure_storage_linux/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/linux/flutter/ephemeral/.plugin_symlinks/flutter_secure_storage_linux/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/linux/flutter/ephemeral/.plugin_symlinks/flutter_secure_storage_linux/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/linux/flutter/ephemeral/.plugin_symlinks/jni/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/linux/flutter/ephemeral/.plugin_symlinks/jni/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/linux/flutter/ephemeral/.plugin_symlinks/jni/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/linux/flutter/ephemeral/.plugin_symlinks/jni/example/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/linux/flutter/ephemeral/.plugin_symlinks/jni/example/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/linux/flutter/ephemeral/.plugin_symlinks/jni/example/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/flutter_secure_storage_windows/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/flutter_secure_storage_windows/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/flutter_secure_storage_windows/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/flutter_secure_storage_windows/example/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/flutter_secure_storage_windows/example/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/flutter_secure_storage_windows/example/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/jni/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/jni/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/jni/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/jni/example/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/jni/example/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/jni/example/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/path_provider_windows/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/path_provider_windows/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/path_provider_windows/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/path_provider_windows/example/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/path_provider_windows/example/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/path_provider_windows/example/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/permission_handler_windows/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/permission_handler_windows/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/permission_handler_windows/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/permission_handler_windows/example/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/permission_handler_windows/example/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/permission_handler_windows/example/build" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/linux/flutter/ephemeral/.plugin_symlinks/flutter_secure_storage_linux/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/linux/flutter/ephemeral/.plugin_symlinks/flutter_secure_storage_linux/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/linux/flutter/ephemeral/.plugin_symlinks/flutter_secure_storage_linux/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/linux/flutter/ephemeral/.plugin_symlinks/jni/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/linux/flutter/ephemeral/.plugin_symlinks/jni/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/linux/flutter/ephemeral/.plugin_symlinks/jni/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/linux/flutter/ephemeral/.plugin_symlinks/jni/example/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/linux/flutter/ephemeral/.plugin_symlinks/jni/example/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/linux/flutter/ephemeral/.plugin_symlinks/jni/example/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/flutter_secure_storage_windows/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/flutter_secure_storage_windows/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/flutter_secure_storage_windows/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/flutter_secure_storage_windows/example/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/flutter_secure_storage_windows/example/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/flutter_secure_storage_windows/example/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/jni/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/jni/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/jni/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/jni/example/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/jni/example/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/jni/example/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/path_provider_windows/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/path_provider_windows/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/path_provider_windows/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/path_provider_windows/example/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/path_provider_windows/example/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/path_provider_windows/example/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/permission_handler_windows/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/permission_handler_windows/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/permission_handler_windows/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/permission_handler_windows/example/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/permission_handler_windows/example/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/permission_handler_windows/example/build" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
Generated
+228
-90
@@ -10,7 +10,7 @@
|
||||
<option name="id" value="A402SO" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Sony" />
|
||||
<option name="name" value="Xperia 10" />
|
||||
<option name="name" value="Xperia 10 VI" />
|
||||
<option name="screenDensity" value="450" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2520" />
|
||||
@@ -34,7 +34,7 @@
|
||||
<option name="id" value="OP535DL1" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="OnePlus" />
|
||||
<option name="name" value="CPH2409" />
|
||||
<option name="name" value="Nord CE 2 Lite 5G" />
|
||||
<option name="screenDensity" value="401" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2412" />
|
||||
@@ -46,7 +46,7 @@
|
||||
<option name="id" value="OP5552L1" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="OnePlus" />
|
||||
<option name="name" value="CPH2415" />
|
||||
<option name="name" value="10T 5G" />
|
||||
<option name="screenDensity" value="480" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2412" />
|
||||
@@ -58,7 +58,7 @@
|
||||
<option name="id" value="OP573DL1" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="OPPO" />
|
||||
<option name="name" value="CPH2557" />
|
||||
<option name="name" value="A79 5G" />
|
||||
<option name="screenDensity" value="480" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2400" />
|
||||
@@ -70,7 +70,7 @@
|
||||
<option name="id" value="OP5759L1" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="OPPO" />
|
||||
<option name="name" value="CPH2579" />
|
||||
<option name="name" value="A38" />
|
||||
<option name="screenDensity" value="320" />
|
||||
<option name="screenX" value="720" />
|
||||
<option name="screenY" value="1612" />
|
||||
@@ -94,7 +94,7 @@
|
||||
<option name="id" value="RMX3231" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="realme" />
|
||||
<option name="name" value="C11" />
|
||||
<option name="name" value="RMX3231" />
|
||||
<option name="screenDensity" value="320" />
|
||||
<option name="screenX" value="720" />
|
||||
<option name="screenY" value="1600" />
|
||||
@@ -155,7 +155,7 @@
|
||||
<option name="id" value="a05s" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="A05s" />
|
||||
<option name="name" value="Galaxy A05s" />
|
||||
<option name="screenDensity" value="450" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2400" />
|
||||
@@ -184,6 +184,18 @@
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2408" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="33" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="a13x" />
|
||||
<option name="id" value="a13x" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy A13 5G" />
|
||||
<option name="screenDensity" value="300" />
|
||||
<option name="screenX" value="720" />
|
||||
<option name="screenY" value="1600" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="samsung" />
|
||||
@@ -191,7 +203,7 @@
|
||||
<option name="id" value="a14m" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="SM-A145R" />
|
||||
<option name="name" value="Galaxy A14" />
|
||||
<option name="screenDensity" value="450" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2408" />
|
||||
@@ -227,7 +239,7 @@
|
||||
<option name="id" value="a15" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="A15" />
|
||||
<option name="name" value="Galaxy A15" />
|
||||
<option name="screenDensity" value="450" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
@@ -239,7 +251,7 @@
|
||||
<option name="id" value="a15x" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="A15 5G" />
|
||||
<option name="name" value="Galaxy A15 5G" />
|
||||
<option name="screenDensity" value="450" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
@@ -251,7 +263,7 @@
|
||||
<option name="id" value="a15x" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="A15 5G" />
|
||||
<option name="name" value="Galaxy A15 5G" />
|
||||
<option name="screenDensity" value="450" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
@@ -275,7 +287,7 @@
|
||||
<option name="id" value="a16" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="SM-A165M" />
|
||||
<option name="name" value="Galaxy A16" />
|
||||
<option name="screenDensity" value="450" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
@@ -287,7 +299,7 @@
|
||||
<option name="id" value="a16x" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="A16 5G" />
|
||||
<option name="name" value="Galaxy A16 5G" />
|
||||
<option name="screenDensity" value="450" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
@@ -299,7 +311,7 @@
|
||||
<option name="id" value="a16x" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="A16 5G" />
|
||||
<option name="name" value="Galaxy A16 5G" />
|
||||
<option name="screenDensity" value="450" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
@@ -311,7 +323,7 @@
|
||||
<option name="id" value="a16xeea" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="A16 5G" />
|
||||
<option name="name" value="Galaxy A16 5G" />
|
||||
<option name="screenDensity" value="450" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
@@ -335,11 +347,23 @@
|
||||
<option name="id" value="a26x" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="SM-A266B" />
|
||||
<option name="name" value="Galaxy A26 5G" />
|
||||
<option name="screenDensity" value="450" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="33" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="a32" />
|
||||
<option name="id" value="a32" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy A32" />
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2400" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="36" />
|
||||
<option name="brand" value="samsung" />
|
||||
@@ -347,7 +371,7 @@
|
||||
<option name="id" value="a34x" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="SM-A346N" />
|
||||
<option name="name" value="Galaxy A34 5G" />
|
||||
<option name="screenDensity" value="450" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
@@ -359,7 +383,7 @@
|
||||
<option name="id" value="a35x" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="A35" />
|
||||
<option name="name" value="Galaxy A35 5G" />
|
||||
<option name="screenDensity" value="450" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
@@ -371,7 +395,7 @@
|
||||
<option name="id" value="a35x" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="A35" />
|
||||
<option name="name" value="Galaxy A35 5G" />
|
||||
<option name="screenDensity" value="450" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
@@ -383,7 +407,7 @@
|
||||
<option name="id" value="a35x" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="A35" />
|
||||
<option name="name" value="Galaxy A35 5G" />
|
||||
<option name="screenDensity" value="450" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
@@ -395,7 +419,7 @@
|
||||
<option name="id" value="a36xq" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="SM-A366E" />
|
||||
<option name="name" value="Galaxy A36 5G" />
|
||||
<option name="screenDensity" value="450" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
@@ -407,7 +431,7 @@
|
||||
<option name="id" value="a36xq" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="SM-A366E" />
|
||||
<option name="name" value="Galaxy A36 5G" />
|
||||
<option name="screenDensity" value="450" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
@@ -419,7 +443,7 @@
|
||||
<option name="id" value="a56x" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="SM-A566E" />
|
||||
<option name="name" value="Galaxy A56 5G" />
|
||||
<option name="screenDensity" value="450" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
@@ -548,6 +572,7 @@
|
||||
<option name="api" value="36" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="blazer" />
|
||||
<option name="default" value="true" />
|
||||
<option name="id" value="blazer" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
@@ -555,6 +580,11 @@
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2410" />
|
||||
<option name="tags">
|
||||
<list>
|
||||
<option value="dda-default" />
|
||||
</list>
|
||||
</option>
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="32" />
|
||||
@@ -575,11 +605,35 @@
|
||||
<option name="id" value="c1q" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy Note 20 5G" />
|
||||
<option name="name" value="Galaxy Note20 5G" />
|
||||
<option name="screenDensity" value="450" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2400" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="33" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="c1qksw" />
|
||||
<option name="id" value="c1qksw" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy Note20 5G" />
|
||||
<option name="screenDensity" value="450" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2400" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="33" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="c2q" />
|
||||
<option name="id" value="c2q" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy Note20 Ultra 5G" />
|
||||
<option name="screenDensity" value="560" />
|
||||
<option name="screenX" value="1440" />
|
||||
<option name="screenY" value="3088" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="google" />
|
||||
@@ -608,7 +662,6 @@
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="comet" />
|
||||
<option name="default" value="true" />
|
||||
<option name="id" value="comet" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
@@ -616,17 +669,11 @@
|
||||
<option name="screenDensity" value="390" />
|
||||
<option name="screenX" value="2076" />
|
||||
<option name="screenY" value="2152" />
|
||||
<option name="tags">
|
||||
<list>
|
||||
<option value="dda-default" />
|
||||
</list>
|
||||
</option>
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="35" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="comet" />
|
||||
<option name="default" value="true" />
|
||||
<option name="id" value="comet" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
@@ -634,11 +681,6 @@
|
||||
<option name="screenDensity" value="390" />
|
||||
<option name="screenX" value="2076" />
|
||||
<option name="screenY" value="2152" />
|
||||
<option name="tags">
|
||||
<list>
|
||||
<option value="dda-default" />
|
||||
</list>
|
||||
</option>
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="35" />
|
||||
@@ -647,7 +689,7 @@
|
||||
<option name="id" value="cuscoi" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Motorola" />
|
||||
<option name="name" value="edge 50 fusion" />
|
||||
<option name="name" value="moto g96 5G" />
|
||||
<option name="screenDensity" value="400" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2400" />
|
||||
@@ -659,7 +701,7 @@
|
||||
<option name="id" value="dm1q" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="S23" />
|
||||
<option name="name" value="Galaxy S23" />
|
||||
<option name="screenDensity" value="480" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
@@ -671,7 +713,7 @@
|
||||
<option name="id" value="dm1q-SM-S911U" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="S23" />
|
||||
<option name="name" value="Galaxy S23" />
|
||||
<option name="screenDensity" value="480" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
@@ -695,7 +737,7 @@
|
||||
<option name="id" value="dm2q" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="S23 Plus" />
|
||||
<option name="name" value="Galaxy S23+" />
|
||||
<option name="screenDensity" value="450" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
@@ -728,7 +770,6 @@
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="e1q" />
|
||||
<option name="default" value="true" />
|
||||
<option name="id" value="e1q" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
@@ -736,17 +777,11 @@
|
||||
<option name="screenDensity" value="480" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
<option name="tags">
|
||||
<list>
|
||||
<option value="dda-default" />
|
||||
</list>
|
||||
</option>
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="36" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="e1q" />
|
||||
<option name="default" value="true" />
|
||||
<option name="id" value="e1q" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
@@ -754,11 +789,18 @@
|
||||
<option name="screenDensity" value="480" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
<option name="tags">
|
||||
<list>
|
||||
<option value="dda-default" />
|
||||
</list>
|
||||
</option>
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="36" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="e1s" />
|
||||
<option name="id" value="e1s" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy S24" />
|
||||
<option name="screenDensity" value="480" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="36" />
|
||||
@@ -767,7 +809,7 @@
|
||||
<option name="id" value="e2q" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy S24 +" />
|
||||
<option name="name" value="Galaxy S24+" />
|
||||
<option name="screenDensity" value="450" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
@@ -916,10 +958,23 @@
|
||||
<option name="screenX" value="720" />
|
||||
<option name="screenY" value="1600" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="35" />
|
||||
<option name="brand" value="motorola" />
|
||||
<option name="codename" value="fogos" />
|
||||
<option name="id" value="fogos" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Motorola" />
|
||||
<option name="name" value="moto g34 5G" />
|
||||
<option name="screenDensity" value="280" />
|
||||
<option name="screenX" value="720" />
|
||||
<option name="screenY" value="1600" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="36" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="frankel" />
|
||||
<option name="default" value="true" />
|
||||
<option name="id" value="frankel" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
@@ -927,6 +982,11 @@
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2424" />
|
||||
<option name="tags">
|
||||
<list>
|
||||
<option value="dda-default" />
|
||||
</list>
|
||||
</option>
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
@@ -935,7 +995,7 @@
|
||||
<option name="id" value="g0q" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="SM-S906U1" />
|
||||
<option name="name" value="Galaxy S22+" />
|
||||
<option name="screenDensity" value="450" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
@@ -947,7 +1007,7 @@
|
||||
<option name="id" value="g0q" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="SM-S906U1" />
|
||||
<option name="name" value="Galaxy S22+" />
|
||||
<option name="screenDensity" value="450" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
@@ -971,7 +1031,7 @@
|
||||
<option name="id" value="gta9pwifi" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="SM-X210" />
|
||||
<option name="name" value="Galaxy Tab A9+" />
|
||||
<option name="screenDensity" value="240" />
|
||||
<option name="screenX" value="1200" />
|
||||
<option name="screenY" value="1920" />
|
||||
@@ -983,7 +1043,7 @@
|
||||
<option name="id" value="gts7lwifi" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="SM-T870" />
|
||||
<option name="name" value="Galaxy Tab S7" />
|
||||
<option name="screenDensity" value="340" />
|
||||
<option name="screenX" value="1600" />
|
||||
<option name="screenY" value="2560" />
|
||||
@@ -995,7 +1055,7 @@
|
||||
<option name="id" value="gts7xllite" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="SM-T738U" />
|
||||
<option name="name" value="Galaxy Tab S7 FE 5G" />
|
||||
<option name="screenDensity" value="340" />
|
||||
<option name="screenX" value="1600" />
|
||||
<option name="screenY" value="2560" />
|
||||
@@ -1045,7 +1105,7 @@
|
||||
<option name="id" value="gts9wifi" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="SM-X710" />
|
||||
<option name="name" value="Galaxy Tab S9" />
|
||||
<option name="screenDensity" value="340" />
|
||||
<option name="screenX" value="1600" />
|
||||
<option name="screenY" value="2560" />
|
||||
@@ -1057,7 +1117,7 @@
|
||||
<option name="id" value="guamna" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Motorola" />
|
||||
<option name="name" value="Moto XT2093-3" />
|
||||
<option name="name" value="moto g play (2021)" />
|
||||
<option name="screenDensity" value="280" />
|
||||
<option name="screenX" value="720" />
|
||||
<option name="screenY" value="1600" />
|
||||
@@ -1069,7 +1129,7 @@
|
||||
<option name="id" value="guamp" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Motorola" />
|
||||
<option name="name" value="Moto XT2083-1" />
|
||||
<option name="name" value="moto g(9) play" />
|
||||
<option name="screenDensity" value="280" />
|
||||
<option name="screenX" value="720" />
|
||||
<option name="screenY" value="1600" />
|
||||
@@ -1194,6 +1254,30 @@
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2400" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="36" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="m1q" />
|
||||
<option name="id" value="m1q" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy S26" />
|
||||
<option name="screenDensity" value="480" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="36" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="m2q" />
|
||||
<option name="id" value="m2q" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy S26+" />
|
||||
<option name="screenDensity" value="450" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="motorola" />
|
||||
@@ -1222,6 +1306,7 @@
|
||||
<option name="api" value="36" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="mustang" />
|
||||
<option name="default" value="true" />
|
||||
<option name="id" value="mustang" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
@@ -1229,6 +1314,11 @@
|
||||
<option name="screenDensity" value="390" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2404" />
|
||||
<option name="tags">
|
||||
<list>
|
||||
<option value="dda-default" />
|
||||
</list>
|
||||
</option>
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
@@ -1237,7 +1327,7 @@
|
||||
<option name="id" value="o1q" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy S21" />
|
||||
<option name="name" value="Galaxy S21 5G" />
|
||||
<option name="screenDensity" value="421" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2400" />
|
||||
@@ -1249,7 +1339,7 @@
|
||||
<option name="id" value="o1q" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy S21" />
|
||||
<option name="name" value="Galaxy S21 5G" />
|
||||
<option name="screenDensity" value="421" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2400" />
|
||||
@@ -1273,7 +1363,19 @@
|
||||
<option name="id" value="p3q" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy S21 Ultra" />
|
||||
<option name="name" value="Galaxy S21 Ultra 5G" />
|
||||
<option name="screenDensity" value="600" />
|
||||
<option name="screenX" value="1440" />
|
||||
<option name="screenY" value="3200" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="35" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="p3s" />
|
||||
<option name="id" value="p3s" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy S21 Ultra 5G" />
|
||||
<option name="screenDensity" value="600" />
|
||||
<option name="screenX" value="1440" />
|
||||
<option name="screenY" value="3200" />
|
||||
@@ -1285,7 +1387,7 @@
|
||||
<option name="id" value="pa2q" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="S25+" />
|
||||
<option name="name" value="Galaxy S25+" />
|
||||
<option name="screenDensity" value="450" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
@@ -1306,6 +1408,7 @@
|
||||
<option name="api" value="35" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="pa3q" />
|
||||
<option name="default" value="true" />
|
||||
<option name="id" value="pa3q" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
@@ -1313,11 +1416,17 @@
|
||||
<option name="screenDensity" value="450" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
<option name="tags">
|
||||
<list>
|
||||
<option value="dda-default" />
|
||||
</list>
|
||||
</option>
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="36" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="pa3q" />
|
||||
<option name="default" value="true" />
|
||||
<option name="id" value="pa3q" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
@@ -1325,6 +1434,11 @@
|
||||
<option name="screenDensity" value="450" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
<option name="tags">
|
||||
<list>
|
||||
<option value="dda-default" />
|
||||
</list>
|
||||
</option>
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="33" />
|
||||
@@ -1362,6 +1476,18 @@
|
||||
<option name="screenX" value="1812" />
|
||||
<option name="screenY" value="2176" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="36" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="q5qksx" />
|
||||
<option name="id" value="q5qksx" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy Z Fold5" />
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="1812" />
|
||||
<option name="screenY" value="2176" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="samsung" />
|
||||
@@ -1374,6 +1500,18 @@
|
||||
<option name="screenX" value="1856" />
|
||||
<option name="screenY" value="2160" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="36" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="q7mq" />
|
||||
<option name="id" value="q7mq" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy Z TriFold" />
|
||||
<option name="screenDensity" value="320" />
|
||||
<option name="screenX" value="2160" />
|
||||
<option name="screenY" value="1584" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="36" />
|
||||
<option name="brand" value="samsung" />
|
||||
@@ -1393,7 +1531,7 @@
|
||||
<option name="id" value="r0qcsx" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="S22" />
|
||||
<option name="name" value="Galaxy S22" />
|
||||
<option name="screenDensity" value="480" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
@@ -1419,7 +1557,7 @@
|
||||
<option name="id" value="r11q" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="SM-S711U" />
|
||||
<option name="name" value="Galaxy S23 FE" />
|
||||
<option name="screenDensity" value="450" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
@@ -1431,7 +1569,7 @@
|
||||
<option name="id" value="r11s" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="SM-S711N" />
|
||||
<option name="name" value="Galaxy S23 FE" />
|
||||
<option name="screenDensity" value="450" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
@@ -1488,6 +1626,7 @@
|
||||
<option name="api" value="36" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="rango" />
|
||||
<option name="default" value="true" />
|
||||
<option name="id" value="rango" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
@@ -1495,6 +1634,11 @@
|
||||
<option name="screenDensity" value="390" />
|
||||
<option name="screenX" value="2076" />
|
||||
<option name="screenY" value="2152" />
|
||||
<option name="tags">
|
||||
<list>
|
||||
<option value="dda-default" />
|
||||
</list>
|
||||
</option>
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="30" />
|
||||
@@ -1544,7 +1688,7 @@
|
||||
<option name="id" value="t2q" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy S21 Plus" />
|
||||
<option name="name" value="Galaxy S21+ 5G" />
|
||||
<option name="screenDensity" value="394" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2400" />
|
||||
@@ -1556,7 +1700,7 @@
|
||||
<option name="id" value="t2q" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy S21 Plus" />
|
||||
<option name="name" value="Galaxy S21+ 5G" />
|
||||
<option name="screenDensity" value="394" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2400" />
|
||||
@@ -1590,7 +1734,6 @@
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="tokay" />
|
||||
<option name="default" value="true" />
|
||||
<option name="id" value="tokay" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
@@ -1598,17 +1741,11 @@
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2424" />
|
||||
<option name="tags">
|
||||
<list>
|
||||
<option value="dda-default" />
|
||||
</list>
|
||||
</option>
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="35" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="tokay" />
|
||||
<option name="default" value="true" />
|
||||
<option name="id" value="tokay" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
@@ -1616,17 +1753,11 @@
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2424" />
|
||||
<option name="tags">
|
||||
<list>
|
||||
<option value="dda-default" />
|
||||
</list>
|
||||
</option>
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="36" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="tokay" />
|
||||
<option name="default" value="true" />
|
||||
<option name="id" value="tokay" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
@@ -1634,11 +1765,6 @@
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2424" />
|
||||
<option name="tags">
|
||||
<list>
|
||||
<option value="dda-default" />
|
||||
</list>
|
||||
</option>
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
@@ -1647,7 +1773,7 @@
|
||||
<option name="id" value="xcover7" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="SM-G556B" />
|
||||
<option name="name" value="Galaxy XCover7" />
|
||||
<option name="screenDensity" value="450" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2408" />
|
||||
@@ -1659,11 +1785,23 @@
|
||||
<option name="id" value="y2q" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="S20 Plus 5G" />
|
||||
<option name="name" value="Galaxy S20+ 5G" />
|
||||
<option name="screenDensity" value="600" />
|
||||
<option name="screenX" value="1440" />
|
||||
<option name="screenY" value="3200" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="33" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="z3q" />
|
||||
<option name="id" value="z3q" />
|
||||
<option name="labId" value="google" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy S20 Ultra 5G" />
|
||||
<option name="screenDensity" value="560" />
|
||||
<option name="screenX" value="1440" />
|
||||
<option name="screenY" value="3200" />
|
||||
</PersistentDeviceSelectionData>
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
|
||||
Generated
+10
-10
@@ -1,11 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="deploymentTargetSelector">
|
||||
<selectionStates>
|
||||
<SelectionState runConfigName="Wear App">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
<DialogSelection />
|
||||
</SelectionState>
|
||||
</selectionStates>
|
||||
</component>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="deploymentTargetSelector">
|
||||
<selectionStates>
|
||||
<SelectionState runConfigName="Wear App">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
<DialogSelection />
|
||||
</SelectionState>
|
||||
</selectionStates>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+7
-7
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="MarkdownSettings">
|
||||
<option name="previewPanelProviderInfo">
|
||||
<ProviderInfo name="Compose (experimental)" className="com.intellij.markdown.compose.preview.ComposePanelProvider" />
|
||||
</option>
|
||||
</component>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="MarkdownSettings">
|
||||
<option name="previewPanelProviderInfo">
|
||||
<ProviderInfo name="Compose (experimental)" className="com.intellij.markdown.compose.preview.ComposePanelProvider" />
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+7
-7
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/CESIZen.iml" filepath="$PROJECT_DIR$/.idea/CESIZen.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/CESIZen.iml" filepath="$PROJECT_DIR$/.idea/CESIZen.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
+7
-7
@@ -1,7 +1,7 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Launch All (Mobile & Wear)" type="CompoundRunConfigurationType">
|
||||
<toRun name="Mobile App" type="FlutterRunConfigurationType" />
|
||||
<toRun name="Wear App" type="AndroidRunConfigurationType" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Launch All (Mobile & Wear)" type="CompoundRunConfigurationType">
|
||||
<toRun name="Mobile App" type="FlutterRunConfigurationType" />
|
||||
<toRun name="Wear App" type="AndroidRunConfigurationType" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
|
||||
Generated
+7
-7
@@ -1,7 +1,7 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Mobile App" type="FlutterRunConfigurationType" factoryName="Flutter">
|
||||
<option name="filePath" value="$PROJECT_DIR$/mobile/lib/main.dart" />
|
||||
<option name="additionalArgs" value="-d PV5HRKVCJVKZ9DOB" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Mobile App" type="FlutterRunConfigurationType" factoryName="Flutter">
|
||||
<option name="filePath" value="$PROJECT_DIR$/mobile/lib/main.dart" />
|
||||
<option name="additionalArgs" value="-d PV5HRKVCJVKZ9DOB" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
|
||||
Generated
+68
-68
@@ -1,68 +1,68 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Wear App" type="AndroidRunConfigurationType" factoryName="Android App">
|
||||
<module name="CESIZen.Wear.app.main" />
|
||||
<option name="DEPLOY" value="true" />
|
||||
<option name="DEPLOY_APK_FROM_BUNDLE" value="false" />
|
||||
<option name="DEPLOY_AS_INSTANT" value="false" />
|
||||
<option name="ARTIFACT_NAME" value="" />
|
||||
<option name="PM_INSTALL_OPTIONS" value="" />
|
||||
<option name="ALL_USERS" value="false" />
|
||||
<option name="ALWAYS_INSTALL_WITH_PM" value="false" />
|
||||
<option name="CLEAR_APP_STORAGE" value="false" />
|
||||
<option name="DYNAMIC_FEATURES_DISABLED_LIST" value="" />
|
||||
<option name="ACTIVITY_EXTRA_FLAGS" value="" />
|
||||
<option name="MODE" value="default_activity" />
|
||||
<option name="CLEAR_LOGCAT" value="false" />
|
||||
<option name="SHOW_LOGCAT_AUTOMATICALLY" value="false" />
|
||||
<option name="INSPECTION_WITHOUT_ACTIVITY_RESTART" value="false" />
|
||||
<option name="TARGET_SELECTION_MODE" value="DEVICE_AND_SNAPSHOT_COMBO_BOX" />
|
||||
<option name="PREFERRED_AVD" value="" />
|
||||
<option name="SELECTED_DEVICE_ID" value="adb-d76a0274-X63ZH9 (2)._adb-tls-connect._tcp" />
|
||||
<option name="SELECTED_CLOUD_MATRIX_CONFIGURATION_ID" value="-1" />
|
||||
<option name="SELECTED_CLOUD_MATRIX_PROJECT_ID" value="" />
|
||||
<option name="DEBUGGER_TYPE" value="Auto" />
|
||||
<Auto>
|
||||
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
||||
<option name="SHOW_STATIC_VARS" value="true" />
|
||||
<option name="WORKING_DIR" value="" />
|
||||
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
||||
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
|
||||
<option name="DEBUG_SANDBOX_SDK" value="false" />
|
||||
</Auto>
|
||||
<Hybrid>
|
||||
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
||||
<option name="SHOW_STATIC_VARS" value="true" />
|
||||
<option name="WORKING_DIR" value="" />
|
||||
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
||||
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
|
||||
<option name="DEBUG_SANDBOX_SDK" value="false" />
|
||||
</Hybrid>
|
||||
<Java>
|
||||
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
|
||||
<option name="DEBUG_SANDBOX_SDK" value="false" />
|
||||
</Java>
|
||||
<Native>
|
||||
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
||||
<option name="SHOW_STATIC_VARS" value="true" />
|
||||
<option name="WORKING_DIR" value="" />
|
||||
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
||||
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
|
||||
<option name="DEBUG_SANDBOX_SDK" value="false" />
|
||||
</Native>
|
||||
<Profilers>
|
||||
<option name="ADVANCED_PROFILING_ENABLED" value="false" />
|
||||
<option name="STARTUP_PROFILING_ENABLED" value="false" />
|
||||
<option name="DETAILED_REPORT_APP_STARTUP_PROFILING_ENABLED" value="false" />
|
||||
</Profilers>
|
||||
<option name="DEEP_LINK" value="" />
|
||||
<option name="ACTIVITY_CLASS" value="" />
|
||||
<option name="SEARCH_ACTIVITY_IN_GLOBAL_SCOPE" value="false" />
|
||||
<option name="SKIP_ACTIVITY_VALIDATION" value="false" />
|
||||
<method v="2">
|
||||
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Wear App" type="AndroidRunConfigurationType" factoryName="Android App">
|
||||
<module name="CESIZen.Wear.app.main" />
|
||||
<option name="DEPLOY" value="true" />
|
||||
<option name="DEPLOY_APK_FROM_BUNDLE" value="false" />
|
||||
<option name="DEPLOY_AS_INSTANT" value="false" />
|
||||
<option name="ARTIFACT_NAME" value="" />
|
||||
<option name="PM_INSTALL_OPTIONS" value="" />
|
||||
<option name="ALL_USERS" value="false" />
|
||||
<option name="ALWAYS_INSTALL_WITH_PM" value="false" />
|
||||
<option name="CLEAR_APP_STORAGE" value="false" />
|
||||
<option name="DYNAMIC_FEATURES_DISABLED_LIST" value="" />
|
||||
<option name="ACTIVITY_EXTRA_FLAGS" value="" />
|
||||
<option name="MODE" value="default_activity" />
|
||||
<option name="CLEAR_LOGCAT" value="false" />
|
||||
<option name="SHOW_LOGCAT_AUTOMATICALLY" value="false" />
|
||||
<option name="INSPECTION_WITHOUT_ACTIVITY_RESTART" value="false" />
|
||||
<option name="TARGET_SELECTION_MODE" value="DEVICE_AND_SNAPSHOT_COMBO_BOX" />
|
||||
<option name="PREFERRED_AVD" value="" />
|
||||
<option name="SELECTED_DEVICE_ID" value="adb-d76a0274-X63ZH9 (2)._adb-tls-connect._tcp" />
|
||||
<option name="SELECTED_CLOUD_MATRIX_CONFIGURATION_ID" value="-1" />
|
||||
<option name="SELECTED_CLOUD_MATRIX_PROJECT_ID" value="" />
|
||||
<option name="DEBUGGER_TYPE" value="Auto" />
|
||||
<Auto>
|
||||
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
||||
<option name="SHOW_STATIC_VARS" value="true" />
|
||||
<option name="WORKING_DIR" value="" />
|
||||
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
||||
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
|
||||
<option name="DEBUG_SANDBOX_SDK" value="false" />
|
||||
</Auto>
|
||||
<Hybrid>
|
||||
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
||||
<option name="SHOW_STATIC_VARS" value="true" />
|
||||
<option name="WORKING_DIR" value="" />
|
||||
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
||||
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
|
||||
<option name="DEBUG_SANDBOX_SDK" value="false" />
|
||||
</Hybrid>
|
||||
<Java>
|
||||
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
|
||||
<option name="DEBUG_SANDBOX_SDK" value="false" />
|
||||
</Java>
|
||||
<Native>
|
||||
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
||||
<option name="SHOW_STATIC_VARS" value="true" />
|
||||
<option name="WORKING_DIR" value="" />
|
||||
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
||||
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
|
||||
<option name="DEBUG_SANDBOX_SDK" value="false" />
|
||||
</Native>
|
||||
<Profilers>
|
||||
<option name="ADVANCED_PROFILING_ENABLED" value="false" />
|
||||
<option name="STARTUP_PROFILING_ENABLED" value="false" />
|
||||
<option name="DETAILED_REPORT_APP_STARTUP_PROFILING_ENABLED" value="false" />
|
||||
</Profilers>
|
||||
<option name="DEEP_LINK" value="" />
|
||||
<option name="ACTIVITY_CLASS" value="" />
|
||||
<option name="SEARCH_ACTIVITY_IN_GLOBAL_SCOPE" value="false" />
|
||||
<option name="SKIP_ACTIVITY_VALIDATION" value="false" />
|
||||
<method v="2">
|
||||
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
|
||||
Generated
+5
-5
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -1,85 +1,85 @@
|
||||
# 🌿 CESIZen - Écosystème de Bien-être Unifié
|
||||
|
||||
CESIZen est une solution complète de gestion du stress et du bien-être, composée d'une plateforme web, d'une application mobile et d'une application pour montre connectée (Wear OS).
|
||||
|
||||
## 🚀 Structure du Projet
|
||||
|
||||
- **/WEB** : API et interface d'administration/diagnostic (Laravel 10, DSFR, Alpine.js).
|
||||
- **/mobile** : Application compagnon pour le suivi et les exercices (Flutter).
|
||||
- **/Wear** : Application de montre pour le tracking rapide de l'humeur (Android Wear OS, Kotlin/Compose).
|
||||
|
||||
---
|
||||
|
||||
## 🛠 Installation & Configuration
|
||||
|
||||
### 1. Backend & Web (Laravel)
|
||||
Le serveur central gère l'authentification (Sanctum) et la persistance des données.
|
||||
|
||||
```bash
|
||||
cd WEB
|
||||
composer install
|
||||
npm install
|
||||
cp .env.example .env # Configurez votre base de données et l'URL du serveur
|
||||
php artisan key:generate
|
||||
php artisan migrate --seed
|
||||
php artisan serve
|
||||
```
|
||||
*Note : Assurez-vous que l'IP de votre serveur est accessible par votre téléphone/émulateur pour la synchronisation.*
|
||||
|
||||
### 2. Application Mobile (Flutter)
|
||||
L'application mobile se connecte à l'API Laravel.
|
||||
|
||||
```bash
|
||||
cd mobile
|
||||
flutter pub get
|
||||
# Modifiez l'URL de l'API dans lib/features/auth/services/auth_service.dart si nécessaire
|
||||
flutter run
|
||||
```
|
||||
|
||||
### 3. Application Montre (Wear OS)
|
||||
La montre communique avec le téléphone via le **Google Wearable Data Layer**.
|
||||
|
||||
1. Ouvrez le dossier `/Wear` dans **Android Studio**.
|
||||
2. Compilez et installez l'APK sur une montre (ou émulateur) Wear OS.
|
||||
3. Assurez-vous que le téléphone appairé possède également l'application mobile CESIZen installée.
|
||||
|
||||
---
|
||||
|
||||
## 📱 Utilisation & Fonctionnalités
|
||||
|
||||
### 🖥️ Plateforme Web
|
||||
- **Tableau de bord** : Visualisation des statistiques de stress.
|
||||
- **Détente interactive** : Module de respiration guidée (méthode DSFR) avec 3 modes (Sommeil, Cohérence, Anxiété).
|
||||
- **Diagnostics** : Historique détaillé des relevés émotionnels.
|
||||
|
||||
### 🤳 Application Mobile
|
||||
- **Authentification** : Connexion sécurisée avec persistance de session.
|
||||
- **Tracker** : Enregistrement quotidien de l'état émotionnel.
|
||||
- **Exercices** : Respiration guidée avec animation synchronisée du logo CESIZen.
|
||||
- **Sync Montre** : Envoi automatique de la date et réception des humeurs capturées sur la montre.
|
||||
|
||||
### ⌚ Application Wear OS
|
||||
- **Quick Mood** : Sélectionnez votre humeur en un clic parmi 5 niveaux.
|
||||
- **Smart Reset** : L'humeur est automatiquement réinitialisée à minuit chaque jour grâce à la synchronisation bidirectionnelle avec le mobile.
|
||||
- **Indicateur visuel** : Confirmation visuelle immédiate de l'enregistrement.
|
||||
|
||||
---
|
||||
|
||||
## 🔗 Synchronisation Bidirectionnelle
|
||||
- **Mobile -> Montre** : Envoie la `date du jour` pour synchroniser le cycle quotidien.
|
||||
- **Montre -> Mobile** : Envoie l'émotion sélectionnée via le `Data Layer` (`/daily_mood`).
|
||||
- **Mobile -> Web** : Synchronise les données finales vers la base de données Laravel pour analyse.
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Identité Visuelle
|
||||
L'ensemble de l'écosystème utilise la charte graphique **CesiZen** :
|
||||
- **Couleur Primaire** : Bleu Marine (`#000080`)
|
||||
- **Couleur Accent** : Orange (`#FFCC80`)
|
||||
- **Typographie** : Marianne (standard DSFR)
|
||||
- **Logo** : Point central de l'animation de respiration.
|
||||
|
||||
---
|
||||
|
||||
## 📄 Licence
|
||||
Projet réalisé dans le cadre de la formation CDA CESI.
|
||||
# 🌿 CESIZen - Écosystème de Bien-être Unifié
|
||||
|
||||
CESIZen est une solution complète de gestion du stress et du bien-être, composée d'une plateforme web, d'une application mobile et d'une application pour montre connectée (Wear OS).
|
||||
|
||||
## 🚀 Structure du Projet
|
||||
|
||||
- **/WEB** : API et interface d'administration/diagnostic (Laravel 10, DSFR, Alpine.js).
|
||||
- **/mobile** : Application compagnon pour le suivi et les exercices (Flutter).
|
||||
- **/Wear** : Application de montre pour le tracking rapide de l'humeur (Android Wear OS, Kotlin/Compose).
|
||||
|
||||
---
|
||||
|
||||
## 🛠 Installation & Configuration
|
||||
|
||||
### 1. Backend & Web (Laravel)
|
||||
Le serveur central gère l'authentification (Sanctum) et la persistance des données.
|
||||
|
||||
```bash
|
||||
cd WEB
|
||||
composer install
|
||||
npm install
|
||||
cp .env.example .env # Configurez votre base de données et l'URL du serveur
|
||||
php artisan key:generate
|
||||
php artisan migrate --seed
|
||||
php artisan serve
|
||||
```
|
||||
*Note : Assurez-vous que l'IP de votre serveur est accessible par votre téléphone/émulateur pour la synchronisation.*
|
||||
|
||||
### 2. Application Mobile (Flutter)
|
||||
L'application mobile se connecte à l'API Laravel.
|
||||
|
||||
```bash
|
||||
cd mobile
|
||||
flutter pub get
|
||||
# Modifiez l'URL de l'API dans lib/features/auth/services/auth_service.dart si nécessaire
|
||||
flutter run
|
||||
```
|
||||
|
||||
### 3. Application Montre (Wear OS)
|
||||
La montre communique avec le téléphone via le **Google Wearable Data Layer**.
|
||||
|
||||
1. Ouvrez le dossier `/Wear` dans **Android Studio**.
|
||||
2. Compilez et installez l'APK sur une montre (ou émulateur) Wear OS.
|
||||
3. Assurez-vous que le téléphone appairé possède également l'application mobile CESIZen installée.
|
||||
|
||||
---
|
||||
|
||||
## 📱 Utilisation & Fonctionnalités
|
||||
|
||||
### 🖥️ Plateforme Web
|
||||
- **Tableau de bord** : Visualisation des statistiques de stress.
|
||||
- **Détente interactive** : Module de respiration guidée (méthode DSFR) avec 3 modes (Sommeil, Cohérence, Anxiété).
|
||||
- **Diagnostics** : Historique détaillé des relevés émotionnels.
|
||||
|
||||
### 🤳 Application Mobile
|
||||
- **Authentification** : Connexion sécurisée avec persistance de session.
|
||||
- **Tracker** : Enregistrement quotidien de l'état émotionnel.
|
||||
- **Exercices** : Respiration guidée avec animation synchronisée du logo CESIZen.
|
||||
- **Sync Montre** : Envoi automatique de la date et réception des humeurs capturées sur la montre.
|
||||
|
||||
### ⌚ Application Wear OS
|
||||
- **Quick Mood** : Sélectionnez votre humeur en un clic parmi 5 niveaux.
|
||||
- **Smart Reset** : L'humeur est automatiquement réinitialisée à minuit chaque jour grâce à la synchronisation bidirectionnelle avec le mobile.
|
||||
- **Indicateur visuel** : Confirmation visuelle immédiate de l'enregistrement.
|
||||
|
||||
---
|
||||
|
||||
## 🔗 Synchronisation Bidirectionnelle
|
||||
- **Mobile -> Montre** : Envoie la `date du jour` pour synchroniser le cycle quotidien.
|
||||
- **Montre -> Mobile** : Envoie l'émotion sélectionnée via le `Data Layer` (`/daily_mood`).
|
||||
- **Mobile -> Web** : Synchronise les données finales vers la base de données Laravel pour analyse.
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Identité Visuelle
|
||||
L'ensemble de l'écosystème utilise la charte graphique **CesiZen** :
|
||||
- **Couleur Primaire** : Bleu Marine (`#000080`)
|
||||
- **Couleur Accent** : Orange (`#FFCC80`)
|
||||
- **Typographie** : Marianne (standard DSFR)
|
||||
- **Logo** : Point central de l'animation de respiration.
|
||||
|
||||
---
|
||||
|
||||
## 📄 Licence
|
||||
Projet réalisé dans le cadre de la formation CDA CESI.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,5 @@
|
||||
node_modules
|
||||
vendor
|
||||
public/build
|
||||
.git
|
||||
.env
|
||||
@@ -0,0 +1,51 @@
|
||||
FROM php:8.3-fpm
|
||||
|
||||
# 1. Installer les dépendances système
|
||||
RUN apt-get update && apt-get install -y \
|
||||
git \
|
||||
curl \
|
||||
libpng-dev \
|
||||
libonig-dev \
|
||||
libxml2-dev \
|
||||
libicu-dev \
|
||||
libzip-dev \
|
||||
zip \
|
||||
unzip \
|
||||
gnupg
|
||||
|
||||
# 2. Installer Node.js (pour Vite)
|
||||
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \
|
||||
apt-get install -y nodejs
|
||||
|
||||
# 3. Nettoyer le cache apt
|
||||
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 4. Installer les extensions PHP
|
||||
# On ajoute 'zip' et on configure 'intl' correctement
|
||||
RUN docker-php-ext-configure intl && \
|
||||
docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd intl zip
|
||||
|
||||
# 5. Installer Composer
|
||||
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
|
||||
|
||||
# 6. Définir le répertoire de travail
|
||||
WORKDIR /var/www
|
||||
|
||||
# 7. Copier le projet
|
||||
COPY . .
|
||||
|
||||
# 8. Installer les dépendances PHP et Node
|
||||
RUN composer install --no-interaction --optimize-autoloader --no-dev
|
||||
RUN npm install
|
||||
|
||||
# 9. Forcer les permissions sur les binaires node_modules
|
||||
RUN chmod -R +x node_modules/.bin/
|
||||
|
||||
# 10. Lancer le build de Vite
|
||||
RUN npm run build
|
||||
|
||||
# 11. Fixer les permissions pour Laravel
|
||||
RUN chown -R www-data:www-data /var/www/storage /var/www/bootstrap/cache
|
||||
|
||||
EXPOSE 9000
|
||||
CMD ["php-fpm"]
|
||||
+37
-53
@@ -1,59 +1,43 @@
|
||||
<p align="center"><a href="https://laravel.com" target="_blank"><img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="400" alt="Laravel Logo"></a></p>
|
||||
# 🖥️ CESIZen - Plateforme Web & API
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/laravel/framework/actions"><img src="https://github.com/laravel/framework/workflows/tests/badge.svg" alt="Build Status"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
|
||||
</p>
|
||||
Ce répertoire contient le backend Laravel, l'API REST sécurisée par Sanctum, et le tableau de bord d'administration utilisant le Design System de l'État Français (DSFR).
|
||||
|
||||
## About Laravel
|
||||
## 🛠️ Guide d'installation
|
||||
|
||||
Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:
|
||||
### Prérequis
|
||||
- Docker et Docker Compose
|
||||
- Composer (optionnel si installation via Docker)
|
||||
- Node.js & NPM
|
||||
|
||||
- [Simple, fast routing engine](https://laravel.com/docs/routing).
|
||||
- [Powerful dependency injection container](https://laravel.com/docs/container).
|
||||
- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
|
||||
- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
|
||||
- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
|
||||
- [Robust background job processing](https://laravel.com/docs/queues).
|
||||
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
|
||||
### Étapes d'installation
|
||||
1. **Cloner le projet** et entrer dans le dossier `WEB`.
|
||||
2. **Configuration de l'environnement** :
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
3. **Lancer les conteneurs Docker** :
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
4. **Installer les dépendances PHP & JS** :
|
||||
```bash
|
||||
docker exec cesizen-app composer install
|
||||
docker exec cesizen-app npm install
|
||||
docker exec cesizen-app npm run build
|
||||
```
|
||||
5. **Initialiser la base de données** :
|
||||
```bash
|
||||
docker exec cesizen-app php artisan key:generate
|
||||
docker exec cesizen-app php artisan migrate --seed
|
||||
```
|
||||
6. **Accès** :
|
||||
- Web : `http://localhost:8090`
|
||||
- API : `http://localhost:8091`
|
||||
|
||||
Laravel is accessible, powerful, and provides tools required for large, robust applications.
|
||||
---
|
||||
|
||||
## Learning Laravel
|
||||
|
||||
Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework. You can also check out [Laravel Learn](https://laravel.com/learn), where you will be guided through building a modern Laravel application.
|
||||
|
||||
If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.
|
||||
|
||||
## Laravel Sponsors
|
||||
|
||||
We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the [Laravel Partners program](https://partners.laravel.com).
|
||||
|
||||
### Premium Partners
|
||||
|
||||
- **[Vehikl](https://vehikl.com)**
|
||||
- **[Tighten Co.](https://tighten.co)**
|
||||
- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)**
|
||||
- **[64 Robots](https://64robots.com)**
|
||||
- **[Curotec](https://www.curotec.com/services/technologies/laravel)**
|
||||
- **[DevSquad](https://devsquad.com/hire-laravel-developers)**
|
||||
- **[Redberry](https://redberry.international/laravel-development)**
|
||||
- **[Active Logic](https://activelogic.com)**
|
||||
|
||||
## Contributing
|
||||
|
||||
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
|
||||
|
||||
## Security Vulnerabilities
|
||||
|
||||
If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed.
|
||||
|
||||
## License
|
||||
|
||||
The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
|
||||
## 🏗️ Architecture Technique (API)
|
||||
- **Framework** : Laravel 10 (PHP 8.2)
|
||||
- **Auth** : Laravel Sanctum (Tokens Bearer)
|
||||
- **Admin** : FilamentPHP / DSFR
|
||||
- **Base de données** : MySQL 8.0
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources;
|
||||
|
||||
use App\Filament\Admin\Resources\InformationResource\Pages;
|
||||
use App\Models\Information;
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
|
||||
class InformationResource extends Resource
|
||||
{
|
||||
protected static ?string $model = Information::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'heroicon-o-information-circle';
|
||||
|
||||
protected static ?string $navigationLabel = 'Informations';
|
||||
|
||||
protected static ?string $modelLabel = 'Information';
|
||||
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
Forms\Components\TextInput::make('title')
|
||||
->label('Titre')
|
||||
->required()
|
||||
->maxLength(255),
|
||||
Forms\Components\TextInput::make('category')
|
||||
->label('Catégorie')
|
||||
->required()
|
||||
->maxLength(255)
|
||||
->default('Général'),
|
||||
Forms\Components\RichEditor::make('content')
|
||||
->label('Contenu')
|
||||
->required()
|
||||
->columnSpanFull(),
|
||||
Forms\Components\FileUpload::make('image_url')
|
||||
->label('Image')
|
||||
->image()
|
||||
->disk('public') // Ajoutez ceci pour forcer l'utilisation du disque public
|
||||
->directory('informations')
|
||||
->visibility('public'),
|
||||
Forms\Components\Toggle::make('is_published')
|
||||
->label('Publié')
|
||||
->default(true)
|
||||
->required(),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
Tables\Columns\TextColumn::make('title')
|
||||
->label('Titre')
|
||||
->searchable(),
|
||||
Tables\Columns\TextColumn::make('category')
|
||||
->label('Catégorie')
|
||||
->badge()
|
||||
->searchable(),
|
||||
Tables\Columns\IconColumn::make('is_published')
|
||||
->label('Publié')
|
||||
->boolean(),
|
||||
Tables\Columns\TextColumn::make('created_at')
|
||||
->label('Créé le')
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
])
|
||||
->filters([
|
||||
Tables\Filters\TernaryFilter::make('is_published')
|
||||
->label('Statut de publication')
|
||||
->boolean()
|
||||
->trueLabel('Publiés')
|
||||
->falseLabel('Brouillons')
|
||||
->native(false),
|
||||
])
|
||||
->actions([
|
||||
Tables\Actions\EditAction::make(),
|
||||
Tables\Actions\DeleteAction::make(),
|
||||
])
|
||||
->bulkActions([
|
||||
Tables\Actions\BulkActionGroup::make([
|
||||
Tables\Actions\DeleteBulkAction::make(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ListInformation::route('/'),
|
||||
'create' => Pages\CreateInformation::route('/create'),
|
||||
'edit' => Pages\EditInformation::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\InformationResource\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\InformationResource;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
|
||||
class CreateInformation extends CreateRecord
|
||||
{
|
||||
protected static string $resource = InformationResource::class;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\InformationResource\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\InformationResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditInformation extends EditRecord
|
||||
{
|
||||
protected static string $resource = InformationResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\DeleteAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\InformationResource\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\InformationResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListInformation extends ListRecords
|
||||
{
|
||||
protected static string $resource = InformationResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources;
|
||||
|
||||
use App\Filament\Admin\Resources\RelaxationActivityResource\Pages;
|
||||
use App\Models\RelaxationActivity;
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
|
||||
class RelaxationActivityResource extends Resource
|
||||
{
|
||||
protected static ?string $model = RelaxationActivity::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'heroicon-o-sparkles';
|
||||
|
||||
protected static ?string $navigationLabel = 'Activités de détente';
|
||||
|
||||
protected static ?string $modelLabel = 'Activité de détente';
|
||||
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
Forms\Components\TextInput::make('title')
|
||||
->label('Titre')
|
||||
->required()
|
||||
->maxLength(255),
|
||||
Forms\Components\Select::make('type')
|
||||
->options([
|
||||
'video' => 'Vidéo',
|
||||
'audio' => 'Audio',
|
||||
'article' => 'Article',
|
||||
'exercice' => 'Exercice',
|
||||
])
|
||||
->required(),
|
||||
Forms\Components\TextInput::make('duration')
|
||||
->label('Durée (min)')
|
||||
->numeric(),
|
||||
Forms\Components\TextInput::make('url')
|
||||
->label('Lien / URL')
|
||||
->url()
|
||||
->maxLength(255),
|
||||
Forms\Components\Textarea::make('description')
|
||||
->columnSpanFull(),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
Tables\Columns\TextColumn::make('title')
|
||||
->label('Titre')
|
||||
->searchable(),
|
||||
Tables\Columns\TextColumn::make('type')
|
||||
->badge(),
|
||||
Tables\Columns\TextColumn::make('duration')
|
||||
->label('Durée')
|
||||
->suffix(' min'),
|
||||
Tables\Columns\TextColumn::make('created_at')
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
])
|
||||
->filters([
|
||||
Tables\Filters\SelectFilter::make('type')
|
||||
->options([
|
||||
'video' => 'Vidéo',
|
||||
'audio' => 'Audio',
|
||||
'article' => 'Article',
|
||||
'exercice' => 'Exercice',
|
||||
]),
|
||||
])
|
||||
->actions([
|
||||
Tables\Actions\EditAction::make(),
|
||||
Tables\Actions\DeleteAction::make(),
|
||||
])
|
||||
->bulkActions([
|
||||
Tables\Actions\BulkActionGroup::make([
|
||||
Tables\Actions\DeleteBulkAction::make(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getRelations(): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ListRelaxationActivities::route('/'),
|
||||
'create' => Pages\CreateRelaxationActivity::route('/create'),
|
||||
'edit' => Pages\EditRelaxationActivity::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\RelaxationActivityResource\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\RelaxationActivityResource;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
|
||||
class CreateRelaxationActivity extends CreateRecord
|
||||
{
|
||||
protected static string $resource = RelaxationActivityResource::class;
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\RelaxationActivityResource\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\RelaxationActivityResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditRelaxationActivity extends EditRecord
|
||||
{
|
||||
protected static string $resource = RelaxationActivityResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\DeleteAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\RelaxationActivityResource\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\RelaxationActivityResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListRelaxationActivities extends ListRecords
|
||||
{
|
||||
protected static string $resource = RelaxationActivityResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources;
|
||||
|
||||
use App\Filament\Admin\Resources\StressEventResource\Pages;
|
||||
use App\Models\StressEvent;
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
|
||||
class StressEventResource extends Resource
|
||||
{
|
||||
protected static ?string $model = StressEvent::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'heroicon-o-bolt';
|
||||
|
||||
protected static ?string $navigationLabel = 'Événements de stress';
|
||||
|
||||
protected static ?string $modelLabel = 'Événement de stress';
|
||||
|
||||
protected static ?string $pluralModelLabel = 'Événements de stress';
|
||||
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
Forms\Components\TextInput::make('event_name')
|
||||
->label('Nom de l\'événement')
|
||||
->required()
|
||||
->maxLength(255),
|
||||
Forms\Components\TextInput::make('points')
|
||||
->label('Points de stress')
|
||||
->required()
|
||||
->numeric()
|
||||
->minValue(0)
|
||||
->maxValue(100),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
Tables\Columns\TextColumn::make('event_name')
|
||||
->label('Événement')
|
||||
->searchable()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('points')
|
||||
->label('Points')
|
||||
->numeric()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('updated_at')
|
||||
->label('Dernière modification')
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
])
|
||||
->filters([
|
||||
//
|
||||
])
|
||||
->actions([
|
||||
Tables\Actions\EditAction::make(),
|
||||
Tables\Actions\DeleteAction::make(),
|
||||
])
|
||||
->bulkActions([
|
||||
Tables\Actions\BulkActionGroup::make([
|
||||
Tables\Actions\DeleteBulkAction::make(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getRelations(): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ListStressEvents::route('/'),
|
||||
'create' => Pages\CreateStressEvent::route('/create'),
|
||||
'edit' => Pages\EditStressEvent::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\StressEventResource\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\StressEventResource;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
|
||||
class CreateStressEvent extends CreateRecord
|
||||
{
|
||||
protected static string $resource = StressEventResource::class;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\StressEventResource\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\StressEventResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditStressEvent extends EditRecord
|
||||
{
|
||||
protected static string $resource = StressEventResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\DeleteAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\StressEventResource\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\StressEventResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListStressEvents extends ListRecords
|
||||
{
|
||||
protected static string $resource = StressEventResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources;
|
||||
|
||||
use App\Filament\Admin\Resources\UserResource\Pages;
|
||||
use App\Models\User;
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
|
||||
class UserResource extends Resource
|
||||
{
|
||||
protected static ?string $model = User::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'heroicon-o-users';
|
||||
|
||||
protected static ?string $navigationLabel = 'Utilisateurs';
|
||||
|
||||
protected static ?string $modelLabel = 'Utilisateur';
|
||||
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
Forms\Components\TextInput::make('name')
|
||||
->required()
|
||||
->maxLength(255),
|
||||
Forms\Components\TextInput::make('email')
|
||||
->email()
|
||||
->required()
|
||||
->maxLength(255),
|
||||
Forms\Components\Select::make('id_role')
|
||||
->relationship('role', 'libelle')
|
||||
->required(),
|
||||
Forms\Components\TextInput::make('password')
|
||||
->password()
|
||||
->dehydrated(fn ($state) => filled($state))
|
||||
->required(fn (string $context): bool => $context === 'create'),
|
||||
Forms\Components\Toggle::make('is_active')
|
||||
->label('Compte actif')
|
||||
->default(true)
|
||||
->required(),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
Tables\Columns\TextColumn::make('name')
|
||||
->searchable(),
|
||||
Tables\Columns\TextColumn::make('email')
|
||||
->searchable(),
|
||||
Tables\Columns\TextColumn::make('role.libelle')
|
||||
->label('Rôle')
|
||||
->badge()
|
||||
->color(fn (string $state): string => match ($state) {
|
||||
'Admin' => 'danger',
|
||||
'Utilisateur' => 'info',
|
||||
default => 'gray',
|
||||
}),
|
||||
Tables\Columns\ToggleColumn::make('is_active')
|
||||
->label('Actif'),
|
||||
Tables\Columns\TextColumn::make('created_at')
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
])
|
||||
->filters([
|
||||
Tables\Filters\TernaryFilter::make('is_active')
|
||||
->label('Statut')
|
||||
->boolean()
|
||||
->trueLabel('Utilisateurs actifs')
|
||||
->falseLabel('Utilisateurs désactivés')
|
||||
->native(false),
|
||||
])
|
||||
->actions([
|
||||
Tables\Actions\EditAction::make(),
|
||||
])
|
||||
->bulkActions([
|
||||
Tables\Actions\BulkActionGroup::make([
|
||||
Tables\Actions\DeleteBulkAction::make(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getRelations(): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ListUsers::route('/'),
|
||||
'create' => Pages\CreateUser::route('/create'),
|
||||
'edit' => Pages\EditUser::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\UserResource\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\UserResource;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
|
||||
class CreateUser extends CreateRecord
|
||||
{
|
||||
protected static string $resource = UserResource::class;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\UserResource\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\UserResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditUser extends EditRecord
|
||||
{
|
||||
protected static string $resource = UserResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\DeleteAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\UserResource\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\UserResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListUsers extends ListRecords
|
||||
{
|
||||
protected static string $resource = UserResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,14 @@ class AuthController extends Controller
|
||||
], 401);
|
||||
}
|
||||
|
||||
// Vérification si le compte est actif
|
||||
if (!$user->is_active) {
|
||||
return response([
|
||||
'status' => 'error',
|
||||
'message' => 'Votre compte a été désactivé par un administrateur.'
|
||||
], 403);
|
||||
}
|
||||
|
||||
// Log::info('Email reçu: ' . $request->email);
|
||||
// Log::info('Password saisi: ' . $request->password);
|
||||
// Log::info('Hachage en BDD: ' . $user->password);
|
||||
@@ -45,7 +53,7 @@ class AuthController extends Controller
|
||||
], 200);
|
||||
}
|
||||
|
||||
public function register(Request $request)
|
||||
public function register(Request $request)
|
||||
{
|
||||
// 1. Validation des données (Standard Qualité)
|
||||
$validator = Validator::make($request->all(), [
|
||||
@@ -54,7 +62,7 @@ class AuthController extends Controller
|
||||
'password' => 'required|string|min:8|confirmed',
|
||||
]);
|
||||
|
||||
|
||||
|
||||
Log::info('Tentative de création', $request->all());
|
||||
|
||||
if ($validator->fails()) {
|
||||
@@ -90,8 +98,8 @@ class AuthController extends Controller
|
||||
$request->validate([
|
||||
'current_password' => 'required',
|
||||
'email' => [
|
||||
'sometimes',
|
||||
'email',
|
||||
'sometimes',
|
||||
'email',
|
||||
Rule::unique('users')->ignore($user->id) // Empêche les doublons en BDD
|
||||
],
|
||||
]);
|
||||
@@ -99,7 +107,7 @@ class AuthController extends Controller
|
||||
// 2. Vérification de sécurité (RGPD/Confidentialité)
|
||||
if (!Hash::check($request->current_password, $user->password)) {
|
||||
return response()->json([
|
||||
'status' => 'error',
|
||||
'status' => 'error',
|
||||
'message' => 'Le mot de passe actuel est incorrect'
|
||||
], 403);
|
||||
}
|
||||
@@ -114,4 +122,4 @@ class AuthController extends Controller
|
||||
'user' => $user
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Information;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class InformationController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$informations = Information::where('is_published', true)
|
||||
->orderBy('created_at', 'desc')
|
||||
->get();
|
||||
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'data' => $informations
|
||||
]);
|
||||
}
|
||||
|
||||
public function show($id)
|
||||
{
|
||||
$information = Information::where('is_published', true)->find($id);
|
||||
|
||||
if (!$information) {
|
||||
return response()->json([
|
||||
'status' => 'error',
|
||||
'message' => 'Information non trouvée'
|
||||
], 404);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'data' => $information
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -30,20 +30,16 @@ class DiagnosticController extends Controller
|
||||
return view('diagnostics.history', compact('history'));
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
public function store(Request $request, \App\Services\StressCalculator $calculator)
|
||||
{
|
||||
// Logique pour calculer le score Holmes-Rahe
|
||||
$totalPoints = 0;
|
||||
if ($request->has('events')) {
|
||||
$totalPoints = StressEvent::whereIn('id', $request->events)->sum('points');
|
||||
$points = StressEvent::whereIn('id', $request->events)->pluck('points')->toArray();
|
||||
$totalPoints = $calculator->calculateScore($points);
|
||||
}
|
||||
|
||||
$niveauStress = 'Faible';
|
||||
if ($totalPoints >= 300) {
|
||||
$niveauStress = 'Élevé';
|
||||
} elseif ($totalPoints >= 150) {
|
||||
$niveauStress = 'Modéré';
|
||||
}
|
||||
$niveauStress = $calculator->determineLevel($totalPoints);
|
||||
|
||||
// Enregistrement si l'utilisateur est connecté
|
||||
if (Auth::check()) {
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Information;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class InformationController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$query = Information::where('is_published', true);
|
||||
|
||||
if ($request->has('category') && $request->category != '') {
|
||||
$query->where('category', $request->category);
|
||||
}
|
||||
|
||||
$informations = $query->orderBy('created_at', 'desc')
|
||||
->paginate(6)
|
||||
->withQueryString();
|
||||
|
||||
$categories = Information::where('is_published', true)
|
||||
->distinct()
|
||||
->pluck('category');
|
||||
|
||||
return view('informations.index', compact('informations', 'categories'));
|
||||
}
|
||||
|
||||
public function show($id)
|
||||
{
|
||||
$information = Information::where('is_published', true)->findOrFail($id);
|
||||
|
||||
return view('informations.show', compact('information'));
|
||||
}
|
||||
}
|
||||
@@ -10,11 +10,21 @@ use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class RelaxationController extends Controller
|
||||
{
|
||||
public function index()
|
||||
public function index(Request $request)
|
||||
{
|
||||
$activities = RelaxationActivity::all();
|
||||
$favorites = [];
|
||||
return view('relaxation.index', compact('activities', 'favorites'));
|
||||
$query = RelaxationActivity::query();
|
||||
|
||||
if ($request->has('category') && $request->category != '') {
|
||||
$query->where('category', $request->category);
|
||||
}
|
||||
|
||||
$activities = $query->get();
|
||||
|
||||
$categories = RelaxationActivity::distinct()->pluck('category');
|
||||
|
||||
$favorites = []; // TODO: Implémenter la logique des favoris si nécessaire
|
||||
|
||||
return view('relaxation.index', compact('activities', 'favorites', 'categories'));
|
||||
}
|
||||
|
||||
public function toggleFavorite($id)
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Information extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'information';
|
||||
|
||||
protected $fillable = [
|
||||
'title',
|
||||
'content',
|
||||
'category',
|
||||
'image_url',
|
||||
'is_published',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'is_published' => 'boolean',
|
||||
];
|
||||
}
|
||||
@@ -2,9 +2,15 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class StressEvent extends Model
|
||||
{
|
||||
//
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'event_name',
|
||||
'points',
|
||||
];
|
||||
}
|
||||
|
||||
+22
-23
@@ -2,61 +2,60 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Database\Factories\UserFactory;
|
||||
use Filament\Models\Contracts\FilamentUser;
|
||||
use Filament\Panel;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Laravel\Sanctum\HasApiTokens;
|
||||
|
||||
class User extends Authenticatable
|
||||
class User extends Authenticatable implements FilamentUser
|
||||
{
|
||||
/** @use HasFactory<UserFactory> */
|
||||
use HasFactory, Notifiable;
|
||||
use HasApiTokens, HasFactory, Notifiable;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'email',
|
||||
'password',
|
||||
'id_role',
|
||||
'is_active',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be hidden for serialization.
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
protected $hidden = [
|
||||
'password',
|
||||
'remember_token',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the attributes that should be cast.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'email_verified_at' => 'datetime',
|
||||
'password' => 'hashed',
|
||||
'is_active' => 'boolean',
|
||||
];
|
||||
}
|
||||
|
||||
public function emotionRecords()
|
||||
public function role(): BelongsTo
|
||||
{
|
||||
return $this->hasMany(EmotionRecord::class);
|
||||
return $this->belongsTo(Role::class, 'id_role');
|
||||
}
|
||||
|
||||
public function favoritedBy()
|
||||
public function emotionRecords(): HasMany
|
||||
{
|
||||
return $this->hasMany(EmotionRecord::class, 'user_id');
|
||||
}
|
||||
|
||||
public function favoritedActivities(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(RelaxationActivity::class, 'favorites', 'id_user', 'id_activite');
|
||||
}
|
||||
|
||||
public function canAccessPanel(Panel $panel): bool
|
||||
{
|
||||
// Autorise l'accès si l'utilisateur est actif et a le rôle 'Admin'
|
||||
return $this->is_active && $this->role && $this->role->libelle === 'Admin';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ class AppServiceProvider extends ServiceProvider
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
//
|
||||
if (config('app.env') === 'production' || config('app.env') === 'local') {
|
||||
\Illuminate\Support\Facades\URL::forceScheme('https');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers\Filament;
|
||||
|
||||
use Filament\Http\Middleware\Authenticate;
|
||||
use Filament\Http\Middleware\AuthenticateSession;
|
||||
use Filament\Http\Middleware\DisableBladeIconComponents;
|
||||
use Filament\Http\Middleware\DispatchServingFilamentEvent;
|
||||
use Filament\Pages;
|
||||
use Filament\Panel;
|
||||
use Filament\PanelProvider;
|
||||
use Filament\Support\Colors\Color;
|
||||
use Filament\Widgets;
|
||||
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
|
||||
use Illuminate\Cookie\Middleware\EncryptCookies;
|
||||
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
|
||||
use Illuminate\Routing\Middleware\SubstituteBindings;
|
||||
use Illuminate\Session\Middleware\StartSession;
|
||||
use Illuminate\View\Middleware\ShareErrorsFromSession;
|
||||
|
||||
class AdminPanelProvider extends PanelProvider
|
||||
{
|
||||
public function panel(Panel $panel): Panel
|
||||
{
|
||||
return $panel
|
||||
->id('admin')
|
||||
->path('admin')
|
||||
->brandName('CESIZen')
|
||||
->homeUrl('/')
|
||||
->colors([
|
||||
'primary' => Color::Amber,
|
||||
])
|
||||
->discoverResources(in: app_path('Filament/Admin/Resources'), for: 'App\\Filament\\Admin\\Resources')
|
||||
->discoverPages(in: app_path('Filament/Admin/Pages'), for: 'App\\Filament\\Admin\\Pages')
|
||||
->pages([
|
||||
Pages\Dashboard::class,
|
||||
])
|
||||
->discoverWidgets(in: app_path('Filament/Admin/Widgets'), for: 'App\\Filament\\Admin\\Widgets')
|
||||
->widgets([
|
||||
Widgets\AccountWidget::class,
|
||||
Widgets\FilamentInfoWidget::class,
|
||||
])
|
||||
->middleware([
|
||||
EncryptCookies::class,
|
||||
AddQueuedCookiesToResponse::class,
|
||||
StartSession::class,
|
||||
AuthenticateSession::class,
|
||||
ShareErrorsFromSession::class,
|
||||
VerifyCsrfToken::class,
|
||||
SubstituteBindings::class,
|
||||
DisableBladeIconComponents::class,
|
||||
DispatchServingFilamentEvent::class,
|
||||
])
|
||||
->authMiddleware([
|
||||
Authenticate::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
class StressCalculator
|
||||
{
|
||||
public const LEVEL_HIGH = 'Élevé';
|
||||
public const LEVEL_MODERATE = 'Modéré';
|
||||
public const LEVEL_LOW = 'Faible';
|
||||
|
||||
public function calculateScore(array $points): int
|
||||
{
|
||||
return array_sum($points);
|
||||
}
|
||||
|
||||
public function determineLevel(int $score): string
|
||||
{
|
||||
if ($score >= 300) {
|
||||
return self::LEVEL_HIGH;
|
||||
}
|
||||
|
||||
if ($score >= 150) {
|
||||
return self::LEVEL_MODERATE;
|
||||
}
|
||||
|
||||
return self::LEVEL_LOW;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
<?php
|
||||
|
||||
use App\Providers\AppServiceProvider;
|
||||
|
||||
return [
|
||||
AppServiceProvider::class,
|
||||
App\Providers\AppServiceProvider::class,
|
||||
App\Providers\Filament\AdminPanelProvider::class,
|
||||
];
|
||||
|
||||
+4
-1
@@ -8,6 +8,7 @@
|
||||
"require": {
|
||||
"php": "^8.2",
|
||||
"blade-ui-kit/blade-heroicons": "^2.7",
|
||||
"filament/filament": "^3.2",
|
||||
"guzzlehttp/guzzle": "^7.10",
|
||||
"laravel/framework": "^12.0",
|
||||
"laravel/sanctum": "^4.0",
|
||||
@@ -53,7 +54,8 @@
|
||||
],
|
||||
"post-autoload-dump": [
|
||||
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
|
||||
"@php artisan package:discover --ansi"
|
||||
"@php artisan package:discover --ansi",
|
||||
"@php artisan filament:upgrade"
|
||||
],
|
||||
"post-update-cmd": [
|
||||
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
|
||||
@@ -79,6 +81,7 @@
|
||||
"optimize-autoloader": true,
|
||||
"preferred-install": "dist",
|
||||
"sort-packages": true,
|
||||
"platform-check": false,
|
||||
"allow-plugins": {
|
||||
"pestphp/pest-plugin": true,
|
||||
"php-http/discovery": true
|
||||
|
||||
Generated
+1692
-158
File diff suppressed because it is too large
Load Diff
@@ -30,6 +30,7 @@ class UserFactory extends Factory
|
||||
'email_verified_at' => now(),
|
||||
'password' => static::$password ??= Hash::make('password'),
|
||||
'remember_token' => Str::random(10),
|
||||
'id_role' => \App\Models\Role::first() ?? \App\Models\Role::create(['libelle' => 'Utilisateur']),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('personal_access_tokens', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->morphs('tokenable');
|
||||
$table->string('name');
|
||||
$table->string('token', 64)->unique();
|
||||
$table->text('abilities')->nullable();
|
||||
$table->timestamp('last_used_at')->nullable();
|
||||
$table->timestamp('expires_at')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('personal_access_tokens');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('cache', function (Blueprint $table) {
|
||||
$table->string('key')->primary();
|
||||
$table->mediumText('value');
|
||||
$table->bigInteger('expiration')->index();
|
||||
});
|
||||
|
||||
Schema::create('cache_locks', function (Blueprint $table) {
|
||||
$table->string('key')->primary();
|
||||
$table->string('owner');
|
||||
$table->bigInteger('expiration')->index();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('cache');
|
||||
Schema::dropIfExists('cache_locks');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('sessions', function (Blueprint $table) {
|
||||
$table->string('id')->primary();
|
||||
$table->foreignId('user_id')->nullable()->index();
|
||||
$table->string('ip_address', 45)->nullable();
|
||||
$table->text('user_agent')->nullable();
|
||||
$table->longText('payload');
|
||||
$table->integer('last_activity')->index();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('sessions');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
//
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
//
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->boolean('is_active')->default(true)->after('id_role');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('is_active');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('information', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('title');
|
||||
$table->text('content');
|
||||
$table->string('category')->default('Général'); // ex: Prévention, Stress, Sommeil
|
||||
$table->string('image_url')->nullable();
|
||||
$table->boolean('is_published')->default(true);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('information');
|
||||
}
|
||||
};
|
||||
@@ -26,11 +26,13 @@ class DatabaseSeeder extends Seeder
|
||||
|
||||
$adminRole = Role::where('libelle', 'Admin')->first();
|
||||
|
||||
User::create([
|
||||
'name' => 'AdminCESI',
|
||||
'email' => 'admin@cesizen.fr',
|
||||
'password' => Hash::make('okokokok'), // Toujours hacher !
|
||||
'id_role' => $adminRole->id,
|
||||
]);
|
||||
User::firstOrCreate(
|
||||
['email' => 'admin@cesizen.fr'],
|
||||
[
|
||||
'name' => 'AdminCESI',
|
||||
'password' => Hash::make('okokokok'),
|
||||
'id_role' => $adminRole->id,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
services:
|
||||
# PHP Application
|
||||
app:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
container_name: cesizen-app
|
||||
restart: unless-stopped
|
||||
tty: true
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
volumes:
|
||||
- ./:/var/www
|
||||
- /var/www/node_modules
|
||||
- ./storage:/var/www/storage
|
||||
networks:
|
||||
- cesizen-network
|
||||
|
||||
# Nginx Service
|
||||
webserver:
|
||||
image: nginx:alpine
|
||||
container_name: cesizen-webserver
|
||||
restart: unless-stopped
|
||||
tty: true
|
||||
ports:
|
||||
- "8090:80" # WEB
|
||||
- "8091:81" # API
|
||||
volumes:
|
||||
- ./:/var/www
|
||||
- ./nginx.conf:/etc/nginx/conf.d/default.conf
|
||||
networks:
|
||||
- cesizen-network
|
||||
|
||||
networks:
|
||||
cesizen-network:
|
||||
driver: bridge
|
||||
@@ -0,0 +1,51 @@
|
||||
# Configuration pour le Web (Port 80 interne, mappé sur 8090)
|
||||
server {
|
||||
listen 80;
|
||||
server_name _; # Accepte tout car NPM a déjà filtré le domaine
|
||||
index index.php index.html;
|
||||
root /var/www/public;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php?$query_string;
|
||||
}
|
||||
|
||||
location ~ \.php$ {
|
||||
try_files $uri =404;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass app:9000;
|
||||
fastcgi_index index.php;
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||
|
||||
# Optimisation pour le proxy
|
||||
fastcgi_param HTTP_X_FORWARDED_FOR $http_x_forwarded_for;
|
||||
fastcgi_param HTTP_X_FORWARDED_PROTO $http_x_forwarded_proto;
|
||||
}
|
||||
}
|
||||
|
||||
# Configuration pour l'API (Port 81 interne, mappé sur 8091)
|
||||
server {
|
||||
listen 81;
|
||||
server_name _; # Accepte tout car NPM a déjà filtré le domaine
|
||||
index index.php index.html;
|
||||
root /var/www/public;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php?$query_string;
|
||||
}
|
||||
|
||||
location ~ \.php$ {
|
||||
try_files $uri =404;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass app:9000;
|
||||
fastcgi_index index.php;
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||
|
||||
# Optimisation pour le proxy
|
||||
fastcgi_param HTTP_X_FORWARDED_FOR $http_x_forwarded_for;
|
||||
fastcgi_param HTTP_X_FORWARDED_PROTO $http_x_forwarded_proto;
|
||||
}
|
||||
}
|
||||
Generated
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "WEB",
|
||||
"name": "www",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
.fi-pagination-items,.fi-pagination-overview,.fi-pagination-records-per-page-select:not(.fi-compact){display:none}@supports (container-type:inline-size){.fi-pagination{container-type:inline-size}@container (min-width: 28rem){.fi-pagination-records-per-page-select.fi-compact{display:none}.fi-pagination-records-per-page-select:not(.fi-compact){display:inline}}@container (min-width: 56rem){.fi-pagination:not(.fi-simple)>.fi-pagination-previous-btn{display:none}.fi-pagination-overview{display:inline}.fi-pagination:not(.fi-simple)>.fi-pagination-next-btn{display:none}.fi-pagination-items{display:flex}}}@supports not (container-type:inline-size){@media (min-width:640px){.fi-pagination-records-per-page-select.fi-compact{display:none}.fi-pagination-records-per-page-select:not(.fi-compact){display:inline}}@media (min-width:768px){.fi-pagination:not(.fi-simple)>.fi-pagination-previous-btn{display:none}.fi-pagination-overview{display:inline}.fi-pagination:not(.fi-simple)>.fi-pagination-next-btn{display:none}.fi-pagination-items{display:flex}}}.tippy-box[data-animation=fade][data-state=hidden]{opacity:0}[data-tippy-root]{max-width:calc(100vw - 10px)}.tippy-box{background-color:#333;border-radius:4px;color:#fff;font-size:14px;line-height:1.4;outline:0;position:relative;transition-property:transform,visibility,opacity;white-space:normal}.tippy-box[data-placement^=top]>.tippy-arrow{bottom:0}.tippy-box[data-placement^=top]>.tippy-arrow:before{border-top-color:initial;border-width:8px 8px 0;bottom:-7px;left:0;transform-origin:center top}.tippy-box[data-placement^=bottom]>.tippy-arrow{top:0}.tippy-box[data-placement^=bottom]>.tippy-arrow:before{border-bottom-color:initial;border-width:0 8px 8px;left:0;top:-7px;transform-origin:center bottom}.tippy-box[data-placement^=left]>.tippy-arrow{right:0}.tippy-box[data-placement^=left]>.tippy-arrow:before{border-left-color:initial;border-width:8px 0 8px 8px;right:-7px;transform-origin:center left}.tippy-box[data-placement^=right]>.tippy-arrow{left:0}.tippy-box[data-placement^=right]>.tippy-arrow:before{border-right-color:initial;border-width:8px 8px 8px 0;left:-7px;transform-origin:center right}.tippy-box[data-inertia][data-state=visible]{transition-timing-function:cubic-bezier(.54,1.5,.38,1.11)}.tippy-arrow{color:#333;height:16px;width:16px}.tippy-arrow:before{border-color:transparent;border-style:solid;content:"";position:absolute}.tippy-content{padding:5px 9px;position:relative;z-index:1}.tippy-box[data-theme~=light]{background-color:#fff;box-shadow:0 0 20px 4px #9aa1b126,0 4px 80px -8px #24282f40,0 4px 4px -2px #5b5e6926;color:#26323d}.tippy-box[data-theme~=light][data-placement^=top]>.tippy-arrow:before{border-top-color:#fff}.tippy-box[data-theme~=light][data-placement^=bottom]>.tippy-arrow:before{border-bottom-color:#fff}.tippy-box[data-theme~=light][data-placement^=left]>.tippy-arrow:before{border-left-color:#fff}.tippy-box[data-theme~=light][data-placement^=right]>.tippy-arrow:before{border-right-color:#fff}.tippy-box[data-theme~=light]>.tippy-backdrop{background-color:#fff}.tippy-box[data-theme~=light]>.tippy-svg-arrow{fill:#fff}.fi-sortable-ghost{opacity:.3}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
function r({state:o}){return{state:o,rows:[],shouldUpdateRows:!0,init:function(){this.updateRows(),this.rows.length<=0?this.rows.push({key:"",value:""}):this.updateState(),this.$watch("state",(t,e)=>{let s=i=>i===null?0:Array.isArray(i)?i.length:typeof i!="object"?0:Object.keys(i).length;s(t)===0&&s(e)===0||this.updateRows()})},addRow:function(){this.rows.push({key:"",value:""}),this.updateState()},deleteRow:function(t){this.rows.splice(t,1),this.rows.length<=0&&this.addRow(),this.updateState()},reorderRows:function(t){let e=Alpine.raw(this.rows);this.rows=[];let s=e.splice(t.oldIndex,1)[0];e.splice(t.newIndex,0,s),this.$nextTick(()=>{this.rows=e,this.updateState()})},updateRows:function(){if(!this.shouldUpdateRows){this.shouldUpdateRows=!0;return}let t=[];for(let[e,s]of Object.entries(this.state??{}))t.push({key:e,value:s});this.rows=t},updateState:function(){let t={};this.rows.forEach(e=>{e.key===""||e.key===null||(t[e.key]=e.value)}),this.shouldUpdateRows=!1,this.state=t}}}export{r as default};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
function i({state:a,splitKeys:n}){return{newTag:"",state:a,createTag:function(){if(this.newTag=this.newTag.trim(),this.newTag!==""){if(this.state.includes(this.newTag)){this.newTag="";return}this.state.push(this.newTag),this.newTag=""}},deleteTag:function(t){this.state=this.state.filter(e=>e!==t)},reorderTags:function(t){let e=this.state.splice(t.oldIndex,1)[0];this.state.splice(t.newIndex,0,e),this.state=[...this.state]},input:{"x-on:blur":"createTag()","x-model":"newTag","x-on:keydown"(t){["Enter",...n].includes(t.key)&&(t.preventDefault(),t.stopPropagation(),this.createTag())},"x-on:paste"(){this.$nextTick(()=>{if(n.length===0){this.createTag();return}let t=n.map(e=>e.replace(/[/\-\\^$*+?.()|[\]{}]/g,"\\$&")).join("|");this.newTag.split(new RegExp(t,"g")).forEach(e=>{this.newTag=e,this.createTag()})})}}}}export{i as default};
|
||||
@@ -0,0 +1 @@
|
||||
function r({initialHeight:t,shouldAutosize:i,state:s}){return{state:s,wrapperEl:null,init:function(){this.wrapperEl=this.$el.parentNode,this.setInitialHeight(),i?this.$watch("state",()=>{this.resize()}):this.setUpResizeObserver()},setInitialHeight:function(){this.$el.scrollHeight<=0||(this.wrapperEl.style.height=t+"rem")},resize:function(){if(this.setInitialHeight(),this.$el.scrollHeight<=0)return;let e=this.$el.scrollHeight+"px";this.wrapperEl.style.height!==e&&(this.wrapperEl.style.height=e)},setUpResizeObserver:function(){new ResizeObserver(()=>{this.wrapperEl.style.height=this.$el.style.height}).observe(this.$el)}}}export{r as default};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
function d(){return{checkboxClickController:null,collapsedGroups:[],isLoading:!1,selectedRecords:[],shouldCheckUniqueSelection:!0,lastCheckedRecord:null,livewireId:null,init:function(){this.livewireId=this.$root.closest("[wire\\:id]").attributes["wire:id"].value,this.$wire.$on("deselectAllTableRecords",()=>this.deselectAllRecords()),this.$watch("selectedRecords",()=>{if(!this.shouldCheckUniqueSelection){this.shouldCheckUniqueSelection=!0;return}this.selectedRecords=[...new Set(this.selectedRecords)],this.shouldCheckUniqueSelection=!1}),this.$nextTick(()=>this.watchForCheckboxClicks()),Livewire.hook("element.init",({component:e})=>{e.id===this.livewireId&&this.watchForCheckboxClicks()})},mountAction:function(e,t=null){this.$wire.set("selectedTableRecords",this.selectedRecords,!1),this.$wire.mountTableAction(e,t)},mountBulkAction:function(e){this.$wire.set("selectedTableRecords",this.selectedRecords,!1),this.$wire.mountTableBulkAction(e)},toggleSelectRecordsOnPage:function(){let e=this.getRecordsOnPage();if(this.areRecordsSelected(e)){this.deselectRecords(e);return}this.selectRecords(e)},toggleSelectRecordsInGroup:async function(e){this.isLoading=!0;let t=await this.$wire.getGroupedSelectableTableRecordKeys(e);this.areRecordsSelected(this.getRecordsInGroupOnPage(e))?this.deselectRecords(t):this.selectRecords(t),this.isLoading=!1},getRecordsInGroupOnPage:function(e){let t=[];for(let s of this.$root?.getElementsByClassName("fi-ta-record-checkbox")??[])s.dataset.group===e&&t.push(s.value);return t},getRecordsOnPage:function(){let e=[];for(let t of this.$root?.getElementsByClassName("fi-ta-record-checkbox")??[])e.push(t.value);return e},selectRecords:function(e){for(let t of e)this.isRecordSelected(t)||this.selectedRecords.push(t)},deselectRecords:function(e){for(let t of e){let s=this.selectedRecords.indexOf(t);s!==-1&&this.selectedRecords.splice(s,1)}},selectAllRecords:async function(){this.isLoading=!0,this.selectedRecords=await this.$wire.getAllSelectableTableRecordKeys(),this.isLoading=!1},deselectAllRecords:function(){this.selectedRecords=[]},isRecordSelected:function(e){return this.selectedRecords.includes(e)},areRecordsSelected:function(e){return e.every(t=>this.isRecordSelected(t))},toggleCollapseGroup:function(e){if(this.isGroupCollapsed(e)){this.collapsedGroups.splice(this.collapsedGroups.indexOf(e),1);return}this.collapsedGroups.push(e)},isGroupCollapsed:function(e){return this.collapsedGroups.includes(e)},resetCollapsedGroups:function(){this.collapsedGroups=[]},watchForCheckboxClicks:function(){this.checkboxClickController&&this.checkboxClickController.abort(),this.checkboxClickController=new AbortController;let{signal:e}=this.checkboxClickController;this.$root?.addEventListener("click",t=>t.target?.matches(".fi-ta-record-checkbox")&&this.handleCheckboxClick(t,t.target),{signal:e})},handleCheckboxClick:function(e,t){if(!this.lastChecked){this.lastChecked=t;return}if(e.shiftKey){let s=Array.from(this.$root?.getElementsByClassName("fi-ta-record-checkbox")??[]);if(!s.includes(this.lastChecked)){this.lastChecked=t;return}let l=s.indexOf(this.lastChecked),r=s.indexOf(t),o=[l,r].sort((c,n)=>c-n),i=[];for(let c=o[0];c<=o[1];c++)s[c].checked=t.checked,i.push(s[c].value);t.checked?this.selectRecords(i):this.deselectRecords(i)}this.lastChecked=t}}}export{d as default};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,61 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container mx-auto px-4 py-8">
|
||||
<div class="mb-8 flex flex-col md:flex-row md:items-end md:justify-between gap-4">
|
||||
<div>
|
||||
<h1 class="text-3xl font-bold text-indigo-900">Informations & Santé Mentale</h1>
|
||||
<p class="text-gray-600">Découvrez nos conseils et articles pour mieux gérer votre quotidien.</p>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<a href="{{ route('informations.index') }}"
|
||||
class="px-4 py-2 rounded-full text-sm font-medium transition-colors {{ !request('category') ? 'bg-indigo-600 text-white' : 'bg-gray-100 text-gray-600 hover:bg-gray-200' }}">
|
||||
Toutes
|
||||
</a>
|
||||
@foreach($categories as $category)
|
||||
<a href="{{ route('informations.index', ['category' => $category]) }}"
|
||||
class="px-4 py-2 rounded-full text-sm font-medium transition-colors {{ request('category') == $category ? 'bg-indigo-600 text-white' : 'bg-gray-100 text-gray-600 hover:bg-gray-200' }}">
|
||||
{{ $category }}
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
@foreach($informations as $info)
|
||||
<div class="bg-white rounded-2xl shadow-sm border border-gray-100 overflow-hidden hover:shadow-md transition-shadow">
|
||||
@if($info->image_url)
|
||||
<img src="{{ asset('storage/' . $info->image_url) }}" alt="{{ $info->title }}" class="w-full h-48 object-cover">
|
||||
@else
|
||||
<div class="w-full h-48 bg-indigo-50 flex items-center justify-center">
|
||||
<svg class="w-12 h-12 text-indigo-200" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="p-6">
|
||||
<span class="inline-block px-3 py-1 bg-indigo-100 text-indigo-700 text-xs font-semibold rounded-full mb-3">
|
||||
{{ $info->category }}
|
||||
</span>
|
||||
<h2 class="text-xl font-bold text-gray-900 mb-2">{{ $info->title }}</h2>
|
||||
<p class="text-gray-600 text-sm mb-4 line-clamp-3">
|
||||
{!! strip_tags($info->content) !!}
|
||||
</p>
|
||||
<a href="{{ route('informations.show', $info->id) }}" class="text-indigo-600 font-semibold hover:text-indigo-800 flex items-center">
|
||||
Lire la suite
|
||||
<svg class="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<div class="mt-12">
|
||||
{{ $informations->links() }}
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -0,0 +1,56 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container mx-auto px-4 py-8">
|
||||
<div class="max-w-4xl mx-auto">
|
||||
<a href="{{ route('informations.index') }}" class="inline-flex items-center text-indigo-600 hover:text-indigo-800 mb-6">
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"></path>
|
||||
</svg>
|
||||
Retour aux articles
|
||||
</a>
|
||||
|
||||
<div class="bg-white rounded-3xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
@if($information->image_url)
|
||||
<img src="{{ asset('storage/' . $information->image_url) }}" alt="{{ $information->title }}" class="w-full h-96 object-cover">
|
||||
@endif
|
||||
|
||||
<div class="p-8 md:p-12">
|
||||
<span class="inline-block px-4 py-1 bg-indigo-100 text-indigo-700 text-sm font-semibold rounded-full mb-6">
|
||||
{{ $information->category }}
|
||||
</span>
|
||||
|
||||
<h1 class="text-4xl font-bold text-gray-900 mb-6 leading-tight">
|
||||
{{ $information->title }}
|
||||
</h1>
|
||||
|
||||
<div class="flex items-center text-gray-500 mb-8 text-sm">
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
Publié le {{ $information->created_at->format('d/m/Y') }}
|
||||
</div>
|
||||
|
||||
<div class="prose prose-indigo max-w-none text-gray-700 leading-relaxed text-lg">
|
||||
{!! $information->content !!}
|
||||
</div>
|
||||
|
||||
<div class="mt-12 p-6 bg-gray-50 rounded-2xl border border-gray-100">
|
||||
<div class="flex items-start">
|
||||
<div class="flex-shrink-0">
|
||||
<svg class="h-6 w-6 text-indigo-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<p class="text-sm text-gray-600 italic">
|
||||
Information de santé publique - CESIZen. Ces contenus sont rédigés à but informatif et ne sauraient remplacer une consultation professionnelle.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -14,20 +14,47 @@
|
||||
|
||||
<nav class="flex items-center gap-6" role="navigation" aria-label="Menu utilisateur">
|
||||
@auth
|
||||
<a href="{{ route('profile.index') }}" class="flex items-center gap-3 px-4 py-1.5 bg-slate-50 rounded-lg border border-slate-200 shadow-sm hover:border-[#000091] transition-all group">
|
||||
<div class="text-right">
|
||||
<p class="text-sm font-bold leading-none text-slate-900 group-hover:text-[#000091]">{{ Auth::user()->name }}</p>
|
||||
<p class="text-[10px] text-slate-500 font-bold uppercase tracking-tighter">Mon Profil</p>
|
||||
</div>
|
||||
<img src="https://ui-avatars.com/api/?name={{ urlencode(Auth::user()->name) }}&background=000091&color=fff" class="w-9 h-9 rounded-lg border border-slate-200" alt="Avatar de {{ Auth::user()->name }}">
|
||||
</a>
|
||||
<form action="{{ route('logout') }}" method="POST" class="inline">
|
||||
@csrf
|
||||
<button type="submit" class="text-xs font-bold text-slate-500 hover:text-[#E1000F] uppercase tracking-tighter flex items-center gap-1">
|
||||
<x-heroicon-o-arrow-left-on-rectangle class="h-4 w-4" />
|
||||
Quitter
|
||||
<div class="relative" x-data="{ open: false }">
|
||||
<button @click="open = !open" @click.away="open = false" class="flex items-center gap-3 px-4 py-1.5 bg-slate-50 rounded-lg border border-slate-200 shadow-sm hover:border-[#000091] transition-all group">
|
||||
<div class="text-right">
|
||||
<p class="text-sm font-bold leading-none text-slate-900 group-hover:text-[#000091]">{{ Auth::user()->name }}</p>
|
||||
<p class="text-[10px] text-slate-500 font-bold uppercase tracking-tighter">Mon Profil</p>
|
||||
</div>
|
||||
<img src="https://ui-avatars.com/api/?name={{ urlencode(Auth::user()->name) }}&background=000091&color=fff" class="w-9 h-9 rounded-lg border border-slate-200" alt="Avatar de {{ Auth::user()->name }}">
|
||||
<x-heroicon-o-chevron-down class="h-4 w-4 text-slate-400 group-hover:text-[#000091] transition-transform" ::class="open ? 'rotate-180' : ''" />
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<!-- Dropdown Menu -->
|
||||
<div x-show="open"
|
||||
x-transition:enter="transition ease-out duration-100"
|
||||
x-transition:enter-start="opacity-0 scale-95"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
class="absolute right-0 mt-2 w-56 bg-white border border-slate-200 rounded-lg shadow-xl z-50 py-2 overflow-hidden">
|
||||
|
||||
<a href="{{ route('profile.index') }}" class="flex items-center gap-3 px-4 py-2.5 text-sm font-medium text-slate-700 hover:bg-slate-50 transition-colors">
|
||||
<x-heroicon-o-user class="h-4 w-4 text-slate-400" />
|
||||
Mon compte
|
||||
</a>
|
||||
|
||||
@if(Auth::user()->role && Auth::user()->role->libelle === 'Admin')
|
||||
<div class="border-t border-slate-100 my-1"></div>
|
||||
<a href="/admin" class="flex items-center gap-3 px-4 py-2.5 text-sm font-bold text-[--fr-blue] hover:bg-blue-50 transition-colors">
|
||||
<x-heroicon-o-shield-check class="h-4 w-4" />
|
||||
Administration
|
||||
</a>
|
||||
@endif
|
||||
|
||||
<div class="border-t border-slate-100 my-1"></div>
|
||||
|
||||
<form action="{{ route('logout') }}" method="POST" class="block w-full">
|
||||
@csrf
|
||||
<button type="submit" class="flex items-center gap-3 w-full px-4 py-2.5 text-sm font-medium text-red-600 hover:bg-red-50 transition-colors">
|
||||
<x-heroicon-o-arrow-left-on-rectangle class="h-4 w-4" />
|
||||
Déconnexion
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<div class="flex items-center gap-4">
|
||||
<a href="/login" class="text-sm font-bold text-[#000091] hover:underline px-2 py-2">Se connecter</a>
|
||||
|
||||
@@ -40,6 +40,16 @@
|
||||
Exercices de Respiration
|
||||
</a>
|
||||
|
||||
<!-- Informations / Guides -->
|
||||
<a href="{{ route('informations.index') }}"
|
||||
class="flex items-center gap-4 px-4 py-3 rounded-sm transition-all group {{ request()->routeIs('informations.*') ? 'text-white bg-[#000091] font-bold shadow-md' : 'text-slate-700 hover:bg-slate-50 font-medium border-b border-slate-100/50' }}"
|
||||
{!! request()->routeIs('informations.*') ? 'aria-current="page"' : '' !!}>
|
||||
<div class="w-8 h-8 rounded-sm flex items-center justify-center transition-colors {{ request()->routeIs('informations.*') ? 'bg-white/20 text-white' : 'bg-blue-50 text-[--fr-blue]' }}" aria-hidden="true">
|
||||
<x-heroicon-o-document-text class="h-4 w-4" />
|
||||
</div>
|
||||
Guides & Informations
|
||||
</a>
|
||||
|
||||
<div class="pt-10 space-y-1">
|
||||
<p class="text-[10px] font-bold text-slate-500 uppercase tracking-[0.2em] mb-4 px-4">Services de l'État</p>
|
||||
<a href="#" class="flex items-center justify-between px-4 py-2 text-sm text-slate-600 hover:text-[--fr-blue] font-medium transition-colors">
|
||||
|
||||
@@ -74,9 +74,24 @@
|
||||
|
||||
<!-- Section Activités -->
|
||||
<div class="space-y-8">
|
||||
<div class="flex items-center gap-4">
|
||||
<h2 class="text-xl font-bold text-slate-900 uppercase tracking-widest whitespace-nowrap">Bibliothèque Méditation</h2>
|
||||
<div class="h-1 flex-1 bg-slate-100"></div>
|
||||
<div class="flex flex-col md:flex-row md:items-center justify-between gap-4">
|
||||
<div class="flex items-center gap-4 flex-1">
|
||||
<h2 class="text-xl font-bold text-slate-900 uppercase tracking-widest whitespace-nowrap">Bibliothèque Méditation</h2>
|
||||
<div class="h-1 flex-1 bg-slate-100"></div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<a href="{{ route('relaxation.index') }}"
|
||||
class="px-4 py-2 rounded-full text-[10px] font-bold uppercase transition-all {{ !request('category') ? 'bg-[--fr-blue] text-white shadow-md' : 'bg-slate-100 text-slate-600 hover:bg-slate-200' }}">
|
||||
Toutes
|
||||
</a>
|
||||
@foreach($categories as $category)
|
||||
<a href="{{ route('relaxation.index', ['category' => $category]) }}"
|
||||
class="px-4 py-2 rounded-full text-[10px] font-bold uppercase transition-all {{ request('category') == $category ? 'bg-[--fr-blue] text-white shadow-md' : 'bg-slate-100 text-slate-600 hover:bg-slate-200' }}">
|
||||
{{ $category }}
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
Faire mon bilan de santé
|
||||
</a>
|
||||
<a href="{{ route('relaxation.index') }}" class="px-8 py-3.5 bg-white border-2 border-[#000091] text-[#000091] font-bold rounded-sm hover:bg-slate-50 transition-all">
|
||||
Voir mes favoris
|
||||
Travailler sa respiration
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -77,7 +77,7 @@
|
||||
</div>
|
||||
<h4 class="text-xl font-bold text-slate-900 mb-2">Guides Pratiques</h4>
|
||||
<p class="text-slate-600 text-sm leading-relaxed mb-6">Consultez les fiches de prévention sur le sommeil et la nutrition.</p>
|
||||
<a href="#" class="inline-flex items-center gap-2 text-sm font-bold text-[--fr-blue] hover:underline underline-offset-4 decoration-2">
|
||||
<a href="{{ route('informations.index') }}" class="inline-flex items-center gap-2 text-sm font-bold text-[--fr-blue] hover:underline underline-offset-4 decoration-2">
|
||||
Lire les guides
|
||||
<x-heroicon-o-arrow-right class="h-4 w-4" aria-hidden="true" />
|
||||
</a>
|
||||
@@ -105,7 +105,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Global Footer Gov -->
|
||||
|
||||
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -25,4 +25,8 @@ Route::middleware('auth:sanctum')->group(function () {
|
||||
|
||||
Route::get('/emotions', [App\Http\Controllers\Api\EmotionController::class, 'index']);
|
||||
Route::post('/emotions', [App\Http\Controllers\Api\EmotionController::class, 'store']);
|
||||
|
||||
// Routes pour les informations (accessibles aux utilisateurs connectés)
|
||||
Route::get('/informations', [App\Http\Controllers\Api\InformationController::class, 'index']);
|
||||
Route::get('/informations/{id}', [App\Http\Controllers\Api\InformationController::class, 'show']);
|
||||
});
|
||||
|
||||
@@ -9,10 +9,14 @@ use App\Http\Controllers\Web\RelaxationController;
|
||||
use App\Http\Controllers\Web\AuthController;
|
||||
use App\Http\Controllers\Web\ProfileController;
|
||||
|
||||
use App\Http\Controllers\Web\InformationController;
|
||||
|
||||
// Public Routes
|
||||
Route::get('/', [DashboardController::class, 'index'])->name('home');
|
||||
Route::get('/diagnostics', [DiagnosticController::class, 'index'])->name('diagnostics.index');
|
||||
Route::get('/relaxation', [RelaxationController::class, 'index'])->name('relaxation.index');
|
||||
Route::get('/informations', [InformationController::class, 'index'])->name('informations.index');
|
||||
Route::get('/informations/{id}', [InformationController::class, 'show'])->name('informations.show');
|
||||
|
||||
// Authentification
|
||||
Route::get('/login', [AuthController::class, 'showLogin'])->name('login');
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\Role;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class AdminAccessTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_guest_is_redirected_to_login(): void
|
||||
{
|
||||
$response = $this->get('/admin');
|
||||
|
||||
$response->assertRedirect('/login');
|
||||
}
|
||||
|
||||
public function test_non_admin_user_cannot_access_admin_panel(): void
|
||||
{
|
||||
$role = Role::create(['libelle' => 'Utilisateur']);
|
||||
$user = User::factory()->create(['id_role' => $role->id]);
|
||||
|
||||
$response = $this->actingAs($user)->get('/admin');
|
||||
|
||||
$response->assertForbidden();
|
||||
}
|
||||
|
||||
public function test_admin_user_can_access_admin_panel(): void
|
||||
{
|
||||
$role = Role::create(['libelle' => 'Admin']);
|
||||
$user = User::factory()->create(['id_role' => $role->id]);
|
||||
|
||||
$response = $this->actingAs($user)->get('/admin');
|
||||
|
||||
$response->assertSuccessful();
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit;
|
||||
namespace Tests\Feature;
|
||||
|
||||
use Tests\TestCase;
|
||||
use App\Models\User;
|
||||
use App\Models\StressEvent;
|
||||
use App\Models\ResultatDiag;
|
||||
use Illuminate\Foundation.Testing.RefreshDatabase;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
class DiagnosticTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_diagnostic_score_calculation_and_storage()
|
||||
public function test_diagnostic_score_calculation_and_storage(): void
|
||||
{
|
||||
$this->withoutMiddleware();
|
||||
|
||||
// Créer un utilisateur
|
||||
$user = User::factory()->create();
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use App\Services\StressCalculator;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class StressCalculatorTest extends TestCase
|
||||
{
|
||||
private StressCalculator $calculator;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->calculator = new StressCalculator();
|
||||
}
|
||||
|
||||
public function test_calculate_score_sums_points_correctly(): void
|
||||
{
|
||||
$points = [100, 73, 50];
|
||||
$this->assertEquals(223, $this->calculator->calculateScore($points));
|
||||
}
|
||||
|
||||
public function test_calculate_score_returns_zero_for_empty_array(): void
|
||||
{
|
||||
$this->assertEquals(0, $this->calculator->calculateScore([]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider stressLevelProvider
|
||||
*/
|
||||
public function test_determine_level_returns_correct_label(int $score, string $expectedLevel): void
|
||||
{
|
||||
$this->assertEquals($expectedLevel, $this->calculator->determineLevel($score));
|
||||
}
|
||||
|
||||
public static function stressLevelProvider(): array
|
||||
{
|
||||
return [
|
||||
[350, StressCalculator::LEVEL_HIGH],
|
||||
[300, StressCalculator::LEVEL_HIGH],
|
||||
[299, StressCalculator::LEVEL_MODERATE],
|
||||
[150, StressCalculator::LEVEL_MODERATE],
|
||||
[149, StressCalculator::LEVEL_LOW],
|
||||
[0, StressCalculator::LEVEL_LOW],
|
||||
];
|
||||
}
|
||||
}
|
||||
Generated
+3
-3
@@ -1,3 +1,3 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
|
||||
Generated
+5
-5
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AndroidProjectSystem">
|
||||
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
|
||||
</component>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AndroidProjectSystem">
|
||||
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
+5
-5
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="21" />
|
||||
</component>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="21" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
+17
-17
@@ -1,18 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="deploymentTargetSelector">
|
||||
<selectionStates>
|
||||
<SelectionState runConfigName="app">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
<DropdownSelection timestamp="2026-04-21T21:27:34.624668600Z">
|
||||
<Target type="DEFAULT_BOOT">
|
||||
<handle>
|
||||
<DeviceId pluginId="PhysicalDevice" identifier="serial=d76a0274" />
|
||||
</handle>
|
||||
</Target>
|
||||
</DropdownSelection>
|
||||
<DialogSelection />
|
||||
</SelectionState>
|
||||
</selectionStates>
|
||||
</component>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="deploymentTargetSelector">
|
||||
<selectionStates>
|
||||
<SelectionState runConfigName="app">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
<DropdownSelection timestamp="2026-04-21T21:27:34.624668600Z">
|
||||
<Target type="DEFAULT_BOOT">
|
||||
<handle>
|
||||
<DeviceId pluginId="PhysicalDevice" identifier="serial=d76a0274" />
|
||||
</handle>
|
||||
</Target>
|
||||
</DropdownSelection>
|
||||
<DialogSelection />
|
||||
</SelectionState>
|
||||
</selectionStates>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+17
-17
@@ -1,18 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="testRunner" value="CHOOSE_PER_TEST" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="testRunner" value="CHOOSE_PER_TEST" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="ComposePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ComposePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ComposePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ComposePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="GlancePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="GlancePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="GlancePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="GlancePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewDeviceShouldUseNewSpec" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewParameterProviderOnFirstParameter" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
Generated
+16
-16
@@ -1,17 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RunConfigurationProducerService">
|
||||
<option name="ignoredProducers">
|
||||
<set>
|
||||
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
|
||||
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
|
||||
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RunConfigurationProducerService">
|
||||
<option name="ignoredProducers">
|
||||
<set>
|
||||
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
|
||||
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
|
||||
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+5
-5
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||
</component>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -0,0 +1,24 @@
|
||||
# ⌚ CESIZen - Wear OS App
|
||||
|
||||
Application pour montre connectée permettant une capture instantanée de l'humeur.
|
||||
|
||||
## 🛠️ Guide d'installation
|
||||
|
||||
### Prérequis
|
||||
- Android Studio Jellyfish+
|
||||
- Wear OS Emulator ou Montre (Wear OS 3.0+)
|
||||
- Google Play Services installés sur le téléphone appairé
|
||||
|
||||
### Étapes d'installation
|
||||
1. **Ouvrir le dossier** `Wear` dans Android Studio.
|
||||
2. **Synchroniser Gradle** et attendre la fin du build.
|
||||
3. **Lancer sur l'appareil** :
|
||||
Sélectionnez votre montre/émulateur et cliquez sur "Run".
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ Architecture Technique
|
||||
- **Langage** : Kotlin
|
||||
- **UI** : Jetpack Compose for Wear OS
|
||||
- **Communication** : Data Layer API (MessageClient / DataClient)
|
||||
- **Fonctionnalité** : Synchronisation bidirectionnelle de la date et de l'humeur quotidienne.
|
||||
@@ -1,50 +1,50 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
|
||||
<uses-feature android:name="android.hardware.type.watch" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@android:style/Theme.DeviceDefault">
|
||||
<uses-library
|
||||
android:name="com.google.android.wearable"
|
||||
android:required="true" />
|
||||
<uses-library
|
||||
android:name="wear-sdk"
|
||||
android:required="false" />
|
||||
|
||||
|
||||
<!--
|
||||
Set to true if your app is Standalone, that is, it does not require the handheld
|
||||
app to run.
|
||||
-->
|
||||
<meta-data
|
||||
android:name="com.google.android.gms.wearable.standalone"
|
||||
android:value="true" />
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.android.gms.wearable.capabilities"
|
||||
android:resource="@xml/wearable_app_values" />
|
||||
|
||||
<activity
|
||||
android:name="com.cesizen.app.MainActivity"
|
||||
android:exported="true"
|
||||
android:taskAffinity=""
|
||||
android:theme="@style/MainActivityTheme.Starting">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
|
||||
<uses-feature android:name="android.hardware.type.watch" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@android:style/Theme.DeviceDefault">
|
||||
<uses-library
|
||||
android:name="com.google.android.wearable"
|
||||
android:required="true" />
|
||||
<uses-library
|
||||
android:name="wear-sdk"
|
||||
android:required="false" />
|
||||
|
||||
|
||||
<!--
|
||||
Set to true if your app is Standalone, that is, it does not require the handheld
|
||||
app to run.
|
||||
-->
|
||||
<meta-data
|
||||
android:name="com.google.android.gms.wearable.standalone"
|
||||
android:value="true" />
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.android.gms.wearable.capabilities"
|
||||
android:resource="@xml/wearable_app_values" />
|
||||
|
||||
<activity
|
||||
android:name="com.cesizen.app.MainActivity"
|
||||
android:exported="true"
|
||||
android:taskAffinity=""
|
||||
android:theme="@style/MainActivityTheme.Starting">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -1,282 +1,282 @@
|
||||
package com.cesizen.app
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.wear.compose.foundation.lazy.TransformingLazyColumn
|
||||
import androidx.wear.compose.foundation.lazy.rememberTransformingLazyColumnState
|
||||
import androidx.wear.compose.material3.*
|
||||
import com.cesizen.app.presentation.theme.MyApplicationTheme
|
||||
import com.google.android.gms.tasks.Tasks
|
||||
import com.google.android.gms.wearable.*
|
||||
import org.json.JSONObject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.Network
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.NetworkRequest
|
||||
|
||||
class MainActivity : ComponentActivity(), DataClient.OnDataChangedListener {
|
||||
|
||||
|
||||
private var userName by mutableStateOf<String?>(null)
|
||||
private var currentMood by mutableStateOf<String?>(null)
|
||||
private var isRefreshing by mutableStateOf(false)
|
||||
private val TAG = "CESIZen_Watch"
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
Wearable.getDataClient(this).addListener(this)
|
||||
refreshData()
|
||||
|
||||
val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
|
||||
val request = NetworkRequest.Builder()
|
||||
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI) // Priorité Wi-Fi
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_BLUETOOTH) // Secours Bluetooth
|
||||
.build()
|
||||
|
||||
connectivityManager.requestNetwork(request, object : ConnectivityManager.NetworkCallback() {
|
||||
override fun onAvailable(network: Network) {
|
||||
// Le système a trouvé soit du Wi-Fi, soit le pont Bluetooth du téléphone
|
||||
connectivityManager.bindProcessToNetwork(network)
|
||||
}
|
||||
})
|
||||
|
||||
setContent {
|
||||
WearApp(
|
||||
userName = userName,
|
||||
currentMood = currentMood,
|
||||
isRefreshing = isRefreshing,
|
||||
onRefresh = { refreshData() },
|
||||
onMoodSelected = { mood -> sendMoodToPhone(mood) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun refreshData() {
|
||||
if (isRefreshing) return
|
||||
isRefreshing = true
|
||||
|
||||
val today = java.text.SimpleDateFormat("yyyy-MM-dd", java.util.Locale.getDefault()).format(java.util.Date())
|
||||
|
||||
Wearable.getDataClient(this).dataItems.addOnSuccessListener { items ->
|
||||
for (item in items) {
|
||||
if (item.uri.path == "/auth_status") {
|
||||
val dataMap = DataMapItem.fromDataItem(item).dataMap
|
||||
userName = if (dataMap.getString("status") == "authenticated") dataMap.getString("userName") else null
|
||||
} else if (item.uri.path == "/daily_mood") {
|
||||
val dataMap = DataMapItem.fromDataItem(item).dataMap
|
||||
val moodDate = dataMap.getString("date")
|
||||
|
||||
if (moodDate == today) {
|
||||
currentMood = dataMap.getString("mood")
|
||||
} else {
|
||||
currentMood = null
|
||||
}
|
||||
}
|
||||
}
|
||||
isRefreshing = false
|
||||
}
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val nodes = Tasks.await(Wearable.getNodeClient(this@MainActivity).connectedNodes)
|
||||
for (node in nodes) {
|
||||
val payload = JSONObject().apply { put("command", "get_sync_data") }.toString().toByteArray()
|
||||
Wearable.getMessageClient(this@MainActivity).sendMessage(node.id, "/wearable_communication", payload)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error sync nodes", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendMoodToPhone(mood: String) {
|
||||
currentMood = mood
|
||||
if (mood.isEmpty()) {
|
||||
// Reset mood locally to show the selection again
|
||||
currentMood = null
|
||||
}
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val nodes = Tasks.await(Wearable.getNodeClient(this@MainActivity).connectedNodes)
|
||||
for (node in nodes) {
|
||||
val payload = JSONObject().apply {
|
||||
put("command", "save_mood")
|
||||
put("mood", mood)
|
||||
}.toString().toByteArray()
|
||||
Wearable.getMessageClient(this@MainActivity).sendMessage(node.id, "/wearable_communication", payload)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error sending mood", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDataChanged(dataEvents: DataEventBuffer) {
|
||||
val today = java.text.SimpleDateFormat("yyyy-MM-dd", java.util.Locale.getDefault()).format(java.util.Date())
|
||||
|
||||
for (event in dataEvents) {
|
||||
val dataMap = DataMapItem.fromDataItem(event.dataItem).dataMap
|
||||
if (event.dataItem.uri.path == "/auth_status") {
|
||||
userName = if (dataMap.getString("status") == "authenticated") dataMap.getString("userName") else null
|
||||
} else if (event.dataItem.uri.path == "/daily_mood") {
|
||||
val moodDate = dataMap.getString("date")
|
||||
if (moodDate == today) {
|
||||
currentMood = dataMap.getString("mood")
|
||||
} else {
|
||||
currentMood = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun WearApp(userName: String?, currentMood: String?, isRefreshing: Boolean, onRefresh: () -> Unit, onMoodSelected: (String) -> Unit) {
|
||||
val cesiBlue = Color(0xFF000091)
|
||||
|
||||
MyApplicationTheme {
|
||||
AppScaffold {
|
||||
val listState = rememberTransformingLazyColumnState()
|
||||
ScreenScaffold(scrollState = listState) { contentPadding ->
|
||||
TransformingLazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Brush.verticalGradient(listOf(Color.Black, Color(0xFF000022)))),
|
||||
contentPadding = contentPadding,
|
||||
state = listState,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
// Header
|
||||
item {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.padding(bottom = 12.dp)
|
||||
) {
|
||||
Text(
|
||||
"CESIZen",
|
||||
color = cesiBlue,
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
}
|
||||
}
|
||||
|
||||
if (userName == null) {
|
||||
item {
|
||||
TitleCard(
|
||||
onClick = onRefresh,
|
||||
title = { Text("Connexion requise", textAlign = TextAlign.Center) },
|
||||
subtitle = { Text("Ouvrez l'app sur votre téléphone", textAlign = TextAlign.Center, fontSize = 12.sp) },
|
||||
modifier = Modifier.padding(horizontal = 8.dp)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// User Status
|
||||
item {
|
||||
val displayName = userName.split(" ").firstOrNull() ?: "Zen"
|
||||
Text(
|
||||
"Bonjour $displayName",
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
|
||||
if (currentMood.isNullOrEmpty()) {
|
||||
item {
|
||||
Text(
|
||||
"Comment allez-vous ?",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = Color.LightGray,
|
||||
modifier = Modifier.padding(vertical = 8.dp)
|
||||
)
|
||||
}
|
||||
|
||||
val moods = listOf(
|
||||
MoodItem("Très bien", "😊", Color(0xFF4CAF50)),
|
||||
MoodItem("Bien", "🙂", Color(0xFF8BC34A)),
|
||||
MoodItem("Neutre", "😐", Color(0xFFFFC107)),
|
||||
MoodItem("Pas top", "🙁", Color(0xFFFF9800)),
|
||||
MoodItem("Stressé", "😫", Color(0xFFF44336))
|
||||
)
|
||||
|
||||
moods.forEach { mood ->
|
||||
item {
|
||||
FilledTonalButton(
|
||||
onClick = { onMoodSelected(mood.label) },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 2.dp, horizontal = 16.dp),
|
||||
colors = ButtonDefaults.filledTonalButtonColors(
|
||||
containerColor = mood.color.copy(alpha = 0.2f),
|
||||
contentColor = Color.White
|
||||
)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(mood.emoji, fontSize = 20.sp)
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
Text(mood.label, style = MaterialTheme.typography.labelLarge)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
item {
|
||||
Card(
|
||||
onClick = { onMoodSelected("") },
|
||||
modifier = Modifier.padding(16.dp),
|
||||
colors = CardDefaults.cardColors(containerColor = cesiBlue.copy(alpha = 0.3f))
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth().padding(8.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text("Humeur enregistrée", style = MaterialTheme.typography.labelSmall)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text(currentMood, style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text("Tapoter pour changer", fontSize = 10.sp, color = Color.LightGray)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bottom Spacer
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class MoodItem(val label: String, val emoji: String, val color: Color)
|
||||
package com.cesizen.app
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.wear.compose.foundation.lazy.TransformingLazyColumn
|
||||
import androidx.wear.compose.foundation.lazy.rememberTransformingLazyColumnState
|
||||
import androidx.wear.compose.material3.*
|
||||
import com.cesizen.app.presentation.theme.MyApplicationTheme
|
||||
import com.google.android.gms.tasks.Tasks
|
||||
import com.google.android.gms.wearable.*
|
||||
import org.json.JSONObject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.Network
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.NetworkRequest
|
||||
|
||||
class MainActivity : ComponentActivity(), DataClient.OnDataChangedListener {
|
||||
|
||||
|
||||
private var userName by mutableStateOf<String?>(null)
|
||||
private var currentMood by mutableStateOf<String?>(null)
|
||||
private var isRefreshing by mutableStateOf(false)
|
||||
private val TAG = "CESIZen_Watch"
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
Wearable.getDataClient(this).addListener(this)
|
||||
refreshData()
|
||||
|
||||
val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
|
||||
val request = NetworkRequest.Builder()
|
||||
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI) // Priorité Wi-Fi
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_BLUETOOTH) // Secours Bluetooth
|
||||
.build()
|
||||
|
||||
connectivityManager.requestNetwork(request, object : ConnectivityManager.NetworkCallback() {
|
||||
override fun onAvailable(network: Network) {
|
||||
// Le système a trouvé soit du Wi-Fi, soit le pont Bluetooth du téléphone
|
||||
connectivityManager.bindProcessToNetwork(network)
|
||||
}
|
||||
})
|
||||
|
||||
setContent {
|
||||
WearApp(
|
||||
userName = userName,
|
||||
currentMood = currentMood,
|
||||
isRefreshing = isRefreshing,
|
||||
onRefresh = { refreshData() },
|
||||
onMoodSelected = { mood -> sendMoodToPhone(mood) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun refreshData() {
|
||||
if (isRefreshing) return
|
||||
isRefreshing = true
|
||||
|
||||
val today = java.text.SimpleDateFormat("yyyy-MM-dd", java.util.Locale.getDefault()).format(java.util.Date())
|
||||
|
||||
Wearable.getDataClient(this).dataItems.addOnSuccessListener { items ->
|
||||
for (item in items) {
|
||||
if (item.uri.path == "/auth_status") {
|
||||
val dataMap = DataMapItem.fromDataItem(item).dataMap
|
||||
userName = if (dataMap.getString("status") == "authenticated") dataMap.getString("userName") else null
|
||||
} else if (item.uri.path == "/daily_mood") {
|
||||
val dataMap = DataMapItem.fromDataItem(item).dataMap
|
||||
val moodDate = dataMap.getString("date")
|
||||
|
||||
if (moodDate == today) {
|
||||
currentMood = dataMap.getString("mood")
|
||||
} else {
|
||||
currentMood = null
|
||||
}
|
||||
}
|
||||
}
|
||||
isRefreshing = false
|
||||
}
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val nodes = Tasks.await(Wearable.getNodeClient(this@MainActivity).connectedNodes)
|
||||
for (node in nodes) {
|
||||
val payload = JSONObject().apply { put("command", "get_sync_data") }.toString().toByteArray()
|
||||
Wearable.getMessageClient(this@MainActivity).sendMessage(node.id, "/wearable_communication", payload)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error sync nodes", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendMoodToPhone(mood: String) {
|
||||
currentMood = mood
|
||||
if (mood.isEmpty()) {
|
||||
// Reset mood locally to show the selection again
|
||||
currentMood = null
|
||||
}
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val nodes = Tasks.await(Wearable.getNodeClient(this@MainActivity).connectedNodes)
|
||||
for (node in nodes) {
|
||||
val payload = JSONObject().apply {
|
||||
put("command", "save_mood")
|
||||
put("mood", mood)
|
||||
}.toString().toByteArray()
|
||||
Wearable.getMessageClient(this@MainActivity).sendMessage(node.id, "/wearable_communication", payload)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error sending mood", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDataChanged(dataEvents: DataEventBuffer) {
|
||||
val today = java.text.SimpleDateFormat("yyyy-MM-dd", java.util.Locale.getDefault()).format(java.util.Date())
|
||||
|
||||
for (event in dataEvents) {
|
||||
val dataMap = DataMapItem.fromDataItem(event.dataItem).dataMap
|
||||
if (event.dataItem.uri.path == "/auth_status") {
|
||||
userName = if (dataMap.getString("status") == "authenticated") dataMap.getString("userName") else null
|
||||
} else if (event.dataItem.uri.path == "/daily_mood") {
|
||||
val moodDate = dataMap.getString("date")
|
||||
if (moodDate == today) {
|
||||
currentMood = dataMap.getString("mood")
|
||||
} else {
|
||||
currentMood = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun WearApp(userName: String?, currentMood: String?, isRefreshing: Boolean, onRefresh: () -> Unit, onMoodSelected: (String) -> Unit) {
|
||||
val cesiBlue = Color(0xFF000091)
|
||||
|
||||
MyApplicationTheme {
|
||||
AppScaffold {
|
||||
val listState = rememberTransformingLazyColumnState()
|
||||
ScreenScaffold(scrollState = listState) { contentPadding ->
|
||||
TransformingLazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Brush.verticalGradient(listOf(Color.Black, Color(0xFF000022)))),
|
||||
contentPadding = contentPadding,
|
||||
state = listState,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
// Header
|
||||
item {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.padding(bottom = 12.dp)
|
||||
) {
|
||||
Text(
|
||||
"CESIZen",
|
||||
color = cesiBlue,
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
}
|
||||
}
|
||||
|
||||
if (userName == null) {
|
||||
item {
|
||||
TitleCard(
|
||||
onClick = onRefresh,
|
||||
title = { Text("Connexion requise", textAlign = TextAlign.Center) },
|
||||
subtitle = { Text("Ouvrez l'app sur votre téléphone", textAlign = TextAlign.Center, fontSize = 12.sp) },
|
||||
modifier = Modifier.padding(horizontal = 8.dp)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// User Status
|
||||
item {
|
||||
val displayName = userName.split(" ").firstOrNull() ?: "Zen"
|
||||
Text(
|
||||
"Bonjour $displayName",
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
|
||||
if (currentMood.isNullOrEmpty()) {
|
||||
item {
|
||||
Text(
|
||||
"Comment allez-vous ?",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = Color.LightGray,
|
||||
modifier = Modifier.padding(vertical = 8.dp)
|
||||
)
|
||||
}
|
||||
|
||||
val moods = listOf(
|
||||
MoodItem("Très bien", "😊", Color(0xFF4CAF50)),
|
||||
MoodItem("Bien", "🙂", Color(0xFF8BC34A)),
|
||||
MoodItem("Neutre", "😐", Color(0xFFFFC107)),
|
||||
MoodItem("Pas top", "🙁", Color(0xFFFF9800)),
|
||||
MoodItem("Stressé", "😫", Color(0xFFF44336))
|
||||
)
|
||||
|
||||
moods.forEach { mood ->
|
||||
item {
|
||||
FilledTonalButton(
|
||||
onClick = { onMoodSelected(mood.label) },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 2.dp, horizontal = 16.dp),
|
||||
colors = ButtonDefaults.filledTonalButtonColors(
|
||||
containerColor = mood.color.copy(alpha = 0.2f),
|
||||
contentColor = Color.White
|
||||
)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(mood.emoji, fontSize = 20.sp)
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
Text(mood.label, style = MaterialTheme.typography.labelLarge)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
item {
|
||||
Card(
|
||||
onClick = { onMoodSelected("") },
|
||||
modifier = Modifier.padding(16.dp),
|
||||
colors = CardDefaults.cardColors(containerColor = cesiBlue.copy(alpha = 0.3f))
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth().padding(8.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text("Humeur enregistrée", style = MaterialTheme.typography.labelSmall)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text(currentMood, style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text("Tapoter pour changer", fontSize = 10.sp, color = Color.LightGray)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bottom Spacer
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class MoodItem(val label: String, val emoji: String, val color: Color)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user