102 Commits

Author SHA1 Message Date
sanchezvem ed3d9bf52e cleaned code 2026-05-18 16:35:12 +01:00
sanchezvem b3cae6e3a9 Change empty box 2026-05-18 16:34:26 +01:00
sanchezvem a37b45f5b6 fixed error message on auth 2026-05-17 16:10:50 +01:00
sanchezvem 036ba39a5f Added permissions 2026-05-17 15:47:02 +01:00
sanchezvem ac9d62d284 Cleaned code 2026-05-14 18:49:56 +01:00
sanchezvem 8f836c6d80 Changed variables name 2026-05-14 18:35:43 +01:00
sanchezvem e8c308adf8 Changed errors messages 2026-05-14 14:37:44 +01:00
sanchezvem 42116be9a6 Added segments to separe friends and global 2026-05-14 14:07:37 +01:00
sanchezvem eee7958bce Added posts and possibility to like post 2026-05-14 13:55:00 +01:00
sanchezvem d5584ebf0a Fixed error in hour of challenge accomplished 2026-05-14 11:49:46 +01:00
sanchezvem 6a1ed5abeb Started implementation of all posts 2026-05-14 11:19:48 +01:00
sanchezvem e483bc4e57 Cleaned code 2026-05-13 23:06:30 +01:00
sanchezvem f1354c2db3 added functions and web-socket to chat in group 2026-05-13 23:01:25 +01:00
sanchezvem bd0d15bbf0 added functions and web-socket to chat in group 2026-05-13 23:01:13 +01:00
sanchezvem 90749ea55e added form into vue to send message 2026-05-13 20:47:55 +01:00
sanchezvem ff7039a80b added messages 2026-05-13 19:52:25 +01:00
sanchezvem fb467d52d9 changed groups page to publication page 2026-05-13 18:13:01 +01:00
sanchezvem f09d5a0f11 Deleted one option in group manage page 2026-04-28 11:28:08 +01:00
sanchezvem 9367f5e7b3 Fixed error with visibility of all groups after change 2026-04-28 11:25:15 +01:00
sanchezvem a9e13c3af3 Fixed error with GroupId il list of groups 2026-04-28 11:15:24 +01:00
sanchezvem 5e6b03356c Cleaned code 2026-04-26 17:19:44 +01:00
sanchezvem 3eacc4976d updated UI 2026-04-26 17:16:45 +01:00
sanchezvem 4e9a4cf428 Added all setting's group (except add user to group) 2026-04-26 17:08:11 +01:00
sanchezvem 4dbf052437 Added all setting's group (except add user to group) 2026-04-26 17:07:55 +01:00
sanchezvem 2c8cb490a4 Optimise code 2026-04-25 18:49:33 +01:00
sanchezvem a229835b1a Cleaned code 2026-04-25 18:18:02 +01:00
sanchezvem 99b4e0ea35 Added creation of groups 2026-04-25 18:14:10 +01:00
sanchezvem 3e6aae68d2 Added vue from groups 2026-04-25 16:12:24 +01:00
sanchezvem a1446f39ca Added vue from groups 2026-04-25 16:11:20 +01:00
sanchezvem 4d82abd8a1 Cleaned code 2026-04-16 17:31:08 +01:00
sanchezvem 6bf8230fa3 Added alternative if we are an empty gallery 2026-04-16 17:02:11 +01:00
sanchezvem 9e729202f0 Added proofs gallery in the app 2026-04-16 16:36:57 +01:00
sanchezvem b08753ebae Added component to change designation 2026-04-16 14:52:26 +01:00
sanchezvem 467cb3f886 Cleaned code 2026-04-16 13:34:32 +01:00
sanchezvem ec9af0c2f3 Created form to update password 2026-04-16 13:34:01 +01:00
sanchezvem c66eb37326 Finished form to edit user infos 2026-04-16 11:43:39 +01:00
sanchezvem 92b85377c2 Implemented form to update profile and fixed labels in sign on form 2026-04-15 20:58:04 +01:00
sanchezvem 9ce8d3051b Cleaned code 2026-04-15 19:18:10 +01:00
sanchezvem cf3aea320e Create a modal window to open a new horizontal window providing access to the selected settings. 2026-04-15 19:17:24 +01:00
sanchezvem 402825d2c0 Added signals to manage settings 2026-04-15 17:13:43 +01:00
sanchezvem ebdb76bf7e Fixed error with loading 2026-04-15 15:39:17 +01:00
sanchezvem ad4a530fb3 Changed touchStart by click 2026-04-15 15:11:28 +01:00
sanchezvem 169abddce7 Added conditions for UI in profil page 2026-04-15 12:36:35 +01:00
sanchezvem d9af61889a Added achievements in profil page 2026-04-15 12:18:00 +01:00
sanchezvem ce5cc5880b Added challenges past in profil page 2026-04-15 11:35:04 +01:00
sanchezvem af04b03bb7 Added new buttons to see all proofs and challenges already execute 2026-04-14 18:04:23 +01:00
sanchezvem b551659a69 Cleaned code with new components 2026-04-14 17:54:44 +01:00
sanchezvem 90243bc671 Started profil page 2026-04-14 15:48:36 +01:00
sanchezvem 83f68a3029 Fixed refresh error with social page 2026-04-14 14:14:44 +01:00
sanchezvem f13541ccab Fixed refresh error with social page 2026-04-14 13:46:17 +01:00
sanchezvem 4df957ddfb Merge remote-tracking branch 'origin/develop' into develop 2026-04-14 12:34:50 +01:00
sanchezvem 8e56b0eced Cleaned code 2026-04-13 23:23:03 +01:00
sanchezvem ee09331baf Added loading and refresh 2026-04-13 23:23:03 +01:00
sanchezvem 697f1e8490 Added lib to compress image into jpeg before send on backend 2026-04-13 23:23:02 +01:00
sanchezvem 97bafde4a4 Cleaned code 2026-04-13 23:21:56 +01:00
sanchezvem 917a0dcc9f Added loading and refresh 2026-04-13 23:18:09 +01:00
sanchezvem f6b2e1dbac Added lib to compresse image into jpeg before send of backend 2026-04-13 22:53:09 +01:00
sanchezvem 2d370b5630 Changed toast message 2026-04-12 22:12:38 +01:00
sanchezvem 5ca9954b0b Fixed error with sending of proofs 2026-04-12 22:10:09 +01:00
sanchezvem 6ab77fe800 Added functions to send and see proof before valid participation from random challenge 2026-04-12 19:06:52 +01:00
sanchezvem 4d5380b40c Add android buil 2026-04-03 10:07:13 +01:00
sanchezvem ffe2ca4f26 Add sdk 2026-04-03 09:22:39 +01:00
sanchezvem d40fbd9a20 Cleaned code 2026-03-29 20:34:36 +01:00
sanchezvem 092dd3b2b1 added input 2026-03-29 20:33:37 +01:00
sanchezvem dc4c751210 Generated new dtos for the app 2026-03-29 19:53:00 +01:00
sanchezvem d5716b69a8 Started card for home page 2026-03-29 12:53:21 +01:00
sanchezvem d43aa9270d Fixed rounded on div in social page 2026-03-29 11:33:41 +01:00
sanchezvem 53f6667035 Added tooltip components 2026-03-29 11:24:09 +01:00
sanchezvem 0a1e30cf69 Started home page 2026-03-29 00:25:09 +01:00
sanchezvem fa937f33bf Cleaned code 2026-03-29 00:24:51 +01:00
sanchezvem c46a437cb2 Fixed error with modal and datas 2026-03-28 23:17:40 +01:00
sanchezvem 5441783eb2 Fixed error with modal and datas 2026-03-28 21:43:06 +01:00
sanchezvem 95d46d7811 Fixed error to display score 2026-03-28 18:30:23 +01:00
sanchezvem 1f8a0e1584 cleaned code 2026-03-28 18:26:51 +01:00
sanchezvem 75251601ae Added searchbar to add friends in the application 2026-03-28 18:26:18 +01:00
sanchezvem d2742f095f Added searchbar to add friends in the application 2026-03-28 18:26:05 +01:00
sanchezvem cf1df4700e cleaned code 2026-03-28 15:46:13 +01:00
sanchezvem fdb4cf119b Created social page 2026-03-28 15:45:09 +01:00
sanchezvem 8dc66363ce Added Capacitor on project 2026-03-27 10:46:04 +01:00
sanchezvem 350313917c added logout on login 2026-03-27 08:34:07 +01:00
sanchezvem 0428a69d15 added ranking and connected with back 2026-03-26 23:21:39 +01:00
sanchezvem 9346bf9ee3 cleaned code and fix css with grid 2026-03-26 19:16:08 +01:00
sanchezvem 57b82b4d3f added ranking 2026-03-26 17:55:23 +01:00
sanchezvem 373daa876a changed notif from login page 2026-03-26 15:36:50 +01:00
sanchezvem d0f519f4b1 cleaned code 2026-03-25 16:14:32 +01:00
sanchezvem 822e22690b Added routing 2026-03-25 16:03:52 +01:00
sanchezvem 5be67500c6 Filter values in all forms of log 2026-03-25 14:08:43 +01:00
sanchezvem 13ad5d369c Added alert 2026-03-25 14:07:24 +01:00
sanchezvem 69c2991c54 implemented routes and error messages in login page 2026-03-24 23:55:53 +01:00
sanchezvem 6ec656fde1 added creation of account but bad request (400) 2026-03-24 19:44:26 +01:00
sanchezvem ef2afb0b58 added interceptor and authService to manage log 2026-03-24 19:08:34 +01:00
sanchezvem 491c57b061 changed sentence 2026-03-24 11:48:53 +01:00
sanchezvem 085a3a507a cleaned code 2026-03-24 11:43:48 +01:00
sanchezvem 870032a733 cleaned code 2026-03-24 11:42:56 +01:00
sanchezvem d7a6d363f8 added dynamic login page 2026-03-24 11:35:26 +01:00
sanchezvem 692e2aef82 added blob into login page 2026-03-24 10:42:30 +01:00
sanchezvem 95f92229a4 created login page 2026-03-24 09:51:47 +01:00
sanchezvem c24103a2ad Started login page 2026-03-23 23:29:38 +01:00
sanchezvem 2a7678d57b added tempo logo 2026-03-14 15:39:41 +01:00
sanchezvem c15e8d9a57 added tailwind css 2026-03-14 15:39:20 +01:00
sanchezvem 3ea7d5e68a Added openapi generator 2026-03-12 17:44:24 +01:00
sanchezvem 2c0381964c added ionic library in project 2026-03-12 17:25:59 +01:00
225 changed files with 12735 additions and 461 deletions
+5
View File
@@ -0,0 +1,5 @@
{
"plugins": {
"@tailwindcss/postcss": {}
}
}
+101
View File
@@ -0,0 +1,101 @@
# Using Android gitignore template: https://github.com/github/gitignore/blob/HEAD/Android.gitignore
# Built application files
*.apk
*.aar
*.ap_
*.aab
# Files for the ART/Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
out/
# Uncomment the following line in case you need and you don't have the release build type files in your app
# release/
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
# Android Studio Navigation editor temp files
.navigation/
# Android Studio captures folder
captures/
# IntelliJ
*.iml
.idea/workspace.xml
.idea/tasks.xml
.idea/gradle.xml
.idea/assetWizardSettings.xml
.idea/dictionaries
.idea/libraries
# Android Studio 3 in .gitignore file.
.idea/caches
.idea/modules.xml
# Comment next line if keeping position of elements in Navigation Editor is relevant for you
.idea/navEditor.xml
# Keystore files
# Uncomment the following lines if you do not want to check your keystore files in.
#*.jks
#*.keystore
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
.cxx/
# Google Services (e.g. APIs or Firebase)
# google-services.json
# Freeline
freeline.py
freeline/
freeline_project_description.json
# fastlane
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
fastlane/readme.md
# Version control
vcs.xml
# lint
lint/intermediates/
lint/generated/
lint/outputs/
lint/tmp/
# lint/reports/
# Android Profiling
*.hprof
# Cordova plugins for Capacitor
capacitor-cordova-android-plugins
# Copied web assets
app/src/main/assets/public
# Generated Config files
app/src/main/assets/capacitor.config.json
app/src/main/assets/capacitor.plugins.json
app/src/main/res/xml/config.xml
+2
View File
@@ -0,0 +1,2 @@
/build/*
!/build/.npmkeep
+54
View File
@@ -0,0 +1,54 @@
apply plugin: 'com.android.application'
android {
namespace = "com.mesc.beready"
compileSdk = rootProject.ext.compileSdkVersion
defaultConfig {
applicationId "com.mesc.beready"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
// Default: https://android.googlesource.com/platform/frameworks/base/+/282e181b58cf72b6ca770dc7ca5f91f135444502/tools/aapt/AaptAssets.cpp#61
ignoreAssetsPattern = '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~'
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
repositories {
flatDir{
dirs '../capacitor-cordova-android-plugins/src/main/libs', 'libs'
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion"
implementation "androidx.core:core-splashscreen:$coreSplashScreenVersion"
implementation project(':capacitor-android')
testImplementation "junit:junit:$junitVersion"
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
implementation project(':capacitor-cordova-android-plugins')
}
apply from: 'capacitor.build.gradle'
try {
def servicesJSON = file('google-services.json')
if (servicesJSON.text) {
apply plugin: 'com.google.gms.google-services'
}
} catch(Exception e) {
logger.info("google-services.json not found, google-services plugin not applied. Push Notifications won't work")
}
+19
View File
@@ -0,0 +1,19 @@
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_21
targetCompatibility JavaVersion.VERSION_21
}
}
apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
dependencies {
}
if (hasProperty('postBuildExtras')) {
postBuildExtras()
}
+21
View File
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
@@ -0,0 +1,26 @@
package com.getcapacitor.myapp;
import static org.junit.Assert.*;
import android.content.Context;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("com.getcapacitor.app", appContext.getPackageName());
}
}
+43
View File
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode|navigation|density"
android:name=".MainActivity"
android:label="@string/title_activity_main"
android:theme="@style/AppTheme.NoActionBarLaunch"
android:launchMode="singleTask"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"></meta-data>
</provider>
</application>
<!-- Permissions -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
</manifest>
@@ -0,0 +1,5 @@
package com.mesc.beready;
import com.getcapacitor.BridgeActivity;
public class MainActivity extends BridgeActivity {}
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

@@ -0,0 +1,34 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillType="evenOdd"
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
android:strokeColor="#00000000"
android:strokeWidth="1">
<aapt:attr name="android:fillColor">
<gradient
android:endX="78.5885"
android:endY="90.9159"
android:startX="48.7653"
android:startY="61.0927"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
</vector>
@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillColor="#26A69A"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
</vector>
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<WebView
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#FFFFFF</color>
</resources>
@@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="app_name">BeReadyFrontend</string>
<string name="title_activity_main">BeReadyFrontend</string>
<string name="package_name">com.mesc.beready</string>
<string name="custom_url_scheme">com.mesc.beready</string>
</resources>
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="AppTheme.NoActionBar" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:background">@null</item>
</style>
<style name="AppTheme.NoActionBarLaunch" parent="Theme.SplashScreen">
<item name="android:background">@drawable/splash</item>
</style>
</resources>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_images" path="." />
<cache-path name="my_cache_images" path="." />
</paths>
@@ -0,0 +1,18 @@
package com.getcapacitor.myapp;
import static org.junit.Assert.*;
import org.junit.Test;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
+29
View File
@@ -0,0 +1,29 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.13.0'
classpath 'com.google.gms:google-services:4.4.4'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
apply from: "variables.gradle"
allprojects {
repositories {
google()
mavenCentral()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
+3
View File
@@ -0,0 +1,3 @@
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
include ':capacitor-android'
project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor')
+22
View File
@@ -0,0 +1,22 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
Binary file not shown.
+7
View File
@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored Executable
+251
View File
@@ -0,0 +1,251 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
+94
View File
@@ -0,0 +1,94 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
set CLASSPATH=
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
+5
View File
@@ -0,0 +1,5 @@
include ':app'
include ':capacitor-cordova-android-plugins'
project(':capacitor-cordova-android-plugins').projectDir = new File('./capacitor-cordova-android-plugins/')
apply from: 'capacitor.settings.gradle'
+16
View File
@@ -0,0 +1,16 @@
ext {
minSdkVersion = 24
compileSdkVersion = 36
targetSdkVersion = 36
androidxActivityVersion = '1.11.0'
androidxAppCompatVersion = '1.7.1'
androidxCoordinatorLayoutVersion = '1.3.0'
androidxCoreVersion = '1.17.0'
androidxFragmentVersion = '1.8.9'
coreSplashScreenVersion = '1.2.0'
androidxWebkitVersion = '1.14.0'
junitVersion = '4.13.2'
androidxJunitVersion = '1.3.0'
androidxEspressoCoreVersion = '3.7.0'
cordovaAndroidVersion = '14.0.1'
}
+73 -2
View File
@@ -50,7 +50,40 @@
} }
], ],
"styles": [ "styles": [
"src/styles.css" "src/styles.css",
{
"input": "node_modules/@ionic/angular/css/core.css"
},
{
"input": "node_modules/@ionic/angular/css/normalize.css"
},
{
"input": "node_modules/@ionic/angular/css/structure.css"
},
{
"input": "node_modules/@ionic/angular/css/typography.css"
},
{
"input": "node_modules/@ionic/angular/css/display.css"
},
{
"input": "node_modules/@ionic/angular/css/padding.css"
},
{
"input": "node_modules/@ionic/angular/css/float-elements.css"
},
{
"input": "node_modules/@ionic/angular/css/text-alignment.css"
},
{
"input": "node_modules/@ionic/angular/css/text-transformation.css"
},
{
"input": "node_modules/@ionic/angular/css/flex-utils.css"
},
{
"input": "src/theme/variables.css"
}
] ]
}, },
"configurations": { "configurations": {
@@ -91,8 +124,46 @@
}, },
"extract-i18n": { "extract-i18n": {
"builder": "@angular/build:extract-i18n" "builder": "@angular/build:extract-i18n"
},
"ionic-cordova-serve": {
"builder": "@ionic/angular-toolkit:cordova-serve",
"options": {
"cordovaBuildTarget": "BeReadyFrontend:ionic-cordova-build",
"devServerTarget": "BeReadyFrontend:serve"
},
"configurations": {
"production": {
"cordovaBuildTarget": "BeReadyFrontend:ionic-cordova-build:production",
"devServerTarget": "BeReadyFrontend:serve:production"
}
}
},
"ionic-cordova-build": {
"builder": "@ionic/angular-toolkit:cordova-build",
"options": {
"browserTarget": "BeReadyFrontend:build"
},
"configurations": {
"production": {
"browserTarget": "BeReadyFrontend:build:production"
}
}
} }
} }
} }
},
"cli": {
"schematicCollections": [
"@ionic/angular-toolkit"
],
"analytics": "e8e950b1-4841-478c-ba40-4e4328545e20"
},
"schematics": {
"@ionic/angular-toolkit:component": {
"styleext": "scss"
},
"@ionic/angular-toolkit:page": {
"styleext": "scss"
}
} }
} }
+9
View File
@@ -0,0 +1,9 @@
import type { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
appId: 'com.mesc.beready',
appName: 'BeReadyFrontend',
webDir: 'dist/BeReadyFrontend/browser',
};
export default config;
+6
View File
@@ -0,0 +1,6 @@
{
"name": "ionic-app",
"app_id": "",
"type": "angular-standalone",
"integrations": {}
}
+3
View File
@@ -0,0 +1,3 @@
additionalProperties:
fileNaming: kebab-case
modelPropertyNaming: camelCase
+7
View File
@@ -0,0 +1,7 @@
{
"$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json",
"spaces": 2,
"generator-cli": {
"version": "7.20.0"
}
}
+3050 -87
View File
File diff suppressed because it is too large Load Diff
+18 -2
View File
@@ -5,7 +5,8 @@
"ng": "ng", "ng": "ng",
"start": "ng serve", "start": "ng serve",
"build": "ng build", "build": "ng build",
"watch": "ng build --watch --configuration development" "watch": "ng build --watch --configuration development",
"openapi": "rimraf src/app/services/api && openapi-generator-cli generate -i http://localhost:5235/swagger/v1/swagger.json -g typescript-angular -o src/app/services/api -c openapi-generator.yaml"
}, },
"prettier": { "prettier": {
"printWidth": 100, "printWidth": 100,
@@ -27,7 +28,20 @@
"@angular/forms": "^20.3.0", "@angular/forms": "^20.3.0",
"@angular/platform-browser": "^20.3.0", "@angular/platform-browser": "^20.3.0",
"@angular/router": "^20.3.0", "@angular/router": "^20.3.0",
"@capacitor/android": "^8.3.0",
"@capacitor/angular": "^2.0.3",
"@capacitor/core": "latest",
"@ionic/angular": "^8.8.1",
"@microsoft/signalr": "^10.0.0",
"@openapitools/openapi-generator-cli": "^2.30.2",
"@tailwindcss/postcss": "^4.2.1",
"browser-image-compression": "^2.0.2",
"jwt-decode": "^4.0.0",
"moment": "^2.30.1",
"postcss": "^8.5.8",
"rimraf": "^6.1.3",
"rxjs": "~7.8.0", "rxjs": "~7.8.0",
"tailwindcss": "^4.2.1",
"tslib": "^2.3.0", "tslib": "^2.3.0",
"zone.js": "~0.15.0" "zone.js": "~0.15.0"
}, },
@@ -35,6 +49,8 @@
"@angular/build": "^20.3.9", "@angular/build": "^20.3.9",
"@angular/cli": "^20.3.9", "@angular/cli": "^20.3.9",
"@angular/compiler-cli": "^20.3.0", "@angular/compiler-cli": "^20.3.0",
"@capacitor/cli": "latest",
"@ionic/angular-toolkit": "latest",
"typescript": "~5.9.2" "typescript": "~5.9.2"
} }
} }
Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" standalone="no"?>
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
<path fill="#F2F4F8" d="M45.1,-79.7C57.7,-70.9,66.4,-57.2,69.5,-43.1C72.6,-29,70,-14.5,71.7,1C73.5,16.5,79.6,33,75.1,44.6C70.6,56.2,55.4,63,41.1,70.5C26.9,78.1,13.4,86.3,-0.2,86.6C-13.7,86.9,-27.5,79.2,-40.1,70.7C-52.7,62.2,-64.3,53,-69.6,41.1C-74.9,29.2,-74.1,14.6,-72.7,0.8C-71.3,-13,-69.4,-26,-62.8,-35.7C-56.2,-45.5,-45,-52.1,-33.8,-61.6C-22.6,-71.1,-11.3,-83.5,2.5,-87.9C16.3,-92.2,32.6,-88.4,45.1,-79.7Z" transform="translate(100 100)" />
</svg>

After

Width:  |  Height:  |  Size: 555 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

+12 -8
View File
@@ -1,12 +1,16 @@
import { ApplicationConfig, provideBrowserGlobalErrorListeners, provideZoneChangeDetection } from '@angular/core'; import {ApplicationConfig, provideBrowserGlobalErrorListeners, provideZoneChangeDetection} from '@angular/core';
import { provideRouter } from '@angular/router'; import {provideRouter} from '@angular/router';
import { routes } from './app.routes'; import {routes} from './app.routes';
import {provideIonicAngular} from '@ionic/angular/standalone';
import {provideHttpClient, withInterceptors} from "@angular/common/http";
import {authInterceptor} from "./interceptors/auth-interceptor";
export const appConfig: ApplicationConfig = { export const appConfig: ApplicationConfig = {
providers: [ providers: [
provideBrowserGlobalErrorListeners(), provideBrowserGlobalErrorListeners(),
provideZoneChangeDetection({ eventCoalescing: true }), provideZoneChangeDetection({eventCoalescing: true}),
provideRouter(routes) provideRouter(routes), provideIonicAngular({}),
] provideHttpClient(withInterceptors([authInterceptor]))
]
}; };
+3 -342
View File
@@ -1,342 +1,3 @@
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * --> <ion-app>
<!-- * * * * * * * * * * * The content below * * * * * * * * * * * --> <ion-router-outlet></ion-router-outlet>
<!-- * * * * * * * * * * is only a placeholder * * * * * * * * * * --> </ion-app>
<!-- * * * * * * * * * * and can be replaced. * * * * * * * * * * -->
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
<!-- * * * * * * * * * Delete the template below * * * * * * * * * -->
<!-- * * * * * * * to get started with your project! * * * * * * * -->
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
<style>
:host {
--bright-blue: oklch(51.01% 0.274 263.83);
--electric-violet: oklch(53.18% 0.28 296.97);
--french-violet: oklch(47.66% 0.246 305.88);
--vivid-pink: oklch(69.02% 0.277 332.77);
--hot-red: oklch(61.42% 0.238 15.34);
--orange-red: oklch(63.32% 0.24 31.68);
--gray-900: oklch(19.37% 0.006 300.98);
--gray-700: oklch(36.98% 0.014 302.71);
--gray-400: oklch(70.9% 0.015 304.04);
--red-to-pink-to-purple-vertical-gradient: linear-gradient(
180deg,
var(--orange-red) 0%,
var(--vivid-pink) 50%,
var(--electric-violet) 100%
);
--red-to-pink-to-purple-horizontal-gradient: linear-gradient(
90deg,
var(--orange-red) 0%,
var(--vivid-pink) 50%,
var(--electric-violet) 100%
);
--pill-accent: var(--bright-blue);
font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
"Segoe UI Symbol";
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
h1 {
font-size: 3.125rem;
color: var(--gray-900);
font-weight: 500;
line-height: 100%;
letter-spacing: -0.125rem;
margin: 0;
font-family: "Inter Tight", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
"Segoe UI Symbol";
}
p {
margin: 0;
color: var(--gray-700);
}
main {
width: 100%;
min-height: 100%;
display: flex;
justify-content: center;
align-items: center;
padding: 1rem;
box-sizing: inherit;
position: relative;
}
.angular-logo {
max-width: 9.2rem;
}
.content {
display: flex;
justify-content: space-around;
width: 100%;
max-width: 700px;
margin-bottom: 3rem;
}
.content h1 {
margin-top: 1.75rem;
}
.content p {
margin-top: 1.5rem;
}
.divider {
width: 1px;
background: var(--red-to-pink-to-purple-vertical-gradient);
margin-inline: 0.5rem;
}
.pill-group {
display: flex;
flex-direction: column;
align-items: start;
flex-wrap: wrap;
gap: 1.25rem;
}
.pill {
display: flex;
align-items: center;
--pill-accent: var(--bright-blue);
background: color-mix(in srgb, var(--pill-accent) 5%, transparent);
color: var(--pill-accent);
padding-inline: 0.75rem;
padding-block: 0.375rem;
border-radius: 2.75rem;
border: 0;
transition: background 0.3s ease;
font-family: var(--inter-font);
font-size: 0.875rem;
font-style: normal;
font-weight: 500;
line-height: 1.4rem;
letter-spacing: -0.00875rem;
text-decoration: none;
white-space: nowrap;
}
.pill:hover {
background: color-mix(in srgb, var(--pill-accent) 15%, transparent);
}
.pill-group .pill:nth-child(6n + 1) {
--pill-accent: var(--bright-blue);
}
.pill-group .pill:nth-child(6n + 2) {
--pill-accent: var(--electric-violet);
}
.pill-group .pill:nth-child(6n + 3) {
--pill-accent: var(--french-violet);
}
.pill-group .pill:nth-child(6n + 4),
.pill-group .pill:nth-child(6n + 5),
.pill-group .pill:nth-child(6n + 6) {
--pill-accent: var(--hot-red);
}
.pill-group svg {
margin-inline-start: 0.25rem;
}
.social-links {
display: flex;
align-items: center;
gap: 0.73rem;
margin-top: 1.5rem;
}
.social-links path {
transition: fill 0.3s ease;
fill: var(--gray-400);
}
.social-links a:hover svg path {
fill: var(--gray-900);
}
@media screen and (max-width: 650px) {
.content {
flex-direction: column;
width: max-content;
}
.divider {
height: 1px;
width: 100%;
background: var(--red-to-pink-to-purple-horizontal-gradient);
margin-block: 1.5rem;
}
}
</style>
<main class="main">
<div class="content">
<div class="left-side">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 982 239"
fill="none"
class="angular-logo"
>
<g clip-path="url(#a)">
<path
fill="url(#b)"
d="M388.676 191.625h30.849L363.31 31.828h-35.758l-56.215 159.797h30.848l13.174-39.356h60.061l13.256 39.356Zm-65.461-62.675 21.602-64.311h1.227l21.602 64.311h-44.431Zm126.831-7.527v70.202h-28.23V71.839h27.002v20.374h1.392c2.782-6.71 7.2-12.028 13.255-15.956 6.056-3.927 13.584-5.89 22.503-5.89 8.264 0 15.465 1.8 21.684 5.318 6.137 3.518 10.964 8.673 14.319 15.382 3.437 6.71 5.074 14.81 4.992 24.383v76.175h-28.23v-71.92c0-8.019-2.046-14.237-6.219-18.819-4.173-4.5-9.819-6.791-17.102-6.791-4.91 0-9.328 1.063-13.174 3.272-3.846 2.128-6.792 5.237-9.001 9.328-2.046 4.009-3.191 8.918-3.191 14.728ZM589.233 239c-10.147 0-18.82-1.391-26.103-4.091-7.282-2.7-13.092-6.382-17.511-10.964-4.418-4.582-7.528-9.655-9.164-15.219l25.448-6.136c1.145 2.372 2.782 4.663 4.991 6.954 2.209 2.291 5.155 4.255 8.837 5.81 3.683 1.554 8.428 2.291 14.074 2.291 8.019 0 14.647-1.964 19.884-5.81 5.237-3.845 7.856-10.227 7.856-19.064v-22.665h-1.391c-1.473 2.946-3.601 5.892-6.383 9.001-2.782 3.109-6.464 5.645-10.965 7.691-4.582 2.046-10.228 3.109-17.101 3.109-9.165 0-17.511-2.209-25.039-6.545-7.446-4.337-13.42-10.883-17.757-19.474-4.418-8.673-6.628-19.473-6.628-32.565 0-13.091 2.21-24.301 6.628-33.383 4.419-9.082 10.311-15.955 17.839-20.7 7.528-4.746 15.874-7.037 25.039-7.037 7.037 0 12.846 1.145 17.347 3.518 4.582 2.373 8.182 5.236 10.883 8.51 2.7 3.272 4.746 6.382 6.137 9.327h1.554v-19.8h27.821v121.749c0 10.228-2.454 18.737-7.364 25.447-4.91 6.709-11.538 11.7-20.048 15.055-8.509 3.355-18.165 4.991-28.884 4.991Zm.245-71.266c5.974 0 11.047-1.473 15.302-4.337 4.173-2.945 7.446-7.118 9.573-12.519 2.21-5.482 3.274-12.027 3.274-19.637 0-7.609-1.064-14.155-3.274-19.8-2.127-5.646-5.318-10.064-9.491-13.255-4.174-3.11-9.329-4.746-15.384-4.746s-11.537 1.636-15.792 4.91c-4.173 3.272-7.365 7.772-9.492 13.418-2.128 5.727-3.191 12.191-3.191 19.392 0 7.2 1.063 13.745 3.273 19.228 2.127 5.482 5.318 9.736 9.573 12.764 4.174 3.027 9.41 4.582 15.629 4.582Zm141.56-26.51V71.839h28.23v119.786h-27.412v-21.273h-1.227c-2.7 6.709-7.119 12.191-13.338 16.446-6.137 4.255-13.747 6.382-22.748 6.382-7.855 0-14.81-1.718-20.783-5.237-5.974-3.518-10.72-8.591-14.075-15.382-3.355-6.709-5.073-14.891-5.073-24.464V71.839h28.312v71.921c0 7.609 2.046 13.664 6.219 18.083 4.173 4.5 9.655 6.709 16.365 6.709 4.173 0 8.183-.982 12.111-3.028 3.927-2.045 7.118-5.072 9.655-9.082 2.537-4.091 3.764-9.164 3.764-15.218Zm65.707-109.395v159.796h-28.23V31.828h28.23Zm44.841 162.169c-7.61 0-14.402-1.391-20.457-4.091-6.055-2.7-10.883-6.791-14.32-12.109-3.518-5.319-5.237-11.946-5.237-19.801 0-6.791 1.228-12.355 3.765-16.773 2.536-4.419 5.891-7.937 10.228-10.637 4.337-2.618 9.164-4.664 14.647-6.055 5.4-1.391 11.046-2.373 16.856-3.027 7.037-.737 12.683-1.391 17.102-1.964 4.337-.573 7.528-1.555 9.574-2.782 1.963-1.309 3.027-3.273 3.027-5.973v-.491c0-5.891-1.718-10.391-5.237-13.664-3.518-3.191-8.51-4.828-15.056-4.828-6.955 0-12.356 1.473-16.447 4.5-4.009 3.028-6.71 6.546-8.183 10.719l-26.348-3.764c2.046-7.282 5.483-13.336 10.31-18.328 4.746-4.909 10.638-8.59 17.511-11.045 6.955-2.455 14.565-3.682 22.912-3.682 5.809 0 11.537.654 17.265 2.045s10.965 3.6 15.711 6.71c4.746 3.109 8.51 7.282 11.455 12.6 2.864 5.318 4.337 11.946 4.337 19.883v80.184h-27.166v-16.446h-.9c-1.719 3.355-4.092 6.464-7.201 9.328-3.109 2.864-6.955 5.237-11.619 6.955-4.828 1.718-10.229 2.536-16.529 2.536Zm7.364-20.701c5.646 0 10.556-1.145 14.729-3.354 4.173-2.291 7.364-5.237 9.655-9.001 2.292-3.763 3.355-7.854 3.355-12.273v-14.155c-.9.737-2.373 1.391-4.5 2.046-2.128.654-4.419 1.145-7.037 1.636-2.619.491-5.155.9-7.692 1.227-2.537.328-4.746.655-6.628.901-4.173.572-8.019 1.472-11.292 2.781-3.355 1.31-5.973 3.11-7.855 5.401-1.964 2.291-2.864 5.318-2.864 8.918 0 5.237 1.882 9.164 5.728 11.782 3.682 2.782 8.51 4.091 14.401 4.091Zm64.643 18.328V71.839h27.412v19.965h1.227c2.21-6.955 5.974-12.274 11.292-16.038 5.319-3.763 11.456-5.645 18.329-5.645 1.555 0 3.355.082 5.237.163 1.964.164 3.601.328 4.91.573v25.938c-1.227-.41-3.109-.819-5.646-1.146a58.814 58.814 0 0 0-7.446-.49c-5.155 0-9.738 1.145-13.829 3.354-4.091 2.209-7.282 5.236-9.655 9.164-2.373 3.927-3.519 8.427-3.519 13.5v70.448h-28.312ZM222.077 39.192l-8.019 125.923L137.387 0l84.69 39.192Zm-53.105 162.825-57.933 33.056-57.934-33.056 11.783-28.556h92.301l11.783 28.556ZM111.039 62.675l30.357 73.803H80.681l30.358-73.803ZM7.937 165.115 0 39.192 84.69 0 7.937 165.115Z"
/>
<path
fill="url(#c)"
d="M388.676 191.625h30.849L363.31 31.828h-35.758l-56.215 159.797h30.848l13.174-39.356h60.061l13.256 39.356Zm-65.461-62.675 21.602-64.311h1.227l21.602 64.311h-44.431Zm126.831-7.527v70.202h-28.23V71.839h27.002v20.374h1.392c2.782-6.71 7.2-12.028 13.255-15.956 6.056-3.927 13.584-5.89 22.503-5.89 8.264 0 15.465 1.8 21.684 5.318 6.137 3.518 10.964 8.673 14.319 15.382 3.437 6.71 5.074 14.81 4.992 24.383v76.175h-28.23v-71.92c0-8.019-2.046-14.237-6.219-18.819-4.173-4.5-9.819-6.791-17.102-6.791-4.91 0-9.328 1.063-13.174 3.272-3.846 2.128-6.792 5.237-9.001 9.328-2.046 4.009-3.191 8.918-3.191 14.728ZM589.233 239c-10.147 0-18.82-1.391-26.103-4.091-7.282-2.7-13.092-6.382-17.511-10.964-4.418-4.582-7.528-9.655-9.164-15.219l25.448-6.136c1.145 2.372 2.782 4.663 4.991 6.954 2.209 2.291 5.155 4.255 8.837 5.81 3.683 1.554 8.428 2.291 14.074 2.291 8.019 0 14.647-1.964 19.884-5.81 5.237-3.845 7.856-10.227 7.856-19.064v-22.665h-1.391c-1.473 2.946-3.601 5.892-6.383 9.001-2.782 3.109-6.464 5.645-10.965 7.691-4.582 2.046-10.228 3.109-17.101 3.109-9.165 0-17.511-2.209-25.039-6.545-7.446-4.337-13.42-10.883-17.757-19.474-4.418-8.673-6.628-19.473-6.628-32.565 0-13.091 2.21-24.301 6.628-33.383 4.419-9.082 10.311-15.955 17.839-20.7 7.528-4.746 15.874-7.037 25.039-7.037 7.037 0 12.846 1.145 17.347 3.518 4.582 2.373 8.182 5.236 10.883 8.51 2.7 3.272 4.746 6.382 6.137 9.327h1.554v-19.8h27.821v121.749c0 10.228-2.454 18.737-7.364 25.447-4.91 6.709-11.538 11.7-20.048 15.055-8.509 3.355-18.165 4.991-28.884 4.991Zm.245-71.266c5.974 0 11.047-1.473 15.302-4.337 4.173-2.945 7.446-7.118 9.573-12.519 2.21-5.482 3.274-12.027 3.274-19.637 0-7.609-1.064-14.155-3.274-19.8-2.127-5.646-5.318-10.064-9.491-13.255-4.174-3.11-9.329-4.746-15.384-4.746s-11.537 1.636-15.792 4.91c-4.173 3.272-7.365 7.772-9.492 13.418-2.128 5.727-3.191 12.191-3.191 19.392 0 7.2 1.063 13.745 3.273 19.228 2.127 5.482 5.318 9.736 9.573 12.764 4.174 3.027 9.41 4.582 15.629 4.582Zm141.56-26.51V71.839h28.23v119.786h-27.412v-21.273h-1.227c-2.7 6.709-7.119 12.191-13.338 16.446-6.137 4.255-13.747 6.382-22.748 6.382-7.855 0-14.81-1.718-20.783-5.237-5.974-3.518-10.72-8.591-14.075-15.382-3.355-6.709-5.073-14.891-5.073-24.464V71.839h28.312v71.921c0 7.609 2.046 13.664 6.219 18.083 4.173 4.5 9.655 6.709 16.365 6.709 4.173 0 8.183-.982 12.111-3.028 3.927-2.045 7.118-5.072 9.655-9.082 2.537-4.091 3.764-9.164 3.764-15.218Zm65.707-109.395v159.796h-28.23V31.828h28.23Zm44.841 162.169c-7.61 0-14.402-1.391-20.457-4.091-6.055-2.7-10.883-6.791-14.32-12.109-3.518-5.319-5.237-11.946-5.237-19.801 0-6.791 1.228-12.355 3.765-16.773 2.536-4.419 5.891-7.937 10.228-10.637 4.337-2.618 9.164-4.664 14.647-6.055 5.4-1.391 11.046-2.373 16.856-3.027 7.037-.737 12.683-1.391 17.102-1.964 4.337-.573 7.528-1.555 9.574-2.782 1.963-1.309 3.027-3.273 3.027-5.973v-.491c0-5.891-1.718-10.391-5.237-13.664-3.518-3.191-8.51-4.828-15.056-4.828-6.955 0-12.356 1.473-16.447 4.5-4.009 3.028-6.71 6.546-8.183 10.719l-26.348-3.764c2.046-7.282 5.483-13.336 10.31-18.328 4.746-4.909 10.638-8.59 17.511-11.045 6.955-2.455 14.565-3.682 22.912-3.682 5.809 0 11.537.654 17.265 2.045s10.965 3.6 15.711 6.71c4.746 3.109 8.51 7.282 11.455 12.6 2.864 5.318 4.337 11.946 4.337 19.883v80.184h-27.166v-16.446h-.9c-1.719 3.355-4.092 6.464-7.201 9.328-3.109 2.864-6.955 5.237-11.619 6.955-4.828 1.718-10.229 2.536-16.529 2.536Zm7.364-20.701c5.646 0 10.556-1.145 14.729-3.354 4.173-2.291 7.364-5.237 9.655-9.001 2.292-3.763 3.355-7.854 3.355-12.273v-14.155c-.9.737-2.373 1.391-4.5 2.046-2.128.654-4.419 1.145-7.037 1.636-2.619.491-5.155.9-7.692 1.227-2.537.328-4.746.655-6.628.901-4.173.572-8.019 1.472-11.292 2.781-3.355 1.31-5.973 3.11-7.855 5.401-1.964 2.291-2.864 5.318-2.864 8.918 0 5.237 1.882 9.164 5.728 11.782 3.682 2.782 8.51 4.091 14.401 4.091Zm64.643 18.328V71.839h27.412v19.965h1.227c2.21-6.955 5.974-12.274 11.292-16.038 5.319-3.763 11.456-5.645 18.329-5.645 1.555 0 3.355.082 5.237.163 1.964.164 3.601.328 4.91.573v25.938c-1.227-.41-3.109-.819-5.646-1.146a58.814 58.814 0 0 0-7.446-.49c-5.155 0-9.738 1.145-13.829 3.354-4.091 2.209-7.282 5.236-9.655 9.164-2.373 3.927-3.519 8.427-3.519 13.5v70.448h-28.312ZM222.077 39.192l-8.019 125.923L137.387 0l84.69 39.192Zm-53.105 162.825-57.933 33.056-57.934-33.056 11.783-28.556h92.301l11.783 28.556ZM111.039 62.675l30.357 73.803H80.681l30.358-73.803ZM7.937 165.115 0 39.192 84.69 0 7.937 165.115Z"
/>
</g>
<defs>
<radialGradient
id="c"
cx="0"
cy="0"
r="1"
gradientTransform="rotate(118.122 171.182 60.81) scale(205.794)"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#FF41F8" />
<stop offset=".707" stop-color="#FF41F8" stop-opacity=".5" />
<stop offset="1" stop-color="#FF41F8" stop-opacity="0" />
</radialGradient>
<linearGradient
id="b"
x1="0"
x2="982"
y1="192"
y2="192"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#F0060B" />
<stop offset="0" stop-color="#F0070C" />
<stop offset=".526" stop-color="#CC26D5" />
<stop offset="1" stop-color="#7702FF" />
</linearGradient>
<clipPath id="a"><path fill="#fff" d="M0 0h982v239H0z" /></clipPath>
</defs>
</svg>
<h1>Hello, {{ title() }}</h1>
<p>Congratulations! Your app is running. 🎉</p>
</div>
<div class="divider" role="separator" aria-label="Divider"></div>
<div class="right-side">
<div class="pill-group">
@for (item of [
{ title: 'Explore the Docs', link: 'https://angular.dev' },
{ title: 'Learn with Tutorials', link: 'https://angular.dev/tutorials' },
{ title: 'Prompt and best practices for AI', link: 'https://angular.dev/ai/develop-with-ai'},
{ title: 'CLI Docs', link: 'https://angular.dev/tools/cli' },
{ title: 'Angular Language Service', link: 'https://angular.dev/tools/language-service' },
{ title: 'Angular DevTools', link: 'https://angular.dev/tools/devtools' },
]; track item.title) {
<a
class="pill"
[href]="item.link"
target="_blank"
rel="noopener"
>
<span>{{ item.title }}</span>
<svg
xmlns="http://www.w3.org/2000/svg"
height="14"
viewBox="0 -960 960 960"
width="14"
fill="currentColor"
>
<path
d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h280v80H200v560h560v-280h80v280q0 33-23.5 56.5T760-120H200Zm188-212-56-56 372-372H560v-80h280v280h-80v-144L388-332Z"
/>
</svg>
</a>
}
</div>
<div class="social-links">
<a
href="https://github.com/angular/angular"
aria-label="Github"
target="_blank"
rel="noopener"
>
<svg
width="25"
height="24"
viewBox="0 0 25 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
alt="Github"
>
<path
d="M12.3047 0C5.50634 0 0 5.50942 0 12.3047C0 17.7423 3.52529 22.3535 8.41332 23.9787C9.02856 24.0946 9.25414 23.7142 9.25414 23.3871C9.25414 23.0949 9.24389 22.3207 9.23876 21.2953C5.81601 22.0377 5.09414 19.6444 5.09414 19.6444C4.53427 18.2243 3.72524 17.8449 3.72524 17.8449C2.61064 17.082 3.81137 17.0973 3.81137 17.0973C5.04697 17.1835 5.69604 18.3647 5.69604 18.3647C6.79321 20.2463 8.57636 19.7029 9.27978 19.3881C9.39052 18.5924 9.70736 18.0499 10.0591 17.7423C7.32641 17.4347 4.45429 16.3765 4.45429 11.6618C4.45429 10.3185 4.9311 9.22133 5.72065 8.36C5.58222 8.04931 5.16694 6.79833 5.82831 5.10337C5.82831 5.10337 6.85883 4.77319 9.2121 6.36459C10.1965 6.09082 11.2424 5.95546 12.2883 5.94931C13.3342 5.95546 14.3801 6.09082 15.3644 6.36459C17.7023 4.77319 18.7328 5.10337 18.7328 5.10337C19.3942 6.79833 18.9789 8.04931 18.8559 8.36C19.6403 9.22133 20.1171 10.3185 20.1171 11.6618C20.1171 16.3888 17.2409 17.4296 14.5031 17.7321C14.9338 18.1012 15.3337 18.8559 15.3337 20.0084C15.3337 21.6552 15.3183 22.978 15.3183 23.3779C15.3183 23.7009 15.5336 24.0854 16.1642 23.9623C21.0871 22.3484 24.6094 17.7341 24.6094 12.3047C24.6094 5.50942 19.0999 0 12.3047 0Z"
/>
</svg>
</a>
<a
href="https://twitter.com/angular"
aria-label="Twitter"
target="_blank"
rel="noopener"
>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
alt="Twitter"
>
<path
d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"
/>
</svg>
</a>
<a
href="https://www.youtube.com/channel/UCbn1OgGei-DV7aSRo_HaAiw"
aria-label="Youtube"
target="_blank"
rel="noopener"
>
<svg
width="29"
height="20"
viewBox="0 0 29 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
alt="Youtube"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M27.4896 1.52422C27.9301 1.96749 28.2463 2.51866 28.4068 3.12258C29.0004 5.35161 29.0004 10 29.0004 10C29.0004 10 29.0004 14.6484 28.4068 16.8774C28.2463 17.4813 27.9301 18.0325 27.4896 18.4758C27.0492 18.9191 26.5 19.2389 25.8972 19.4032C23.6778 20 14.8068 20 14.8068 20C14.8068 20 5.93586 20 3.71651 19.4032C3.11363 19.2389 2.56449 18.9191 2.12405 18.4758C1.68361 18.0325 1.36732 17.4813 1.20683 16.8774C0.613281 14.6484 0.613281 10 0.613281 10C0.613281 10 0.613281 5.35161 1.20683 3.12258C1.36732 2.51866 1.68361 1.96749 2.12405 1.52422C2.56449 1.08095 3.11363 0.76113 3.71651 0.596774C5.93586 0 14.8068 0 14.8068 0C14.8068 0 23.6778 0 25.8972 0.596774C26.5 0.76113 27.0492 1.08095 27.4896 1.52422ZM19.3229 10L11.9036 5.77905V14.221L19.3229 10Z"
/>
</svg>
</a>
</div>
</div>
</div>
</main>
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
<!-- * * * * * * * * * * * The content above * * * * * * * * * * * * -->
<!-- * * * * * * * * * * is only a placeholder * * * * * * * * * * * -->
<!-- * * * * * * * * * * and can be replaced. * * * * * * * * * * * -->
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
<!-- * * * * * * * * * * End of Placeholder * * * * * * * * * * * * -->
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
<router-outlet />
+28 -2
View File
@@ -1,3 +1,29 @@
import { Routes } from '@angular/router'; import {Routes} from '@angular/router';
export const routes: Routes = []; // TODO: Guard pour empêcher la recherche de pages
export const routes: Routes = [
{path: '', redirectTo: '/login', pathMatch: 'full'},
{path: 'login', loadComponent: () => import('./pages/login/login.component').then(m => m.LoginComponent)},
{
path: '',
loadComponent: () => import('./components/navbar/navbar.component').then(m => m.NavbarComponent),
children: [
{
path: 'home',
loadComponent: () => import('./pages/home/home.component').then(m => m.HomeComponent)
},
{
path: 'groups',
loadComponent: () => import('./pages/publication/publication.component').then(m => m.PublicationComponent)
},
{
path: 'ranking',
loadComponent: () => import('./pages/ranking/ranking.component').then(m => m.RankingComponent)
},
{
path: 'social',
loadComponent: () => import('./pages/social/social.component').then(m => m.SocialComponent)
},
]
}
];
+6 -7
View File
@@ -1,12 +1,11 @@
import { Component, signal } from '@angular/core'; import {Component} from '@angular/core';
import { RouterOutlet } from '@angular/router'; import {IonicModule} from "@ionic/angular";
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
imports: [RouterOutlet], imports: [IonicModule],
templateUrl: './app.html', templateUrl: './app.html',
styleUrl: './app.css' styleUrl: './app.css'
}) })
export class App { export class App {
protected readonly title = signal('BeReadyFrontend');
} }
@@ -0,0 +1,62 @@
<div class="bg-white m-4 rounded-2xl border border-amber-200 shadow-sm p-4 space-y-3">
<div class="grid grid-cols-5 items-center">
<h3 class="col-span-4 text-base font-semibold text-gray-900 m-0">
{{ challenge().label }}
</h3>
</div>
<p class="text-[11px] text-gray-500 leading-relaxed">{{ challenge().libelle }}</p>
<div class="grid grid-cols-5 items-center">
<span class="col-span-4 text-[11px] text-gray-500">
Aucun malus pour ce défi
</span>
<div class="col-span-1 flex justify-end">
<ion-button
fill="clear"
class="m-0 p-0 min-h-0 text-[11px] font-black bg-black text-white rounded-3xl"
style="--padding-top: 4px; --padding-bottom: 4px;"
(click)="setOpen(true)">
Relever
</ion-button>
</div>
</div>
</div>
<ion-modal [isOpen]="isModalOpen">
<ng-template>
<ion-header>
<ion-toolbar>
<ion-title>Défi Quotidien</ion-title>
<ion-buttons slot="start" style="--ion-color-primary: #0054E9;">
<ion-back-button default-href="" (click)="setOpen(false)"></ion-back-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding m-1" style="--background: #f7f6f2;">
<div class="mt-4 bg-white rounded-2xl shadow-sm p-4 space-y-3">
<div class="grid grid-cols-5 items-center">
<h3 class="col-span-4 text-base font-semibold text-gray-900 m-0">
{{ challenge().label }}
</h3>
</div>
<p class="text-[11px] text-gray-500 leading-relaxed">{{ challenge().libelle }}</p>
</div>
<app-tooltip title="Aucun malus si non réalisé"
content="entièrement facultatif et solo. Points réduits comparé à un défi classique."></app-tooltip>
<div class="mb-6">
<app-proof-form #proofForm></app-proof-form>
</div>
<ion-button expand="block" (click)="sendProof(challenge().id)">Soumettre ma preuve</ion-button>
</ion-content>
</ng-template>
</ion-modal>
@@ -0,0 +1,78 @@
import {Component, inject, input, viewChild} from '@angular/core';
import {IonicModule, LoadingController, ToastController} from "@ionic/angular";
import {GetRandomChallengeDto, RandomchallengesService} from "../../services/api";
import {firstValueFrom} from "rxjs";
import {TooltipComponent} from "../tooltip/tooltip.component";
import {ProofFormComponent} from "../proof-form/proof-form.component";
import {addIcons} from "ionicons";
import {checkmarkCircleOutline} from "ionicons/icons";
addIcons({
"checkmark-circle": checkmarkCircleOutline,
})
@Component({
selector: 'app-challenge-card',
templateUrl: './challenge-card.component.html',
styleUrls: ['./challenge-card.component.scss'],
imports: [
IonicModule,
TooltipComponent,
ProofFormComponent
]
})
export class ChallengeCardComponent {
private randomChallengesService = inject(RandomchallengesService);
private toastCtrl = inject(ToastController);
private loadCtrl = inject(LoadingController);
challenge = input.required<GetRandomChallengeDto>();
proof = viewChild<ProofFormComponent>('proofForm');
isModalOpen = false;
async setOpen(isOpen: boolean) {
this.isModalOpen = isOpen;
}
async sendProof(randomChallengeId: number) {
const file = this.proof().proofForm.value.proof;
if (!file) {
const toast = await this.toastCtrl.create({
message: 'Aucune preuve n\'a été déposée',
duration: 2000,
color: 'danger'
});
await toast.present();
return;
}
const loading = await this.loadCtrl.create({
message: 'Chargement...',
spinner: 'lines-sharp-small'
});
await loading.present();
try {
await firstValueFrom(this.randomChallengesService.patchProofEndpoint(randomChallengeId, file));
this.isModalOpen = false;
const toast = await this.toastCtrl.create({
message: 'La preuve de ta réussite a bien été déposée',
duration: 2000,
color: 'success'
});
await toast.present();
} catch (e) {
this.isModalOpen = false;
const toast = await this.toastCtrl.create({
message: e.error ?? "Impossible de déposer une preuve",
duration: 2000,
color: 'danger'
});
await toast.present();
}
await loading.dismiss();
}
}
@@ -0,0 +1,29 @@
<div class="rounded-lg m-3 bg-white border border-gray-200 items-center h-full max-h-48 overflow-scroll">
@if (userChallenges().length > 0) {
@for (challenge of userChallenges(); track challenge.challengeTitle) {
<ion-item lines="none" class="border border-gray-300 rounded-xl m-2">
<div class="grid grid-cols-[1fr_auto] gap-2 items-start w-full px-3 py-2">
<div>
<p class="m-0 text-sm text-gray-500">{{ challenge.challengeTitle }}</p>
<p class="m-0 mt-1 text-xs text-gray-500">{{ challenge.challengeDescription }}</p>
</div>
<div class="text-right shrink-0">
<p class="m-0 mt-1 text-xs text-gray-400">{{ challenge.challengeStartDate }}</p>
</div>
</div>
</ion-item>
}
} @else {
<ion-item lines="none" class="border border-stone-200 rounded-xl m-3" style="--background: #fafaf8;">
<div class="flex flex-col items-center w-full px-5 py-8 gap-3">
<div class="w-10 h-10 rounded-full bg-stone-100 border border-stone-200 flex items-center justify-center">
<ion-icon name="trophy" style="color:#a8a090; font-size:20px;"></ion-icon>
</div>
<div class="text-center">
<p class="m-0 text-sm font-medium text-stone-400">Aucun défi réalisé</p>
<p class="m-0 mt-1 text-xs text-stone-300 leading-relaxed">Vos premiers défis apparaîtront ici</p>
</div>
</div>
</ion-item>
}
</div>
@@ -0,0 +1,15 @@
import {Component, input} from '@angular/core';
import {IonicModule} from "@ionic/angular";
import {GetUserChallengeDto} from "../../services/api";
@Component({
selector: 'app-challenges-accomplished',
templateUrl: './challenges-accomplished.component.html',
styleUrls: ['./challenges-accomplished.component.scss'],
imports: [
IonicModule
]
})
export class ChallengesAccomplishedComponent {
userChallenges = input.required<GetUserChallengeDto[]>();
}
@@ -0,0 +1,25 @@
<div class="bg-white rounded-xl p-5 shadow-sm border border-gray-200">
<form [formGroup]="designationForm" class="space-y-5">
<div class="space-y-3 max-h-[70vh] overflow-y-auto">
<ion-label class="text-sm text-gray-500 block">
Choisis ton titre de préstige
</ion-label>
<ion-radio-group formControlName="designationId">
@for (designation of designations(); track designation.id) {
<ion-item lines="none" class="rounded-lg border border-gray-100">
<ion-radio [value]="designation.id" class="text-sm text-gray-700">{{ designation.label }}
</ion-radio>
</ion-item>
}
</ion-radio-group>
</div>
<ion-button expand="block"
class="mt-6 rounded-xl font-medium"
style="--background: #1f2937;"
(click)="updateDesignation()">
Modifier
</ion-button>
</form>
</div>
@@ -0,0 +1,101 @@
import {Component, inject, input, OnInit, output, signal} from '@angular/core';
import {FormControl, FormGroup, ReactiveFormsModule} from "@angular/forms";
import {IonicModule, LoadingController, ToastController} from "@ionic/angular";
import {DesignationsService, GetDesignationDto, GetUserDetailsDto, UsersService} from "../../services/api";
import {firstValueFrom} from "rxjs";
@Component({
selector: 'app-designation-form',
templateUrl: './designation-form.component.html',
styleUrls: ['./designation-form.component.scss'],
imports: [
IonicModule,
ReactiveFormsModule
]
})
export class DesignationFormComponent implements OnInit {
private designationsService = inject(DesignationsService);
private usersService = inject(UsersService);
private loadCtrl = inject(LoadingController);
private toastCtrl = inject(ToastController);
designationForm: FormGroup = new FormGroup({
designationId: new FormControl<string>(null)
});
designations = signal<GetDesignationDto[]>([]);
user = input.required<GetUserDetailsDto>();
userDesignation = output<GetUserDetailsDto>();
async ngOnInit() {
const loading = await this.loadCtrl.create({
message: 'Chargement...',
spinner: 'lines-sharp-small'
});
await loading.present();
this.designationForm.patchValue({
designationId: this.user().designationId,
});
try {
const designation = await firstValueFrom(this.designationsService.getAllDesignationsEndpoint());
this.designations.set(designation);
await loading.dismiss();
} catch {
const toast = await this.toastCtrl.create({
message: 'Impossible de récupérer les titres',
duration: 2000,
color: 'danger'
});
await toast.present();
await loading.dismiss();
}
}
async updateDesignation() {
const loading = await this.loadCtrl.create({
message: 'Modification...',
spinner: 'lines-sharp-small'
});
await loading.present();
try {
const form = this.designationForm.getRawValue();
const label = this.designations().find(x => x.id == form.designationId).label;
await firstValueFrom(this.usersService.patchUserDesignationEndpoint(form));
const userEdited = {
id: this.user().id,
firstName: this.user().firstName,
name: this.user().name,
username: this.user().username,
email: this.user().email,
designationId: form.designationId,
designationName: label,
creationDate: this.user().creationDate,
getUserStatsDto: this.user().getUserStatsDto
}
this.userDesignation.emit(userEdited);
const toast = await this.toastCtrl.create({
message: 'Modification réussie',
duration: 2000,
color: 'success'
});
await loading.dismiss();
await toast.present();
} catch {
const toast = await this.toastCtrl.create({
message: 'Modification impossible',
duration: 2000,
color: 'danger'
});
await loading.dismiss();
await toast.present();
}
}
}
@@ -0,0 +1,51 @@
@if (friendsRequest().length) {
<div class="rounded-xl px-5 m-3 bg-white overflow-auto font-mono border border-gray-300">
<ion-list>
@for (request of friendsRequest(); track request.userId; let i = $index) {
@if (i == friendsRequest().length - 1) {
<ion-item lines="none">
<ion-avatar slot="start" class="w-5 h-5">
<img alt="Silhouette of a person's head"
src="https://ionicframework.com/docs/img/demos/avatar.svg"/>
</ion-avatar>
<ion-label class="text-xs font-mono">{{ request.username }}</ion-label>
<ion-button fill="clear" (click)="acceptRequest(request)">
<ion-icon slot="icon-only" name="check" class="text-green-600 m-0"></ion-icon>
</ion-button>
<app-pipe></app-pipe>
<ion-button fill="clear" (click)="rejectRequest(request.userId)">
<ion-icon slot="icon-only" name="close" class="text-red-600 m-0"></ion-icon>
</ion-button>
</ion-item>
} @else {
<ion-item lines="full">
<ion-avatar slot="start" class="w-5 h-5">
<img alt="Silhouette of a person's head"
src="https://ionicframework.com/docs/img/demos/avatar.svg"/>
</ion-avatar>
<ion-label class="text-xs font-mono">{{ request.username }}</ion-label>
<ion-button fill="clear" (click)="acceptRequest(request)">
<ion-icon slot="icon-only" name="check" class="text-green-600 m-0"></ion-icon>
</ion-button>
<app-pipe></app-pipe>
<ion-button fill="clear" (click)="rejectRequest(request.userId)">
<ion-icon slot="icon-only" name="close" class="text-red-600 m-0"></ion-icon>
</ion-button>
</ion-item>
}
}
</ion-list>
</div>
} @else {
<ion-item lines="none" class="border border-stone-200 rounded-xl m-3" style="--background: #fafaf8;">
<div class="flex flex-col items-center w-full px-10 py-20 gap-3">
<div class="w-10 h-10 rounded-full bg-stone-100 border border-stone-200 flex items-center justify-center">
<ion-icon name="people-outline" style="color:#a8a090; font-size:20px;"></ion-icon>
</div>
<div class="text-center">
<p class="m-0 text-sm font-medium text-stone-400">Ajoutez vos amis</p>
<p class="m-0 mt-1 text-xs text-stone-300 leading-relaxed">Vos demandes d'amis apparaîtront ici</p>
</div>
</div>
</ion-item>
}
@@ -0,0 +1,86 @@
import {Component, inject} from '@angular/core';
import {IonicModule, LoadingController, ToastController} from "@ionic/angular";
import {checkmarkCircleOutline, closeCircleOutline, peopleOutline} from 'ionicons/icons';
import {addIcons} from "ionicons";
import {PipeComponent} from "../pipe/pipe.component";
import {FriendsService, GetFriendRequestDto} from "../../services/api";
import {firstValueFrom} from "rxjs";
import {FriendsStateService} from "../../services/friends-state";
addIcons({
'check': checkmarkCircleOutline,
'close': closeCircleOutline,
'people-outline': peopleOutline
});
@Component({
selector: 'app-friend-request',
templateUrl: './friend-request.component.html',
styleUrls: ['./friend-request.component.scss'],
imports: [
IonicModule,
PipeComponent
]
})
export class FriendRequestComponent {
private friendsService = inject(FriendsService);
private toastCtrl = inject(ToastController);
private friendsState = inject(FriendsStateService);
private loadCtrl = inject(LoadingController)
friendsRequest = this.friendsState.requests;
async acceptRequest(request: GetFriendRequestDto) {
const loading = await this.loadCtrl.create({
message: 'Chargement...',
spinner: 'lines-sharp-small'
});
await loading.present();
try {
await firstValueFrom(this.friendsService.acceptFriendRequestEndpoint(request.userId));
this.friendsState.acceptRequest(request);
const toast = await this.toastCtrl.create({
message: 'Demande d\'ami acceptée',
duration: 1000,
color: 'success'
});
await toast.present();
} catch (e) {
const toast = await this.toastCtrl.create({
message: 'Echec de l\'acceptation',
duration: 1000,
color: 'danger'
});
await toast.present();
}
await loading.dismiss();
}
async rejectRequest(id: number) {
const loading = await this.loadCtrl.create({
message: 'Chargement...',
spinner: 'lines-sharp-small'
});
await loading.present();
try {
await firstValueFrom(this.friendsService.rejectFriendRequestEndpoint(id));
this.friendsState.removeRequest(id);
const toast = await this.toastCtrl.create({
message: 'Demande d\'ami refusée',
duration: 1000,
color: 'success'
});
await toast.present();
} catch (e) {
const toast = await this.toastCtrl.create({
message: 'Echec du refus',
duration: 1000,
color: 'danger'
});
await toast.present();
}
await loading.dismiss();
}
}
@@ -0,0 +1,65 @@
@if (friends().length) {
<div class="rounded-xl px-5 m-3 bg-white font-mono border border-gray-300">
<ion-list>
@for (friend of friends(); track friend.friendId; let i = $index) {
@if (i == friends().length - 1) {
<ion-item lines="none">
<ion-avatar slot="start" class="w-5 h-5">
<img alt="Silhouette of a person's head"
src="https://ionicframework.com/docs/img/demos/avatar.svg"/>
</ion-avatar>
<ion-label class="text-xs font-mono font-bold"
(click)="setOpen(true, friend.friendId)">{{ friend.username }}
</ion-label>
<ion-label class="text-xs font-mono text-gray-400">{{ friend.score }} <em>pts</em></ion-label>
<ion-button fill="clear" (click)="deleteFriend(friend.friendId)">
<ion-icon slot="icon-only" name="close" class="text-red-600"></ion-icon>
</ion-button>
</ion-item>
} @else {
<ion-item lines="full">
<ion-avatar slot="start" class="w-5 h-5">
<img alt="Silhouette of a person's head"
src="https://ionicframework.com/docs/img/demos/avatar.svg"/>
</ion-avatar>
<ion-label class="text-xs font-mono font-bold"
(click)="setOpen(true, friend.friendId)">{{ friend.username }}
</ion-label>
<ion-label class="text-xs font-mono text-gray-400">{{ friend.score }} <em>pts</em></ion-label>
<ion-button fill="clear" (click)="deleteFriend(friend.friendId)">
<ion-icon slot="icon-only" name="close" class="text-red-600"></ion-icon>
</ion-button>
</ion-item>
}
}
</ion-list>
</div>
} @else {
<ion-item lines="none" class="border border-stone-200 rounded-xl m-3" style="--background: #fafaf8;">
<div class="flex flex-col items-center w-full px-10 py-20 gap-3">
<div class="w-10 h-10 rounded-full bg-stone-100 border border-stone-200 flex items-center justify-center">
<ion-icon name="people" style="color:#a8a090; font-size:20px;"></ion-icon>
</div>
<div class="text-center">
<p class="m-0 text-sm font-medium text-stone-400">Ajoutez vos amis</p>
<p class="m-0 mt-1 text-xs text-stone-300 leading-relaxed">Vos amis apparaîtront ici</p>
</div>
</div>
</ion-item>
}
<ion-modal [isOpen]="isModalOpen">
<ng-template>
<ion-header>
<ion-toolbar>
<ion-title>Profil</ion-title>
<ion-buttons slot="start" style="--ion-color-primary: #0054E9;">
<ion-back-button default-href="" (click)="setOpen(false, null)"></ion-back-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding" style="--background: #f7f6f2;">
<app-generic-user-info [userInfo]="selectedFriend()"></app-generic-user-info>
</ion-content>
</ng-template>
</ion-modal>
@@ -0,0 +1,80 @@
import {Component, inject, signal} from '@angular/core';
import {IonicModule, LoadingController, ToastController} from "@ionic/angular";
import {closeCircleOutline, peopleOutline} from 'ionicons/icons';
import {addIcons} from "ionicons";
import {FriendsService, GetUserDto, UsersService} from "../../services/api";
import {firstValueFrom} from "rxjs";
import {FriendsStateService} from "../../services/friends-state";
import {GenericUserInfoComponent} from "../generic-user-info/generic-user-info.component";
addIcons({
'close': closeCircleOutline,
'people': peopleOutline
});
@Component({
selector: 'app-friends-list',
templateUrl: './friends-list.component.html',
styleUrls: ['./friends-list.component.scss'],
imports: [
IonicModule,
GenericUserInfoComponent
]
})
export class FriendsListComponent {
private friendsService = inject(FriendsService);
private usersService = inject(UsersService);
private toastCtrl = inject(ToastController);
private friendsState = inject(FriendsStateService);
private loadCtrl = inject(LoadingController)
selectedFriend = signal<GetUserDto>(null);
isModalOpen = false;
friends = this.friendsState.friends;
async deleteFriend(friendId: number) {
const loading = await this.loadCtrl.create({
message: 'Chargement...',
spinner: 'lines-sharp-small'
});
await loading.present();
try {
await firstValueFrom(this.friendsService.deleteFriendEndpoint(friendId));
const toast = await this.toastCtrl.create({
message: 'Vous avez supprimé cet ami',
duration: 2000,
color: 'success'
});
await toast.present();
} catch (e) {
const toast = await this.toastCtrl.create({
message: 'Vous ne pouvez pas supprimer cet ami',
duration: 2000,
color: 'danger'
});
await toast.present();
}
this.friendsState.removeFriend(friendId);
await loading.dismiss();
}
async setOpen(isOpen: boolean, userId: number) {
if (isOpen) {
try {
const userInfo = await firstValueFrom(this.usersService.getUserEndpoint(userId));
this.selectedFriend.set(userInfo);
} catch (e) {
const toast = await this.toastCtrl.create({
message: 'Impossible de charger les données du joueur',
duration: 2000,
color: 'primary'
});
await toast.present();
}
}
this.isModalOpen = isOpen;
}
}
@@ -0,0 +1,41 @@
@if (proofs().length > 0) {
@if (!modal) {
<div class="bg-white rounded-lg p-2 shadow-sm border border-gray-200 overflow-scroll max-h-full">
<div class="grid grid-cols-4 gap-3">
@for (p of proofs(); track p) {
<img [src]="'data:image/jpeg;base64,' + p.proof"
class="w-20 h-20 object-cover"
alt=""
(click)="openProof(p.proof)"
/>
}
</div>
</div>
} @else {
<div class="fixed inset-0 flex items-center justify-center z-50" (click)="closeProof()">
<button class="absolute top-15 right-1 text-gray-300 text-4xl font-bold p-5 bg-transparent"
(click)="closeProof()">
<ion-icon name="close-circle-outline"></ion-icon>
</button>
<img [src]="'data:image/jpeg;base64,' + selectedProof()"
class="w-[90%] h-[90%] object-cover rounded-md mt-10"
alt=""
/>
</div>
}
} @else {
<ion-item lines="none" class="border border-stone-200 rounded-xl m-3" style="--background: #fafaf8;">
<div class="flex flex-col items-center w-full px-5 py-8 gap-3">
<div class="w-10 h-10 rounded-full bg-stone-100 border border-stone-200 flex items-center justify-center">
<ion-icon name="image" style="color:#a8a090; font-size:20px;"></ion-icon>
</div>
<div class="text-center">
<p class="m-0 text-sm font-medium text-stone-400">C'est bien vide par ici...</p>
<p class="m-0 mt-1 text-xs text-stone-300 leading-relaxed">Participe à des défis pour remplir ta galerie
!</p>
</div>
</div>
</ion-item>
}
@@ -0,0 +1,70 @@
import {Component, inject, OnInit, signal} from '@angular/core';
import {IonicModule, LoadingController, ToastController} from "@ionic/angular";
import {GetUserProofDto, UsersService} from "../../services/api";
import {firstValueFrom} from "rxjs";
import {addIcons} from "ionicons";
import {closeCircleOutline, imageOutline} from "ionicons/icons";
addIcons({
'close-circle-outline': closeCircleOutline,
'image': imageOutline,
})
@Component({
selector: 'app-gallery',
templateUrl: './gallery.component.html',
styleUrls: ['./gallery.component.scss'],
imports: [
IonicModule
]
})
export class GalleryComponent implements OnInit {
private loadCtrl = inject(LoadingController);
private usersService = inject(UsersService);
private toastCtrl = inject(ToastController);
proofs = signal<GetUserProofDto[]>([]);
selectedProof = signal<string>("");
modal = false;
async ngOnInit() {
const loading = await this.loadCtrl.create({
message: 'Chargement...',
spinner: 'lines-sharp-small'
});
await loading.present();
try {
const proofs = await firstValueFrom(this.usersService.getAllUserProofsEndpoint());
this.proofs.set(proofs);
await loading.dismiss();
} catch {
const toast = await this.toastCtrl.create({
message: 'Impossible de charger la galerie photos',
duration: 2000,
color: 'danger'
});
await loading.dismiss();
await toast.present();
}
}
async openProof(proof: string) {
const loading = await this.loadCtrl.create({
message: 'Chargement...',
spinner: 'lines-sharp-small'
});
await loading.present();
this.selectedProof.set(proof);
this.modal = true;
await loading.dismiss();
}
closeProof() {
this.modal = false;
}
}
@@ -0,0 +1,42 @@
<div class="rounded-lg px-5 m-3 bg-white border border-gray-200 grid grid-cols-3 items-center">
<div>
<ion-avatar slot="start" class="w-20 h-20 p-2">
<img alt=""
src="https://ionicframework.com/docs/img/demos/avatar.svg"/>
</ion-avatar>
</div>
<div class="col-span-2 py-3">
<ion-label class="text-[15px] font-sans font-bold block">
{{ userInfo()?.firstName }} {{ userInfo()?.name.charAt(0) }}.
</ion-label>
<ion-label class="text-xs font-mono block text-gray-400 mt-0.5">
{{ userInfo()?.username }}
</ion-label>
<ion-label
class="text-[10px] font-sans font-semibold block mt-2 bg-gray-100 text-gray-500 rounded-full px-2 py-0.5 w-fit">
@if (userInfo().designationName) {
⚡ {{ userInfo().designationName }}
} @else {
⚡ En sommeil
}
</ion-label>
</div>
</div>
<div class="grid grid-cols-2 px-1">
<div class="rounded-lg px-4 py-0.5 m-2 bg-white border border-gray-200">
<p class="text-2xl font-black font-mono mb-0.5">{{ userInfo().getUserStatsDto.totalChallenge }}</p>
<p class="text-[11px] font-semibold font-mono text-gray-400 mt-0">Défis Réalisés</p>
</div>
<div class="rounded-lg px-4 py-0.5 m-2 bg-white border border-gray-200">
<p class="text-2xl font-black font-mono mb-0.5">{{ userInfo().getUserStatsDto.series }}×</p>
<p class="text-[11px] font-semibold font-mono text-gray-400 mt-0">Séries max</p>
</div>
</div>
<div class="px-1">
<div class="rounded-lg px-4 py-0.5 m-2 bg-white border border-gray-200">
<p class="text-2xl font-black font-mono mb-0.5">{{ userInfo().getUserStatsDto.totalLikes }}</p>
<p class="text-[11px] font-semibold font-mono text-gray-400 mt-0">Total de likes</p>
</div>
</div>
@@ -0,0 +1,15 @@
import {Component, input} from '@angular/core';
import {IonicModule} from "@ionic/angular";
import {GetUserDetailsDto, GetUserDto} from "../../services/api";
@Component({
selector: 'app-generic-user-info',
templateUrl: './generic-user-info.component.html',
styleUrls: ['./generic-user-info.component.scss'],
imports: [
IonicModule
]
})
export class GenericUserInfoComponent {
userInfo = input.required<GetUserDetailsDto | GetUserDto>()
}
@@ -0,0 +1,38 @@
<div class="bg-white rounded-xl p-5 shadow-sm border border-gray-200">
<form [formGroup]="groupForm">
<ion-label class="text-xs text-gray-500 mb-1 block">
Nom du groupe
<ion-text color="danger">*</ion-text>
</ion-label>
<ion-item lines="none" class="rounded-xl border border-gray-200">
<ion-input placeholder="Nom de ton groupe"
[clearInput]="true"
formControlName="label"
class="text-gray-800">
</ion-input>
</ion-item>
<div class="space-y-3 mt-4 max-h-[50vh] overflow-y-auto">
<ion-label class="text-sm text-gray-500 block">
Ajouter des amis dans le groupe
</ion-label>
@for (friend of friends(); track friend.friendId) {
<ion-item lines="none" class="rounded-lg border border-gray-100">
<ion-checkbox class="text-sm text-gray-700"
[checked]="groupForm.value.userGroups?.includes(friend.friendId)"
(ionChange)="selectFriends($event, friend.friendId)">
{{ friend.username }}
</ion-checkbox>
</ion-item>
}
</div>
<ion-button expand="block"
class="mt-6 rounded-xl font-medium"
style="--background: #1f2937;"
(click)="createGroup()">
Créer un groupe
</ion-button>
</form>
</div>
@@ -0,0 +1,112 @@
import {Component, inject, input, OnInit, output, signal} from '@angular/core';
import {FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators} from "@angular/forms";
import {IonicModule, LoadingController, ToastController} from "@ionic/angular";
import {
CreateGroupDto,
CreateUserGroupDto,
FriendsService,
GetFriendDto,
GetGroupDto,
GroupsService
} from "../../services/api";
import {firstValueFrom} from "rxjs";
@Component({
selector: 'app-group-form',
templateUrl: './group-form.component.html',
styleUrls: ['./group-form.component.scss'],
imports: [
FormsModule,
ReactiveFormsModule,
IonicModule
]
})
export class GroupFormComponent implements OnInit {
private friendsServices = inject(FriendsService);
private groupsServices = inject(GroupsService);
private loadCtrl = inject(LoadingController);
private toastCtrl = inject(ToastController);
friends = signal<GetFriendDto[]>([]);
newGroup = output<GetGroupDto>();
groupForm: FormGroup = new FormGroup({
label: new FormControl<string>(null, [Validators.required]),
userGroups: new FormControl<CreateUserGroupDto[]>(null, [Validators.required]),
});
async ngOnInit() {
const loading = await this.loadCtrl.create({
message: 'Chargement...',
spinner: 'lines-sharp-small'
});
await loading.present();
try {
const friends = await firstValueFrom(this.friendsServices.getAllFriendsEndpoint());
this.friends.set(friends);
} catch {
const toast = await this.toastCtrl.create({
message: 'Impossible d\'afficher les amis du joueur',
duration: 2000,
color: 'danger'
});
await toast.present();
}
await loading.dismiss();
}
selectFriends(event: any, friendId: number) {
const current = this.groupForm.value.userGroups || [];
if (event.detail.checked) {
this.groupForm.patchValue({
userGroups: [...current, friendId]
});
} else {
this.groupForm.patchValue({
userGroups: current.filter((x: number) => x != friendId)
});
}
}
async createGroup() {
if (this.groupForm.invalid) {
this.groupForm.reset();
const toast = await this.toastCtrl.create({
message: 'Tous les champs doivent être saisis',
duration: 2000,
color: 'danger'
});
await toast.present();
return;
}
const createGroupDto: CreateGroupDto = {
label: this.groupForm.value.label,
userGroups: this.groupForm.value.userGroups.map((x: number) => ({
userId: x
}))
};
try {
const group = await firstValueFrom(this.groupsServices.createGroupEndpoint(createGroupDto));
this.newGroup.emit(group);
const toast = await this.toastCtrl.create({
message: 'Le groupe a été crée',
duration: 2000,
color: 'success'
});
await toast.present();
} catch (e) {
const toast = await this.toastCtrl.create({
message: 'Impossible de créer le groupe',
duration: 2000,
color: 'danger'
});
await toast.present();
}
}
}
@@ -0,0 +1,68 @@
<div class="rounded-lg m-3 bg-white border border-gray-200 p-4 flex flex-col items-center text-center gap-2">
<div class="w-16 h-16 rounded-full bg-gray-100 flex items-center justify-center">
<ion-icon name="people-outline" class="text-3xl text-gray-600"></ion-icon>
</div>
<ion-label class="text-lg font-mono">{{ group().label }}</ion-label>
<ion-label class="text-[13px] font-mono text-gray-400">{{ date() }}</ion-label>
</div>
<app-title-part textInfo="Participants"></app-title-part>
<div class="rounded-lg px-5 m-3 bg-white border border-gray-200 items-center">
@for (user of group().users; track user.id; let i = $index) {
@if (group().users.length - 1 == i) {
<ion-item lines="none">
<p class="text-sm text-gray-600 mr-2">{{ user.username }}</p>
@if (user.grade === 'Admin') {
<i class="fa-solid fa-crown text-xs text-yellow-600"></i>
}
<div class="flex items-center justify-end gap-1 w-full">
@if (user.grade == 'Member') {
<ion-icon class="text-2xl text-green-800 cursor-pointer" name="arrow-up-circle-outline"
(click)="promoteUser(user.id)"></ion-icon>
<app-pipe></app-pipe>
}
<ion-icon class="text-xl text-red-800 cursor-pointer" name="ban"
(click)="deleteUser(user.id)"></ion-icon>
</div>
</ion-item>
} @else {
<ion-item lines="full">
<p class="text-sm text-gray-600 mr-2">{{ user.username }}</p>
@if (user.grade === 'Admin') {
<i class="fa-solid fa-crown text-xs text-yellow-600"></i>
}
<div class="flex items-center justify-end gap-1 w-full">
@if (user.grade == 'Member') {
<ion-icon class="text-2xl text-green-800 cursor-pointer" name="arrow-up-circle-outline"
(click)="promoteUser(user.id)"></ion-icon>
<app-pipe></app-pipe>
}
<ion-icon class="text-xl text-red-800 cursor-pointer" name="ban"
(click)="deleteUser(user.id)"></ion-icon>
</div>
</ion-item>
}
}
</div>
<app-title-part textInfo="Paramètres"></app-title-part>
<div class="rounded-lg px-5 m-3 bg-white border border-gray-200 items-center">
<ion-list>
@for (n of options; track n) {
@if (n == options.length) {
<ion-item lines="none" class="transition-all duration-200 active:[--background:#DBD8D7]"
(click)="leaveGroup()">
<p class="text-sm text-red-800">Quitter le groupe</p>
<ion-icon slot="end" class="text-xl text-red-800" name="logout"></ion-icon>
</ion-item>
} @else {
<ion-item lines="full" class="transition-all duration-200 active:[--background:#DBD8D7]"
(click)="deleteGroup()">
<p class="text-sm text-red-800">Supprimer le groupe</p>
<ion-icon slot="end" class="text-xl text-red-800" name="chevron"></ion-icon>
</ion-item>
}
}
</ion-list>
</div>
@@ -0,0 +1,161 @@
import {Component, computed, inject, input, OnInit, output, signal} from '@angular/core';
import {GetGroupDetailsDto, GroupsService} from "../../services/api";
import {addIcons} from "ionicons";
import {peopleOutline, banOutline, arrowUpCircleOutline} from "ionicons/icons";
import {IonicModule, LoadingController, ToastController} from "@ionic/angular";
import moment from 'moment';
import {TitlePartComponent} from "../title-part/title-part.component";
import {firstValueFrom} from "rxjs";
import {PipeComponent} from "../pipe/pipe.component";
addIcons({
'avatars': peopleOutline,
'ban': banOutline,
'arrow-up-circle-outline': arrowUpCircleOutline,
})
@Component({
selector: 'app-group-info',
templateUrl: './group-info.component.html',
styleUrls: ['./group-info.component.scss'],
imports: [
IonicModule,
TitlePartComponent,
PipeComponent
]
})
export class GroupInfoComponent implements OnInit {
private groupsService = inject(GroupsService);
private toastCtrl = inject(ToastController);
private loadCtrl = inject(LoadingController);
groupSelected = input.required<GetGroupDetailsDto>();
group = signal<GetGroupDetailsDto>({});
date = computed<string>(() => moment(this.group().creationDate).format('DD/MM/YYYY'));
idGroup = output<number>();
options = [1, 2];
ngOnInit() {
this.group.set(this.groupSelected());
}
async deleteUser(id: number) {
const loading = await this.loadCtrl.create({
message: 'Suppression...',
spinner: 'lines-sharp-small'
});
await loading.present();
try {
await firstValueFrom(this.groupsService.deleteUserFromGroupEndpoint(this.group().id, id));
this.group.update(x => ({
...x,
users: x.users.filter(u => u.id != id)
}));
const toast = await this.toastCtrl.create({
message: 'Utilisateur retiré du groupe',
duration: 2000,
color: 'success'
});
await toast.present();
} catch (e) {
const toast = await this.toastCtrl.create({
message: 'Vous devez être administrateur pour faire cela',
duration: 2000,
color: 'danger'
});
await toast.present();
}
await loading.dismiss();
}
async leaveGroup() {
const loading = await this.loadCtrl.create({
message: 'Chargement...',
spinner: 'lines-sharp-small'
});
await loading.present();
const oldGroup = this.group().id;
try {
await firstValueFrom(this.groupsService.leaveGroupEndpoint(oldGroup));
this.idGroup.emit(oldGroup);
const toast = await this.toastCtrl.create({
message: 'Vous avez quitter ce groupe',
duration: 2000,
color: 'success'
});
await toast.present();
} catch {
const toast = await this.toastCtrl.create({
message: 'Impossible de quitter le groupe',
duration: 2000,
color: 'danger'
});
await toast.present();
}
await loading.dismiss();
}
addToGroup() {
}
async deleteGroup() {
const loading = await this.loadCtrl.create({
message: 'Suppression...',
spinner: 'lines-sharp-small'
});
await loading.present();
const oldGroup = this.group().id;
try {
await firstValueFrom(this.groupsService.deleteGroupEndpoint(oldGroup));
this.idGroup.emit(oldGroup);
const toast = await this.toastCtrl.create({
message: 'Groupe supprimé',
duration: 2000,
color: 'success'
});
await toast.present();
} catch (e) {
const toast = await this.toastCtrl.create({
message: 'Vous devez être administrateur pour faire cela',
duration: 2000,
color: 'danger'
});
await toast.present();
}
await loading.dismiss();
}
async promoteUser(id: number) {
const loading = await this.loadCtrl.create({
message: 'Promotion...',
spinner: 'lines-sharp-small'
});
await loading.present();
try {
const group = await firstValueFrom(this.groupsService.patchGroupUserRoleEndpoint(this.group().id, id));
this.group.set(group);
const toast = await this.toastCtrl.create({
message: 'Utilisateur promu avec succès',
duration: 2000,
color: 'success'
});
await toast.present();
} catch (e) {
const toast = await this.toastCtrl.create({
message: 'Vous devez être administrateur pour faire cela',
duration: 2000,
color: 'danger'
});
await toast.present();
}
await loading.dismiss();
}
}
@@ -0,0 +1,35 @@
<div class="rounded-lg m-3 bg-white border border-gray-200 items-center max-h-80 overflow-scroll">
<ion-list>
@if (groups().length) {
@for (group of groups(); track group.id; let i = $index) {
@if (i == groups().length - 1) {
<ion-item lines="none"
class="transition-all duration-200 active:[--background:#DBD8D7]"
(click)="selectedGroup(group.id)">
<p class="text-sm text-gray-600">{{ group.label }}</p>
<ion-icon slot="end" class="text-xl text-gray-400" name="chevron"></ion-icon>
</ion-item>
} @else {
<ion-item lines="full"
class="transition-all duration-200 active:[--background:#DBD8D7]"
(click)="selectedGroup(group.id)">
<p class="text-sm text-gray-600">{{ group.label }}</p>
<ion-icon slot="end" class="text-xl text-gray-400" name="chevron"></ion-icon>
</ion-item>
}
}
} @else {
<ion-item lines="none" class="border border-stone-200 rounded-xl m-3" style="--background: #fafaf8;">
<div class="flex flex-col items-center w-full px-10 py-20 gap-3">
<div class="w-10 h-10 rounded-full bg-stone-100 border border-stone-200 flex items-center justify-center">
<ion-icon name="group" style="color:#a8a090; font-size:20px;"></ion-icon>
</div>
<div class="text-center">
<p class="m-0 text-sm font-medium text-stone-400">Pas encore de groupes</p>
<p class="m-0 mt-1 text-xs text-stone-300 leading-relaxed">Vos groupes apparaîtront ici</p>
</div>
</div>
</ion-item>
}
</ion-list>
</div>
@@ -0,0 +1,50 @@
import {Component, inject, input, output} from '@angular/core';
import {IonicModule, LoadingController, ToastController} from "@ionic/angular";
import {GetGroupDetailsDto, GetGroupDto, GroupsService} from "../../services/api";
import {addIcons} from "ionicons";
import {chatbubblesOutline, chevronForwardOutline} from "ionicons/icons";
import {firstValueFrom} from "rxjs";
addIcons({
'group': chatbubblesOutline,
'chevron': chevronForwardOutline,
});
@Component({
selector: 'app-groups',
templateUrl: './groups.component.html',
styleUrls: ['./groups.component.scss'],
imports: [
IonicModule
]
})
export class GroupsComponent {
private groupsService = inject(GroupsService);
private toastCtrl = inject(ToastController);
private loadCtrl = inject(LoadingController);
groups = input.required<GetGroupDto[]>();
group = output<GetGroupDetailsDto>();
async selectedGroup(id: number) {
const loading = await this.loadCtrl.create({
message: 'Chargement...',
spinner: 'lines-sharp-small'
});
await loading.present();
try {
const group = await firstValueFrom(this.groupsService.getGroupDetailsEndpoint(id));
this.group.emit(group);
} catch {
const toast = await this.toastCtrl.create({
message: 'Impossible de charger le groupe',
duration: 2000,
color: 'danger'
});
await toast.present();
}
await loading.dismiss();
}
}
@@ -0,0 +1,8 @@
<form [formGroup]="messageForm">
<ion-item lines="none" class="rounded-2xl border border-stone-200 mt-2">
<ion-input placeholder="Envoyer un message..." formControlName="libelle"></ion-input>
<ion-button style="--background: white" (click)="sendMessageOnGroup()">
<ion-icon name="send-outline" class="text-xl text-stone-400"></ion-icon>
</ion-button>
</ion-item>
</form>

Some files were not shown because too many files have changed in this diff Show More