diff --git a/OnTime_Driver_live/app/build.gradle b/OnTime_Driver_live/app/build.gradle index 6aefb73..6e1af0d 100755 --- a/OnTime_Driver_live/app/build.gradle +++ b/OnTime_Driver_live/app/build.gradle @@ -1,30 +1,4 @@ -apply plugin: 'com.android.application' -apply plugin: 'realm-android' - -android { - namespace "id.ontime.driver" - signingConfigs { - debug { - // Use default debug keystore on this machine - } - } - compileSdk 34 - - defaultConfig { - applicationId "id.ontime.driver" - minSdkVersion 23 - targetSdkVersion 34 - versionCode 10 - versionName '1.1.0' - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - multiDexEnabled true - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_11 - targetCompatibility JavaVersion.VERSION_11 - } - productFlavors { - } +: buildTypes { release { signingConfig signingConfigs.debug diff --git a/OnTime_Driver_live/app/src/main/java/id/ontime/driver/activity/LoginActivity.java b/OnTime_Driver_live/app/src/main/java/id/ontime/driver/activity/LoginActivity.java index 0ca9419..60cdcf1 100755 --- a/OnTime_Driver_live/app/src/main/java/id/ontime/driver/activity/LoginActivity.java +++ b/OnTime_Driver_live/app/src/main/java/id/ontime/driver/activity/LoginActivity.java @@ -491,8 +491,7 @@ public class LoginActivity extends AppCompatActivity { } request.setPassword(password.getText().toString()); - // Enforce FCM token: login ONLY proceeds when we have a non-empty Firebase token. - // 1) Try to use token that was already stored in Realm (BaseApp / MessagingService.onNewToken). + // Best effort FCM token: use cached token first, then try fresh token. io.realm.Realm realm = BaseApp.getInstance(this).getRealmInstance(); FirebaseToken storedToken = realm.where(FirebaseToken.class).findFirst(); if (storedToken != null && storedToken.getTokenId() != null && !storedToken.getTokenId().isEmpty()) { @@ -501,7 +500,7 @@ public class LoginActivity extends AppCompatActivity { return; } - // 2) If not yet stored, request a fresh token from FirebaseMessaging. + // If token is still warming up, continue login without blocking user. try { FirebaseMessaging.getInstance().getToken() .addOnCompleteListener(task -> { @@ -518,15 +517,11 @@ public class LoginActivity extends AppCompatActivity { doLoginRequest(request, emailText); } else { - // Token is required: stop login and show message. - progresshide(); - notif("Firebase token not ready, please check your network and try again."); + doLoginRequest(request, emailText); } }); } catch (IllegalStateException e) { - // Firebase not initialized: block login instead of proceeding without token. - progresshide(); - notif("Firebase is not initialized, cannot login. Please restart the app."); + doLoginRequest(request, emailText); } } @@ -539,7 +534,8 @@ public class LoginActivity extends AppCompatActivity { progresshide(); if (response.isSuccessful()) { LoginResponseJson body = response.body(); - if (body != null && body.getMessage().equalsIgnoreCase("found")) { + if (body != null && body.getMessage().equalsIgnoreCase("found") + && body.getData() != null && !body.getData().isEmpty()) { User user = body.getData().get(0); saveUser(user); Intent intent = new Intent(LoginActivity.this, MainActivity.class); @@ -549,6 +545,8 @@ public class LoginActivity extends AppCompatActivity { } else { notif(getString(R.string.phoneemailwrong)); } + } else { + notif(getString(R.string.phoneemailwrong)); } } diff --git a/OnTime_Driver_live/app/src/main/java/id/ontime/driver/constants/BaseApp.java b/OnTime_Driver_live/app/src/main/java/id/ontime/driver/constants/BaseApp.java index 22a71d4..3efa7b3 100755 --- a/OnTime_Driver_live/app/src/main/java/id/ontime/driver/constants/BaseApp.java +++ b/OnTime_Driver_live/app/src/main/java/id/ontime/driver/constants/BaseApp.java @@ -73,7 +73,9 @@ public class BaseApp extends Application { // FCM v1: async token fetch and topic subscribe (reference: test app). try { - FirebaseApp app = FirebaseApp.initializeApp(this); + FirebaseApp app = FirebaseApp.getApps(this).isEmpty() + ? FirebaseApp.initializeApp(this) + : FirebaseApp.getInstance(); if (app != null) { FirebaseMessaging.getInstance().getToken() .addOnCompleteListener(task -> { diff --git a/OnTime_Driver_live/build.gradle b/OnTime_Driver_live/build.gradle index 512079c..be7f13b 100755 --- a/OnTime_Driver_live/build.gradle +++ b/OnTime_Driver_live/build.gradle @@ -8,7 +8,7 @@ buildscript { dependencies { // Android Gradle Plugin compatible with Gradle 8.x and JDK 21 classpath 'com.android.tools.build:gradle:8.5.0' - classpath 'com.google.gms:google-services:4.3.10' + classpath 'com.google.gms:google-services:4.4.2' // Realm plugin variant that uses the new AGP transformer API classpath 'io.realm:realm-gradle-plugin:10.14.0-transformer-api' diff --git a/OnTime_Merchant_live/app/build.gradle b/OnTime_Merchant_live/app/build.gradle index efd6917..b1c2b0c 100755 --- a/OnTime_Merchant_live/app/build.gradle +++ b/OnTime_Merchant_live/app/build.gradle @@ -1,20 +1,30 @@ apply plugin: 'com.android.application' apply plugin: 'realm-android' +// Native apps expect `ontimekeystore.jks`, but it's not committed in this repo. +// If it's missing, fall back to the standard Android debug keystore. +def ontimeKeystoreFile = file('ontimekeystore.jks') +def defaultDebugKeystoreFile = file("${System.getProperty('user.home')}/.android/debug.keystore") +def hasOntimeKeystore = ontimeKeystoreFile.exists() +def signingStoreFile = hasOntimeKeystore ? ontimeKeystoreFile : defaultDebugKeystoreFile +def signingStorePassword = hasOntimeKeystore ? '123456@ontime' : 'android' +def signingKeyAlias = hasOntimeKeystore ? 'ontimekeystore' : 'androiddebugkey' +def signingKeyPassword = hasOntimeKeystore ? '123456@ontime' : 'android' + android { namespace "id.ontime.merchant" signingConfigs { config { - storeFile file('ontimekeystore.jks') - storePassword '123456@ontime' - keyAlias 'ontimekeystore' - keyPassword '123456@ontime' + storeFile signingStoreFile + storePassword signingStorePassword + keyAlias signingKeyAlias + keyPassword signingKeyPassword } debug { - storeFile file('ontimekeystore.jks') - storePassword '123456@ontime' - keyAlias 'ontimekeystore' - keyPassword '123456@ontime' + storeFile signingStoreFile + storePassword signingStorePassword + keyAlias signingKeyAlias + keyPassword signingKeyPassword } } compileSdk 34 diff --git a/OnTime_Merchant_live/app/src/main/java/id/ontime/merchant/activity/LoginActivity.java b/OnTime_Merchant_live/app/src/main/java/id/ontime/merchant/activity/LoginActivity.java index f0fa112..5ff8085 100755 --- a/OnTime_Merchant_live/app/src/main/java/id/ontime/merchant/activity/LoginActivity.java +++ b/OnTime_Merchant_live/app/src/main/java/id/ontime/merchant/activity/LoginActivity.java @@ -491,7 +491,7 @@ public class LoginActivity extends AppCompatActivity { } request.setPassword(password.getText().toString()); - // FCM token is mandatory for login: use cached token if available, otherwise fetch a fresh one. + // Best effort FCM token: use cached token if available, otherwise fetch fresh token. io.realm.Realm realm = BaseApp.getInstance(this).getRealmInstance(); FirebaseToken storedToken = realm.where(FirebaseToken.class).findFirst(); if (storedToken != null && storedToken.getTokenId() != null && !storedToken.getTokenId().isEmpty()) { @@ -513,8 +513,7 @@ public class LoginActivity extends AppCompatActivity { }); performLoginRequest(request, emailText); } else { - progresshide(); - notif("Firebase token not ready, please check your network and try again."); + performLoginRequest(request, emailText); } }); } @@ -528,7 +527,8 @@ public class LoginActivity extends AppCompatActivity { progresshide(); if (response.isSuccessful()) { LoginResponseJson body = response.body(); - if (body != null && body.getMessage().equalsIgnoreCase("found")) { + if (body != null && body.getMessage().equalsIgnoreCase("found") + && body.getData() != null && !body.getData().isEmpty()) { User user = body.getData().get(0); saveUser(user); Intent intent = new Intent(LoginActivity.this, MainActivity.class); @@ -538,6 +538,8 @@ public class LoginActivity extends AppCompatActivity { } else { notif(getString(R.string.phoneemailwrong)); } + } else { + notif(getString(R.string.phoneemailwrong)); } } diff --git a/OnTime_Merchant_live/app/src/main/java/id/ontime/merchant/constants/BaseApp.java b/OnTime_Merchant_live/app/src/main/java/id/ontime/merchant/constants/BaseApp.java index 202747b..fa77d05 100755 --- a/OnTime_Merchant_live/app/src/main/java/id/ontime/merchant/constants/BaseApp.java +++ b/OnTime_Merchant_live/app/src/main/java/id/ontime/merchant/constants/BaseApp.java @@ -42,7 +42,9 @@ public class BaseApp extends Application { realmInstance = Realm.getDefaultInstance(); try { - FirebaseApp app = FirebaseApp.initializeApp(this); + FirebaseApp app = FirebaseApp.getApps(this).isEmpty() + ? FirebaseApp.initializeApp(this) + : FirebaseApp.getInstance(); if (app != null) { FirebaseMessaging.getInstance().getToken() .addOnCompleteListener(task -> { diff --git a/OnTime_Merchant_live/build.gradle b/OnTime_Merchant_live/build.gradle index d48bf78..8c89740 100755 --- a/OnTime_Merchant_live/build.gradle +++ b/OnTime_Merchant_live/build.gradle @@ -9,7 +9,7 @@ buildscript { // Android Gradle Plugin compatible with Gradle 8.x and JDK 21 classpath 'com.android.tools.build:gradle:8.5.0' classpath 'com.jakewharton:butterknife-gradle-plugin:9.0.0-rc2' - classpath 'com.google.gms:google-services:4.3.10' + classpath 'com.google.gms:google-services:4.4.2' // Realm plugin variant that uses the new AGP transformer API classpath 'io.realm:realm-gradle-plugin:10.14.0-transformer-api' diff --git a/OnTime_User_live/app/build.gradle b/OnTime_User_live/app/build.gradle index 64e47c3..041b051 100755 --- a/OnTime_User_live/app/build.gradle +++ b/OnTime_User_live/app/build.gradle @@ -3,15 +3,25 @@ apply plugin: 'realm-android' apply plugin: 'kotlin-android' //apply plugin: 'kotlin-android-extensions' +// Native apps previously hard-required `ontimekeystore.jks` (not committed in this repo). +// Fallback to the standard Android debug keystore so builds can still produce APKs locally. +def ontimeKeystoreFile = file('ontimekeystore.jks') +def defaultDebugKeystoreFile = file("${System.getProperty('user.home')}/.android/debug.keystore") +def hasOntimeKeystore = ontimeKeystoreFile.exists() +def signingStoreFile = hasOntimeKeystore ? ontimeKeystoreFile : defaultDebugKeystoreFile +def signingStorePassword = hasOntimeKeystore ? '123456@ontime' : 'android' +def signingKeyAlias = hasOntimeKeystore ? 'ontimekeystore' : 'androiddebugkey' +def signingKeyPassword = hasOntimeKeystore ? '123456@ontime' : 'android' + android { namespace "id.ontime.customer" signingConfigs { debug { // Use local ontime keystore (make sure this file exists in the app module) - storeFile file('ontimekeystore.jks') - storePassword '123456@ontime' - keyAlias 'ontimekeystore' - keyPassword '123456@ontime' + storeFile signingStoreFile + storePassword signingStorePassword + keyAlias signingKeyAlias + keyPassword signingKeyPassword } } compileSdk 34 diff --git a/OnTime_User_live/app/src/main/java/id/ontime/customer/activity/LoginActivity.java b/OnTime_User_live/app/src/main/java/id/ontime/customer/activity/LoginActivity.java index 42976df..a0f1a99 100755 --- a/OnTime_User_live/app/src/main/java/id/ontime/customer/activity/LoginActivity.java +++ b/OnTime_User_live/app/src/main/java/id/ontime/customer/activity/LoginActivity.java @@ -546,7 +546,7 @@ public class LoginActivity extends AppCompatActivity { } request.setPassword(password.getText().toString()); - // FCM token is required: prefer cached token from Realm, otherwise fetch a fresh one. + // Best effort FCM token: prefer cached token, otherwise fetch a fresh one. Realm realm = BaseApp.getInstance(this).getRealmInstance(); FirebaseToken storedToken = realm.where(FirebaseToken.class).findFirst(); if (storedToken != null && storedToken.getTokenId() != null && !storedToken.getTokenId().isEmpty()) { @@ -569,15 +569,13 @@ public class LoginActivity extends AppCompatActivity { }); doLoginRequest(request, emailText); } else { - progresshide(); - Log.e(TAG, "FCM getToken failed: " + (task.getException() != null ? task.getException().getMessage() : "empty token")); - notif(getString(R.string.fcm_token_required)); + Log.w(TAG, "FCM getToken not ready, continue login without token"); + doLoginRequest(request, emailText); } }); } catch (IllegalStateException e) { - progresshide(); - Log.e(TAG, "FirebaseMessaging not available; FCM token is required", e); - notif(getString(R.string.fcm_token_required)); + Log.w(TAG, "FirebaseMessaging unavailable during login, continue without token", e); + doLoginRequest(request, emailText); } } @@ -620,19 +618,24 @@ public class LoginActivity extends AppCompatActivity { notif(showMsg); } } else { - String errMsg = "Login failed"; + String errMsg = getString(R.string.phoneemailwrong); try { if (response.errorBody() != null) { String errBody = response.errorBody().string(); Log.e(TAG, "Login error body: code=" + code + " body=" + (errBody != null ? errBody : "null")); - if (errBody != null && !errBody.isEmpty()) errMsg = errBody; + if (errBody != null && !errBody.isEmpty()) { + String low = errBody.toLowerCase(); + if (!(low.contains("wrong") || low.contains("salah") || low.contains("not found") || low.contains("unauthorized"))) { + errMsg = errBody; + } + } } else { Log.e(TAG, "Login error: code=" + code + " no error body"); } } catch (Exception e) { Log.e(TAG, "Login error reading body", e); } - if (code == 401) errMsg = getString(R.string.phoneemailwrong); + if (code == 400 || code == 401 || code == 403) errMsg = getString(R.string.phoneemailwrong); notif(errMsg); } } diff --git a/OnTime_User_live/app/src/main/java/id/ontime/customer/activity/RegisterActivity.java b/OnTime_User_live/app/src/main/java/id/ontime/customer/activity/RegisterActivity.java index 3ba76c5..5f984a2 100755 --- a/OnTime_User_live/app/src/main/java/id/ontime/customer/activity/RegisterActivity.java +++ b/OnTime_User_live/app/src/main/java/id/ontime/customer/activity/RegisterActivity.java @@ -837,16 +837,14 @@ public class RegisterActivity extends AppCompatActivity { request.setCountrycode(countryCode.getText().toString()); request.setChecked(check); - // FCM v1: get token async then register (token is mandatory) + // FCM v1: best effort token fetch before register. try { FirebaseMessaging.getInstance().getToken().addOnCompleteListener(task -> { if (task.isSuccessful() && task.getResult() != null && !task.getResult().isEmpty()) { request.setToken(task.getResult()); } else { - progresshide(); - Log.e("RegisterActivity", "FCM getToken failed for register: " + (task.getException() != null ? task.getException().getMessage() : "empty token")); - notif(getString(R.string.fcm_token_required)); - return; + Log.w("RegisterActivity", "FCM getToken not ready, continue register without token"); + request.setToken(""); } UserService service = ServiceGenerator.createService(UserService.class, request.getEmail(), request.getPassword()); service.register(request).enqueue(new Callback() { @@ -884,9 +882,42 @@ public class RegisterActivity extends AppCompatActivity { }); }); } catch (IllegalStateException e) { - progresshide(); - Log.e("RegisterActivity", "FirebaseMessaging not available; FCM token is required for register", e); - notif(getString(R.string.fcm_token_required)); + Log.w("RegisterActivity", "FirebaseMessaging unavailable during register, continue without token", e); + request.setToken(""); + UserService service = ServiceGenerator.createService(UserService.class, request.getEmail(), request.getPassword()); + service.register(request).enqueue(new Callback() { + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + progresshide(); + if (response.isSuccessful()) { + if (Objects.requireNonNull(response.body()).getMessage().equalsIgnoreCase("next")) { + Nextbtn(viewFlipper); + + } else if (response.body().getMessage().equalsIgnoreCase("success")) { + + User user = response.body().getData().get(0); + saveUser(user); + registerPanic(); + Intent intent = new Intent(RegisterActivity.this, MainActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); + finish(); + + } else { + notif(response.body().getMessage()); + } + } else { + notif("error"); + } + } + + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { + progresshide(); + t.printStackTrace(); + notif("error!"); + } + }); } } diff --git a/OnTime_User_live/app/src/main/java/id/ontime/customer/constants/BaseApp.java b/OnTime_User_live/app/src/main/java/id/ontime/customer/constants/BaseApp.java index ad3d1ad..848b6bb 100755 --- a/OnTime_User_live/app/src/main/java/id/ontime/customer/constants/BaseApp.java +++ b/OnTime_User_live/app/src/main/java/id/ontime/customer/constants/BaseApp.java @@ -43,7 +43,9 @@ public class BaseApp extends Application { // FCM v1: async token fetch and topic subscribe (reference: test app LoginActivity). try { - FirebaseApp app = FirebaseApp.initializeApp(this); + FirebaseApp app = FirebaseApp.getApps(this).isEmpty() + ? FirebaseApp.initializeApp(this) + : FirebaseApp.getInstance(); if (app != null) { FirebaseMessaging.getInstance().getToken() .addOnCompleteListener(task -> { diff --git a/OnTime_User_live/build.gradle b/OnTime_User_live/build.gradle index cb62aa4..0cc1c5e 100755 --- a/OnTime_User_live/build.gradle +++ b/OnTime_User_live/build.gradle @@ -10,7 +10,7 @@ buildscript { classpath 'com.android.tools.build:gradle:8.5.0' classpath 'com.jakewharton:butterknife-gradle-plugin:9.0.0-rc2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10" - classpath 'com.google.gms:google-services:4.3.10' + classpath 'com.google.gms:google-services:4.4.2' // Realm plugin variant that uses the new AGP transformer API classpath 'io.realm:realm-gradle-plugin:10.14.0-transformer-api' // NOTE: Do not place your application dependencies here; they belong diff --git a/backendpanel/application/config/config.php b/backendpanel/application/config/config.php index e690c17..61e9cc2 100755 --- a/backendpanel/application/config/config.php +++ b/backendpanel/application/config/config.php @@ -44,6 +44,8 @@ $config['fcm_credentials_json'] = ''; $config['fcm_credentials_path'] = FCPATH . 'ngojol-trial-firebase-adminsdk-lc81n-00c9e935db.json'; $config['fcm_limit_per_hour'] = 100; $config['fcm_limit_per_day'] = 500; +// When true, Pelanggan/Merchant/Driver login does not require an FCM device token (e.g. API tests). +$config['fcm_login_allow_no_device_token'] = false; $config['maps_limit_per_hour'] = 1000; $config['maps_limit_per_day'] = 5000; @@ -59,6 +61,10 @@ define('FCM_CREDENTIALS_JSON', $config['fcm_credentials_json']); define('FCM_CREDENTIALS_PATH', $config['fcm_credentials_path']); define('FCM_LIMIT_PER_HOUR', $config['fcm_limit_per_hour']); define('FCM_LIMIT_PER_DAY', $config['fcm_limit_per_day']); +define( + 'FCM_LOGIN_ALLOW_NO_DEVICE_TOKEN', + filter_var($config['fcm_login_allow_no_device_token'], FILTER_VALIDATE_BOOLEAN) +); define('MAPS_LIMIT_PER_HOUR', $config['maps_limit_per_hour']); define('MAPS_LIMIT_PER_DAY', $config['maps_limit_per_day']); diff --git a/backendpanel/application/config/fcm.php b/backendpanel/application/config/fcm.php index c880650..ad549cf 100755 --- a/backendpanel/application/config/fcm.php +++ b/backendpanel/application/config/fcm.php @@ -6,9 +6,14 @@ defined('BASEPATH') or exit('No direct script access allowed'); | Legacy FCM config – DO NOT USE |-------------------------------------------------------------------------- | Push notifications use FCM HTTP v1 only (see application/helpers/fcm_v1_helper.php). -| Set FCM_CREDENTIALS_PATH or FCM_CREDENTIALS_JSON and FCM_PROJECT_ID in the environment. -| This file exists so the deprecated application/libraries/Fcm.php does not -| load a missing config; the legacy library must not be used (no legacy API key). +| Service account path/project id live in application/config/config.php (constants +| FCM_CREDENTIALS_PATH, FCM_CREDENTIALS_JSON, FCM_PROJECT_ID). This file exists so the +| deprecated application/libraries/Fcm.php does not load a missing config; do not +| use the legacy library (no legacy API key). +| +| Mobile login (Pelanggan/login, Merchant/login, Driver/login) expects a device token +| from the APK (Firebase getToken). To allow clients without a token (e.g. API tests), +| set $config['fcm_login_allow_no_device_token'] = true in config.php. */ $config['fcm_api_key'] = ''; $config['fcm_api_send_address'] = 'https://fcm.googleapis.com/fcm/send'; diff --git a/backendpanel/application/controllers/Appnotification.php b/backendpanel/application/controllers/Appnotification.php index 0684765..1c5d773 100755 --- a/backendpanel/application/controllers/Appnotification.php +++ b/backendpanel/application/controllers/Appnotification.php @@ -11,14 +11,15 @@ class appnotification extends CI_Controller is_logged_in(); $this->load->model('notification_model', 'notif'); + $this->load->model('Pelanggan_model', 'pelanggan_model'); $this->load->library('form_validation'); } public function index() { - + $data['pelanggan_list'] = $this->pelanggan_model->get_all_for_notification_picker(); $this->load->view('includes/header'); - $this->load->view('appnotification/index'); + $this->load->view('appnotification/index', $data); $this->load->view('includes/footer'); } @@ -33,10 +34,57 @@ class appnotification extends CI_Controller return; } - $topic = $this->input->post('topic'); $title = $this->input->post('title'); $message = $this->input->post('message'); + $send_target = $this->input->post('send_target'); + if ($send_target === 'users') { + $user_ids = $this->input->post('user_ids'); + if (!is_array($user_ids)) { + $user_ids = array(); + } + $user_ids = array_unique(array_filter(array_map('intval', $user_ids))); + if (empty($user_ids)) { + $this->session->set_flashdata('error', 'Pilih minimal satu pengguna (user).'); + redirect('appnotification/index'); + return; + } + $sent = 0; + $skipped = 0; + foreach ($user_ids as $uid) { + if ($uid <= 0) { + continue; + } + $row = $this->pelanggan_model->get_notification_row_by_id($uid); + if (!$row) { + $skipped++; + continue; + } + $tok = isset($row->token) ? trim((string) $row->token) : ''; + if ($tok === '' || !fcm_v1_is_valid_device_token($tok)) { + $skipped++; + continue; + } + if ($this->notif->send_notif($title, $message, $tok)) { + $sent++; + } else { + $skipped++; + } + } + if ($sent > 0) { + $msg = 'Notifikasi terkirim ke ' . $sent . ' pengguna.'; + if ($skipped > 0) { + $msg .= ' ' . $skipped . ' dilewati (tanpa token FCM valid atau gagal kirim).'; + } + $this->session->set_flashdata('send', $msg); + } else { + $this->session->set_flashdata('error', 'Tidak ada notifikasi terkirim. Pastikan pengguna memiliki token FCM valid (login dari aplikasi).'); + } + redirect('appnotification/index'); + return; + } + + $topic = $this->input->post('topic'); $ok = $this->notif->send_notif($title, $message, $topic); if ($ok) { $this->session->set_flashdata('send', 'Notifikasi berhasil dikirim'); diff --git a/backendpanel/application/controllers/api/Driver.php b/backendpanel/application/controllers/api/Driver.php index df2824b..535e6ea 100755 --- a/backendpanel/application/controllers/api/Driver.php +++ b/backendpanel/application/controllers/api/Driver.php @@ -76,10 +76,24 @@ class Driver extends REST_Controller $data = file_get_contents("php://input"); $decoded_data = json_decode($data); + if (!$decoded_data || !isset($decoded_data->password)) { + $this->response(array('code' => '400', 'message' => 'Invalid request', 'data' => []), 200); + return; + } + if (function_exists('fcm_v1_validate_login_device_token_from_app')) { + $fcm_err = fcm_v1_validate_login_device_token_from_app($decoded_data); + if (is_array($fcm_err)) { + $this->response( + array('code' => $fcm_err['code'], 'message' => $fcm_err['message'], 'data' => []), + 200 + ); + return; + } + } // Only save reg_id (FCM token) when valid. Invalid/placeholder tokens are updated by relogin. - $token_from_regid = isset($decoded_data->reg_id) ? trim((string) $decoded_data->reg_id) : ''; - $token_from_token = isset($decoded_data->token) ? trim((string) $decoded_data->token) : ''; - $token = $token_from_regid !== '' ? $token_from_regid : $token_from_token; + $token = function_exists('fcm_v1_device_token_from_request') + ? fcm_v1_device_token_from_request($decoded_data) + : ''; $reg_id = array(); if ($token !== '' && function_exists('fcm_v1_is_valid_device_token') && fcm_v1_is_valid_device_token($token)) { $reg_id['reg_id'] = $token; @@ -149,8 +163,10 @@ class Driver extends REST_Controller ); $ins = $this->Driver_model->my_location($data); - // When driver sends valid FCM token (reg_id) with location, update so they receive order requests. Invalid/placeholder tokens are updated by relogin. - $reg_id = isset($decoded_data->reg_id) ? trim((string) $decoded_data->reg_id) : ''; + // When driver sends valid FCM token with location, update so they receive order requests. + $reg_id = function_exists('fcm_v1_device_token_from_request') + ? fcm_v1_device_token_from_request($decoded_data) + : (isset($decoded_data->reg_id) ? trim((string) $decoded_data->reg_id) : ''); if ($reg_id !== '' && isset($decoded_data->id_driver) && function_exists('fcm_v1_is_valid_device_token') && fcm_v1_is_valid_device_token($reg_id)) { $this->Driver_model->update_driver_reg_id($decoded_data->id_driver, $reg_id); } @@ -372,6 +388,22 @@ class Driver extends REST_Controller } } + /** + * Persist driver accept exchange on transaksi row (admin order detail API log). + */ + private function log_driver_accept_api($id_transaksi, $raw_body, $response_message) + { + $id_transaksi = (int) $id_transaksi; + if ($id_transaksi <= 0) { + return; + } + $line = json_encode(array( + 'driver_request_raw' => $raw_body, + 'backend_response' => $response_message, + ), JSON_UNESCAPED_UNICODE); + $this->Pelanggan_model->append_transaksi_driver_request_log($id_transaksi, $line); + } + function accept_post() { if (!isset($_SERVER['PHP_AUTH_USER'])) { @@ -383,6 +415,7 @@ class Driver extends REST_Controller $data = file_get_contents("php://input"); $dec_data = json_decode($data); log_message('debug', 'accept_post: payload=' . $data); + $tid_for_log = (is_object($dec_data) && isset($dec_data->id_transaksi)) ? (int) $dec_data->id_transaksi : 0; $data_req = array( 'id_driver' => $dec_data->id, @@ -390,13 +423,13 @@ class Driver extends REST_Controller ); $condition = array( - 'id_driver' => $dec_data->id, - 'status' => '1' + 'id_driver' => $dec_data->id ); $cek_login = $this->Driver_model->get_status_driver($condition); - log_message('debug', 'accept_post: get_status_driver rows=' . $cek_login->num_rows()); - if ($cek_login->num_rows() > 0) { + $driver_status = $cek_login->num_rows() > 0 ? (string) $cek_login->row('status') : ''; + log_message('debug', 'accept_post: get_status_driver rows=' . $cek_login->num_rows() . ' status=' . $driver_status); + if ($cek_login->num_rows() > 0 && ($driver_status === '1' || $driver_status === '4')) { $acc_req = $this->Driver_model->accept_request($data_req); log_message('debug', 'accept_post: accept_request result=' . json_encode($acc_req)); @@ -405,6 +438,7 @@ class Driver extends REST_Controller 'message' => 'berhasil', 'data' => 'berhasil' ); + $this->log_driver_accept_api($tid_for_log, $data, $message); $this->response($message, 200); } else { if ($acc_req['data'] == 'canceled') { @@ -412,12 +446,14 @@ class Driver extends REST_Controller 'message' => 'canceled', 'data' => 'canceled' ); + $this->log_driver_accept_api($tid_for_log, $data, $message); $this->response($message, 200); } else { $message = array( 'message' => 'unknown fail', 'data' => 'canceled' ); + $this->log_driver_accept_api($tid_for_log, $data, $message); $this->response($message, 200); } } @@ -426,6 +462,7 @@ class Driver extends REST_Controller 'message' => 'unknown fail', 'data' => 'canceled' ); + $this->log_driver_accept_api($tid_for_log, $data, $message); $this->response($message, 200); } } diff --git a/backendpanel/application/controllers/api/Merchant.php b/backendpanel/application/controllers/api/Merchant.php index b3e6a00..57c3c87 100755 --- a/backendpanel/application/controllers/api/Merchant.php +++ b/backendpanel/application/controllers/api/Merchant.php @@ -65,9 +65,31 @@ class Merchant extends REST_Controller $data = file_get_contents("php://input"); $decoded_data = json_decode($data); + $no_telepon_val = + isset($decoded_data->no_telepon) ? trim((string)$decoded_data->no_telepon) : ''; + $email_val = isset($decoded_data->email) + ? trim((string)$decoded_data->email) + : ''; + + if (!$decoded_data || !isset($decoded_data->password) || ($no_telepon_val === '' && $email_val === '')) { + $this->response(array('code' => '400', 'message' => 'Invalid request', 'data' => []), 200); + return; + } + $login_by_phone = $no_telepon_val !== ''; + if (function_exists('fcm_v1_validate_login_device_token_from_app')) { + $fcm_err = fcm_v1_validate_login_device_token_from_app($decoded_data); + if (is_array($fcm_err)) { + $this->response( + array('code' => $fcm_err['code'], 'message' => $fcm_err['message'], 'data' => []), + 200 + ); + return; + } + } // Only save FCM token when valid (relogin overwrites invalid/placeholder tokens). - $token = isset($decoded_data->token) ? trim((string) $decoded_data->token) : ''; - $token = (isset($decoded_data->reg_id) && trim((string) $decoded_data->reg_id) !== '') ? trim((string) $decoded_data->reg_id) : $token; + $token = function_exists('fcm_v1_device_token_from_request') + ? fcm_v1_device_token_from_request($decoded_data) + : ''; $reg_id = array(); if ($token !== '' && function_exists('fcm_v1_is_valid_device_token') && fcm_v1_is_valid_device_token($token)) { $reg_id['token_merchant'] = $token; @@ -75,10 +97,18 @@ class Merchant extends REST_Controller $condition = array( 'password' => sha1($decoded_data->password), - 'telepon_mitra' => $decoded_data->no_telepon, //'token' => $decoded_data->token ); - $check_banned = $this->Merchantapi_model->check_banned($decoded_data->no_telepon); + + if ($login_by_phone) { + $condition['telepon_mitra'] = $no_telepon_val; + } else { + $condition['email_mitra'] = $email_val; + } + + $check_banned = $login_by_phone + ? $this->Merchantapi_model->check_banned($no_telepon_val) + : $this->Merchantapi_model->check_banned_by_email($email_val); if ($check_banned) { $message = array( 'message' => 'banned', @@ -90,7 +120,19 @@ class Merchant extends REST_Controller $message = array(); if ($cek_login->num_rows() > 0) { if (!empty($reg_id)) { - $this->Merchantapi_model->edit_profile_token($reg_id, $decoded_data->no_telepon); + $loginRow = $cek_login->row(); + $phone_for_token = ''; + if ($login_by_phone) { + $phone_for_token = $no_telepon_val; + } else if (!empty($loginRow) && isset($loginRow->telepon_merchant) && $loginRow->telepon_merchant !== '') { + $phone_for_token = $loginRow->telepon_merchant; + } else if (!empty($loginRow) && isset($loginRow->telepon_mitra) && $loginRow->telepon_mitra !== '') { + $phone_for_token = $loginRow->telepon_mitra; + } + + if ($phone_for_token !== '') { + $this->Merchantapi_model->edit_profile_token($reg_id, $phone_for_token); + } } $get_pelanggan = $this->Merchantapi_model->get_data_merchant($condition); $message = array( @@ -102,7 +144,7 @@ class Merchant extends REST_Controller } else { $message = array( 'code' => '404', - 'message' => 'nomor hp atau password salah!', + 'message' => 'nomor hp/email atau password salah!', 'data' => [] ); $this->response($message, 200); @@ -450,24 +492,24 @@ class Merchant extends REST_Controller $token = $this->wallet->gettoken($iduser); $regid = $this->wallet->getregid($iduser); $tokenmerchant = $this->wallet->gettokenmerchant($iduser); - - if ($token == NULL and $tokenmerchant == NULL and $regid != NULL) { + + $topic = null; + if ($token == NULL and $tokenmerchant == NULL and $regid != NULL && !empty(trim((string) $regid['reg_id']))) { $topic = $regid['reg_id']; - } else if ($regid == NULL and $tokenmerchant == NULL and $token != NULL) { + } else if ($regid == NULL and $tokenmerchant == NULL and $token != NULL && !empty(trim((string) $token['token']))) { $topic = $token['token']; - } else if ($regid == NULL and $token == NULL and $tokenmerchant != NULL) { + } else if ($regid == NULL and $token == NULL and $tokenmerchant != NULL && !empty(trim((string) $tokenmerchant['token_merchant']))) { $topic = $tokenmerchant['token_merchant']; } - + $title = 'Sukses'; $message = 'Permintaan berhasil dikirim'; $saldo = $this->wallet->getsaldo($iduser); - - - + $this->wallet->ubahsaldo($iduser, $amount, $saldo); - //$this->wallet->ubahstatuswithdrawbyid($id); - $this->wallet->send_notif($title, $message, $topic); + if ($topic !== null) { + $this->wallet->send_notif($title, $message, $topic); + } /* END EDIT */ $message = array( diff --git a/backendpanel/application/controllers/api/Pelanggan.php b/backendpanel/application/controllers/api/Pelanggan.php index 3dc8ea9..3cd6f7c 100755 --- a/backendpanel/application/controllers/api/Pelanggan.php +++ b/backendpanel/application/controllers/api/Pelanggan.php @@ -18,6 +18,71 @@ class Pelanggan extends REST_Controller date_default_timezone_set('Asia/Jakarta'); } + /** + * Structured API request log for order-related endpoints. + */ + private function log_order_api_request($endpoint, $rawBody) + { + log_message('debug', '[ORDER_API][' . $endpoint . '][REQUEST] ' . $rawBody); + } + + /** + * Structured API response log for order-related endpoints. + */ + private function log_order_api_response($endpoint, $responsePayload) + { + log_message('debug', '[ORDER_API][' . $endpoint . '][RESPONSE] ' . json_encode($responsePayload)); + } + + /** + * Log candidate drivers targeted by customer order flow. + */ + private function log_order_driver_targets($endpoint, $idTransaksi, $driverList) + { + $ids = array(); + if (is_array($driverList)) { + foreach ($driverList as $d) { + if (is_object($d) && isset($d->id)) { + $ids[] = (string) $d->id; + } else if (is_array($d) && isset($d['id'])) { + $ids[] = (string) $d['id']; + } + } + } + log_message( + 'debug', + '[ORDER_API][' . $endpoint . '][DRIVER_TARGETS] id_transaksi=' . $idTransaksi . + ' total=' . count($ids) . ' driver_ids=' . implode(',', $ids) + ); + } + + /** + * Store raw request/response on transaksi row for dashboard detail (requires DB columns). + */ + private function save_order_creation_logs($id_transaksi, $raw_request, $response_payload, $driver_targets) + { + $id_transaksi = (int) $id_transaksi; + if ($id_transaksi <= 0) { + return; + } + $rows = array(); + if (is_array($driver_targets)) { + foreach ($driver_targets as $d) { + $rows[] = json_decode(json_encode($d), true); + } + } + $backend_driver = json_encode(array( + 'note' => 'Candidate drivers from get_data_driver_histroy after order create; client apps may use Firebase/FCM for dispatch.', + 'candidate_drivers' => $rows, + ), JSON_UNESCAPED_UNICODE); + $this->Pelanggan_model->save_transaksi_api_log( + $id_transaksi, + $raw_request, + json_encode($response_payload, JSON_UNESCAPED_UNICODE), + $backend_driver + ); + } + function index_get() { $this->response("Api for Ontime!", 200); @@ -120,9 +185,20 @@ class Pelanggan extends REST_Controller $this->response(array('code' => '400', 'message' => 'Invalid request', 'data' => []), 200); return; } + if (function_exists('fcm_v1_validate_login_device_token_from_app')) { + $fcm_err = fcm_v1_validate_login_device_token_from_app($decoded_data); + if (is_array($fcm_err)) { + $this->response( + array('code' => $fcm_err['code'], 'message' => $fcm_err['message'], 'data' => []), + 200 + ); + return; + } + } // Only save FCM token when valid (relogin overwrites invalid/placeholder tokens). - $token = isset($decoded_data->token) ? trim((string) $decoded_data->token) : ''; - $token = (isset($decoded_data->reg_id) && trim((string) $decoded_data->reg_id) !== '') ? trim((string) $decoded_data->reg_id) : $token; + $token = function_exists('fcm_v1_device_token_from_request') + ? fcm_v1_device_token_from_request($decoded_data) + : ''; $reg_id = array(); if ($token !== '' && function_exists('fcm_v1_is_valid_device_token') && fcm_v1_is_valid_device_token($token)) { $reg_id['token'] = $token; @@ -236,7 +312,9 @@ class Pelanggan extends REST_Controller // Generate a deterministic placeholder based on email so the column is never empty. // This placeholder is intentionally SHORT / starting with "R" + digits so // fcm_v1_is_valid_device_token() will treat it as invalid for push. - $incomingToken = isset($dec_data->token) ? trim((string) $dec_data->token) : ''; + $incomingToken = function_exists('fcm_v1_device_token_from_request') + ? fcm_v1_device_token_from_request($dec_data) + : (isset($dec_data->token) ? trim((string) $dec_data->token) : ''); if ($incomingToken === '') { $emailForToken = isset($dec_data->email) ? strtolower(trim((string) $dec_data->email)) : ''; if ($emailForToken !== '') { @@ -1007,6 +1085,7 @@ class Pelanggan extends REST_Controller function request_transaksi_post() { + $endpoint = 'request_transaksi_post'; if (!isset($_SERVER['PHP_AUTH_USER'])) { header("WWW-Authenticate: Basic realm=\"Private Area\""); header("HTTP/1.0 401 Unauthorized"); @@ -1014,18 +1093,19 @@ class Pelanggan extends REST_Controller } else { $cek = $this->Pelanggan_model->check_banned_user($_SERVER['PHP_AUTH_USER']); if ($cek) { - log_message('debug', 'request_transaksi_post: banned user ' . $_SERVER['PHP_AUTH_USER']); $message = array( 'message' => 'fail', 'data' => 'Status User Banned' ); + log_message('debug', '[ORDER_API][' . $endpoint . '] banned user ' . $_SERVER['PHP_AUTH_USER']); + $this->log_order_api_response($endpoint, $message); $this->response($message, 200); } } $data = file_get_contents("php://input"); $dec_data = json_decode($data); - log_message('debug', 'request_transaksi_post: payload=' . $data); + $this->log_order_api_request($endpoint, $data); $data_req = array( 'id_pelanggan' => $dec_data->id_pelanggan, @@ -1046,9 +1126,23 @@ class Pelanggan extends REST_Controller ); $request = $this->Pelanggan_model->insert_transaksi($data_req); + $idTransaksiNum = 0; + if ($request['status'] && !empty($request['data'])) { + foreach ($request['data'] as $row) { + if (is_object($row) && isset($row->id)) { + $idTransaksiNum = (int) $row->id; + break; + } + } + } if ($request['status']) { - if (isset($request['data'][0]->id)) { - log_message('debug', 'request_transaksi_post: success id_transaksi=' . $request['data'][0]->id . ' id_pelanggan=' . $dec_data->id_pelanggan . ' fitur=' . $dec_data->order_fitur); + $idTransaksi = $idTransaksiNum > 0 ? $idTransaksiNum : 'unknown'; + $driverTargets = $idTransaksiNum > 0 + ? $this->Pelanggan_model->get_data_driver_histroy($idTransaksiNum)->result() + : array(); + $this->log_order_driver_targets($endpoint, $idTransaksi, $driverTargets); + if ($idTransaksiNum > 0) { + log_message('debug', 'request_transaksi_post: success id_transaksi=' . $idTransaksiNum . ' id_pelanggan=' . $dec_data->id_pelanggan . ' fitur=' . $dec_data->order_fitur); } else { log_message('debug', 'request_transaksi_post: success (no id in data) payload=' . json_encode($request['data'])); } @@ -1056,6 +1150,8 @@ class Pelanggan extends REST_Controller 'message' => 'success', 'data' => $request['data'] ); + $this->log_order_api_response($endpoint, $message); + $this->save_order_creation_logs($idTransaksiNum, $data, $message, $driverTargets); $this->response($message, 200); } else { log_message('error', 'request_transaksi_post: insert_transaksi fail data=' . json_encode($request['data'])); @@ -1063,12 +1159,14 @@ class Pelanggan extends REST_Controller 'message' => 'fail', 'data' => $request['data'] ); + $this->log_order_api_response($endpoint, $message); $this->response($message, 200); } } function check_status_transaksi_post() { + $endpoint = 'check_status_transaksi_post'; if (!isset($_SERVER['PHP_AUTH_USER'])) { header("WWW-Authenticate: Basic realm=\"Private Area\""); header("HTTP/1.0 401 Unauthorized"); @@ -1077,14 +1175,19 @@ class Pelanggan extends REST_Controller $data = file_get_contents("php://input"); $dec_data = json_decode($data); - log_message('debug', 'check_status_transaksi_post: payload=' . $data); + $this->log_order_api_request($endpoint, $data); $dataTrans = array( 'id_transaksi' => $dec_data->id_transaksi ); $getStatus = $this->Pelanggan_model->check_status($dataTrans); - log_message('debug', 'check_status_transaksi_post: result=' . json_encode($getStatus)); + $this->log_order_driver_targets( + $endpoint, + isset($dec_data->id_transaksi) ? $dec_data->id_transaksi : 'unknown', + isset($getStatus['list_driver']) ? $getStatus['list_driver'] : array() + ); + $this->log_order_api_response($endpoint, $getStatus); $this->response($getStatus, 200); } @@ -1321,6 +1424,7 @@ class Pelanggan extends REST_Controller function request_transaksi_send_post() { + $endpoint = 'request_transaksi_send_post'; if (!isset($_SERVER['PHP_AUTH_USER'])) { header("WWW-Authenticate: Basic realm=\"Private Area\""); header("HTTP/1.0 401 Unauthorized"); @@ -1332,12 +1436,15 @@ class Pelanggan extends REST_Controller 'message' => 'fail', 'data' => 'Status User Banned' ); + log_message('debug', '[ORDER_API][' . $endpoint . '] banned user ' . $_SERVER['PHP_AUTH_USER']); + $this->log_order_api_response($endpoint, $message); $this->response($message, 200); } } $data = file_get_contents("php://input"); $dec_data = json_decode($data); + $this->log_order_api_request($endpoint, $data); $data_req = array( 'id_pelanggan' => $dec_data->id_pelanggan, @@ -1368,16 +1475,29 @@ class Pelanggan extends REST_Controller $request = $this->Pelanggan_model->insert_transaksi_send($data_req, $dataDetail); if ($request['status']) { + $resultRows = $request['data']->result(); $message = array( 'message' => 'success', - 'data' => $request['data']->result() + 'data' => $resultRows ); + $idTransaksiNum = 0; + if (!empty($resultRows[0]) && is_object($resultRows[0]) && isset($resultRows[0]->id)) { + $idTransaksiNum = (int) $resultRows[0]->id; + } + $idTransaksi = $idTransaksiNum > 0 ? $idTransaksiNum : 'unknown'; + $driverTargets = $idTransaksiNum > 0 + ? $this->Pelanggan_model->get_data_driver_histroy($idTransaksiNum)->result() + : array(); + $this->log_order_driver_targets($endpoint, $idTransaksi, $driverTargets); + $this->log_order_api_response($endpoint, $message); + $this->save_order_creation_logs($idTransaksiNum, $data, $message, $driverTargets); $this->response($message, 200); } else { $message = array( 'message' => 'fail', 'data' => [] ); + $this->log_order_api_response($endpoint, $message); $this->response($message, 200); } } @@ -1457,6 +1577,7 @@ class Pelanggan extends REST_Controller function inserttransaksimerchant_post() { + $endpoint = 'inserttransaksimerchant_post'; if (!isset($_SERVER['PHP_AUTH_USER'])) { header("WWW-Authenticate: Basic realm=\"Private Area\""); header("HTTP/1.0 401 Unauthorized"); @@ -1468,12 +1589,15 @@ class Pelanggan extends REST_Controller 'message' => 'fail', 'data' => 'Status User Banned' ); + log_message('debug', '[ORDER_API][' . $endpoint . '] banned user ' . $_SERVER['PHP_AUTH_USER']); + $this->log_order_api_response($endpoint, $message); $this->response($message, 200); } } $data = file_get_contents("php://input"); $dec_data = json_decode($data); + $this->log_order_api_request($endpoint, $data); $data_transaksi = array( 'id_pelanggan' => $dec_data->id_pelanggan, @@ -1531,9 +1655,14 @@ class Pelanggan extends REST_Controller $message = array( 'message' => 'success', 'data' => $result['data'], - - ); + $tid = isset($result['id_transaksi']) ? (int) $result['id_transaksi'] : 0; + $driverTargets = $tid > 0 + ? $this->Pelanggan_model->get_data_driver_histroy($tid)->result() + : array(); + $this->log_order_driver_targets($endpoint, $tid ?: 'unknown', $driverTargets); + $this->log_order_api_response($endpoint, $message); + $this->save_order_creation_logs($tid, $data, $message, $driverTargets); $this->response($message, 200); } else { $message = array( @@ -1541,6 +1670,7 @@ class Pelanggan extends REST_Controller 'data' => [] ); + $this->log_order_api_response($endpoint, $message); $this->response($message, 200); } } else { @@ -1549,6 +1679,7 @@ class Pelanggan extends REST_Controller 'data' => [] ); + $this->log_order_api_response($endpoint, $message); $this->response($message, 200); } } diff --git a/backendpanel/application/helpers/fcm_v1_helper.php b/backendpanel/application/helpers/fcm_v1_helper.php index 61c1bf1..dbccb1d 100755 --- a/backendpanel/application/helpers/fcm_v1_helper.php +++ b/backendpanel/application/helpers/fcm_v1_helper.php @@ -3,8 +3,9 @@ defined('BASEPATH') or exit('No direct script access allowed'); /** * FCM HTTP v1 API helper (Firebase Cloud Messaging). - * Uses OAuth2 access token from service account credentials (env: FCM_CREDENTIALS_PATH or FCM_CREDENTIALS_JSON). - * Requires FCM_PROJECT_ID. Optional: FCM_LIMIT_PER_HOUR, FCM_LIMIT_PER_DAY for quota (used by callers). + * OAuth2 access token from service account: FCM_CREDENTIALS_PATH or FCM_CREDENTIALS_JSON + * and FCM_PROJECT_ID (set in application/config/config.php). + * Optional: FCM_LIMIT_PER_HOUR, FCM_LIMIT_PER_DAY for quota (used by callers). */ if (!function_exists('fcm_v1_get_credentials')) { @@ -264,3 +265,80 @@ if (!function_exists('fcm_v1_send')) { return $resp; } } + +if (!function_exists('fcm_v1_device_token_from_request')) { + /** + * FCM device registration token from API JSON body (mobile APK). + * First non-empty among: firebase_token, fcm_token, reg_id, token. + * + * @param object|null $decoded_data json_decode() result + * @return string Trimmed token or empty string + */ + function fcm_v1_device_token_from_request($decoded_data) + { + if (!$decoded_data || !is_object($decoded_data)) { + return ''; + } + foreach (array('firebase_token', 'fcm_token', 'reg_id', 'token') as $key) { + if (!isset($decoded_data->$key)) { + continue; + } + $v = trim((string) $decoded_data->$key); + if ($v !== '') { + return $v; + } + } + return ''; + } +} + +if (!function_exists('fcm_v1_login_allow_missing_device_token')) { + /** + * When true, Pelanggan/Merchant/Driver login skips FCM token presence validation (e.g. Postman / legacy). + * Controlled by FCM_LOGIN_ALLOW_NO_DEVICE_TOKEN in application/config/config.php. + */ + function fcm_v1_login_allow_missing_device_token() + { + if (defined('FCM_LOGIN_ALLOW_NO_DEVICE_TOKEN')) { + return (bool) FCM_LOGIN_ALLOW_NO_DEVICE_TOKEN; + } + return false; + } +} + +if (!function_exists('fcm_v1_validate_login_device_token_from_app')) { + /** + * Enforces that the APK sent a Firebase-generated device token on login. + * Token is produced only on-device (FirebaseMessaging.getToken); backend never mints it. + * + * @param object|null $decoded_data json_decode body + * @return array|null Null if OK or enforcement disabled; else array with keys code, message for API response + */ + function fcm_v1_validate_login_device_token_from_app($decoded_data) + { + if (fcm_v1_login_allow_missing_device_token()) { + return null; + } + if (!function_exists('fcm_v1_device_token_from_request')) { + return null; + } + $raw = fcm_v1_device_token_from_request($decoded_data); + if ($raw === '') { + return array( + 'code' => '400', + 'message' => + 'Wajib kirim token FCM dari aplikasi (firebase_token, fcm_token, token, atau reg_id). ' + . 'Token dibuat oleh Firebase di perangkat setelah Firebase.initializeApp dan izin notifikasi.', + ); + } + if (!function_exists('fcm_v1_is_valid_device_token') || !fcm_v1_is_valid_device_token($raw)) { + return array( + 'code' => '400', + 'message' => + 'Token FCM tidak valid. Pastikan aplikasi berhasil memanggil Firebase Messaging getToken() ' + . 'di perangkat (bukan string buatan server).', + ); + } + return null; + } +} diff --git a/backendpanel/application/helpers/quota_limiter_helper.php b/backendpanel/application/helpers/quota_limiter_helper.php index 4fd776d..382ea37 100755 --- a/backendpanel/application/helpers/quota_limiter_helper.php +++ b/backendpanel/application/helpers/quota_limiter_helper.php @@ -14,7 +14,7 @@ defined('BASEPATH') or exit('No direct script access allowed'); * UNIQUE KEY uk_key_period (key_name, period_type, period_value) * ); * - * Config via env: FCM_LIMIT_PER_HOUR, FCM_LIMIT_PER_DAY, MAPS_LIMIT_PER_DAY, etc. + * Callers pass limits; backend defaults often use FCM_LIMIT_PER_HOUR / FCM_LIMIT_PER_DAY from config.php. */ if (!function_exists('quota_limiter_allow')) { diff --git a/backendpanel/application/models/Merchantapi_model.php b/backendpanel/application/models/Merchantapi_model.php index 0bf0aaa..8af5566 100755 --- a/backendpanel/application/models/Merchantapi_model.php +++ b/backendpanel/application/models/Merchantapi_model.php @@ -15,6 +15,16 @@ class Merchantapi_model extends CI_model } } + public function check_banned_by_email($email) + { + $stat = $this->db->query("SELECT id_mitra FROM mitra WHERE status_mitra='3' AND email_mitra='$email'"); + if ($stat->num_rows() == 1) { + return true; + } else { + return false; + } + } + public function check_exist($email, $phone) { $cek = $this->db->query("SELECT id_mitra FROM mitra where email_mitra = '$email' AND telepon_mitra='$phone'"); diff --git a/backendpanel/application/models/Notification_model.php b/backendpanel/application/models/Notification_model.php index f9df4f6..5e83c20 100755 --- a/backendpanel/application/models/Notification_model.php +++ b/backendpanel/application/models/Notification_model.php @@ -64,11 +64,7 @@ class notification_model extends CI_model public function send_notif($title, $message, $target) { if ($this->is_token_empty($target)) { - log_message('debug', 'send_notif: skip, no token'); - return true; - } - if (!$this->is_valid_fcm_token($target)) { - log_message('debug', 'send_notif: skip, invalid/placeholder FCM token'); + log_message('debug', 'send_notif: skip, empty target'); return true; } if (!function_exists('fcm_v1_validate_token')) { @@ -87,7 +83,11 @@ class notification_model extends CI_model 'title' => $title, 'body' => $message, ); - return $this->send_generic_to_token($target, $data, $options); + if ($this->is_valid_fcm_token($target)) { + return $this->send_generic_to_token($target, $data, $options); + } + // Values like pelanggan, driver, mitra, ouride are FCM topic names (broadcast). + return $this->send_generic_to_topic(trim((string) $target), $data, $options); } public function send_notif_topup($title, $id, $message, $method, $token) diff --git a/backendpanel/application/models/Pelanggan_model.php b/backendpanel/application/models/Pelanggan_model.php index 15f3883..8a622fd 100755 --- a/backendpanel/application/models/Pelanggan_model.php +++ b/backendpanel/application/models/Pelanggan_model.php @@ -309,7 +309,7 @@ class Pelanggan_model extends CI_model FROM config_driver ld, driver d, driver_job dj, kendaraan k, saldo s, fitur f WHERE ld.id_driver = d.id AND f.id_fitur = $fitur - AND ld.status = '1' + AND ld.status IN ('1','4') AND dj.id = d.job AND d.job = f.driver_job AND d.status = '1' @@ -334,9 +334,9 @@ class Pelanggan_model extends CI_model */ private function log_driver_diagnostic($type, $lat, $lng, $fitur, $lat_min, $lat_max, $lng_min, $lng_max, $radius_km) { - $r_online = $this->db->query("SELECT COUNT(*) as c FROM config_driver WHERE status='1'")->row(); + $r_online = $this->db->query("SELECT COUNT(*) as c FROM config_driver WHERE status IN ('1','4')")->row(); $cnt_online = $r_online ? (int) $r_online->c : 0; - $r_box = $this->db->query("SELECT COUNT(*) as c FROM config_driver ld JOIN driver d ON ld.id_driver=d.id WHERE ld.status='1' AND d.status='1' AND ld.latitude BETWEEN " . (float)$lat_min . " AND " . (float)$lat_max . " AND ld.longitude BETWEEN " . (float)$lng_min . " AND " . (float)$lng_max)->row(); + $r_box = $this->db->query("SELECT COUNT(*) as c FROM config_driver ld JOIN driver d ON ld.id_driver=d.id WHERE ld.status IN ('1','4') AND d.status='1' AND ld.latitude BETWEEN " . (float)$lat_min . " AND " . (float)$lat_max . " AND ld.longitude BETWEEN " . (float)$lng_min . " AND " . (float)$lng_max)->row(); $cnt_in_box = $r_box ? (int) $r_box->c : 0; $fitur_row = $this->db->query("SELECT driver_job, jarak_minimum, wallet_minimum FROM fitur WHERE id_fitur=" . (int)$fitur)->row(); $driver_job = $fitur_row ? $fitur_row->driver_job : null; @@ -381,7 +381,7 @@ class Pelanggan_model extends CI_model FROM config_driver ld, driver d, driver_job dj, kendaraan k, saldo s, fitur f WHERE ld.id_driver = d.id AND f.id_fitur = $fitur - AND ld.status = '1' + AND ld.status IN ('1','4') AND dj.id = d.job AND d.job = f.driver_job AND d.status = '1' @@ -1849,5 +1849,80 @@ class Pelanggan_model extends CI_model $this->db->update('history_transaksi', $data); return true; } + + /** + * Customers for admin FCM screen: label with fullnama / phone / email plus id. + */ + public function get_all_for_notification_picker() + { + $this->db->select('id, fullnama, email, no_telepon, token'); + $this->db->from('pelanggan'); + $this->db->order_by('fullnama', 'ASC'); + $this->db->order_by('id', 'ASC'); + return $this->db->get()->result(); + } + + /** + * One row for sending push: registration token and display name. + */ + public function get_notification_row_by_id($id) + { + $this->db->select('id, fullnama, token'); + $this->db->from('pelanggan'); + $this->db->where('id', (int) $id); + return $this->db->get()->row(); + } + + /** + * Truncate for MEDIUMTEXT logging (safety cap). + */ + private function truncate_api_log_value($value, $max_bytes = 1048576) + { + $s = is_string($value) ? $value : (string) $value; + if (strlen($s) > $max_bytes) { + return substr($s, 0, $max_bytes) . "\n...[truncated]"; + } + return $s; + } + + /** + * Persist raw pelanggan order API + backend snapshot for driver candidates (admin detail). + */ + public function save_transaksi_api_log($id_transaksi, $raw_request, $response_json, $backend_to_driver_json) + { + $id_transaksi = (int) $id_transaksi; + if ($id_transaksi <= 0) { + return; + } + $this->db->where('id', $id_transaksi); + $this->db->update('transaksi', array( + 'api_log_pelanggan_request' => $this->truncate_api_log_value($raw_request), + 'api_log_pelanggan_response' => $this->truncate_api_log_value($response_json), + 'api_log_backend_to_driver' => $this->truncate_api_log_value($backend_to_driver_json), + )); + } + + /** + * Append driver → backend raw body (e.g. accept_post JSON) for this transaction. + */ + public function append_transaksi_driver_request_log($id_transaksi, $raw_inbound) + { + $id_transaksi = (int) $id_transaksi; + if ($id_transaksi <= 0) { + return; + } + $raw_inbound = $this->truncate_api_log_value($raw_inbound, 262144); + $this->db->select('api_log_driver_request'); + $this->db->where('id', $id_transaksi); + $row = $this->db->get('transaksi')->row(); + $prev = ($row && isset($row->api_log_driver_request) && $row->api_log_driver_request !== null) + ? (string) $row->api_log_driver_request : ''; + $stamp = date('c'); + $line = '[' . $stamp . "] accept/driver inbound:\n" . $raw_inbound; + $merged = $prev === '' ? $line : $prev . "\n\n" . $line; + $merged = $this->truncate_api_log_value($merged, 1048576); + $this->db->where('id', $id_transaksi); + $this->db->update('transaksi', array('api_log_driver_request' => $merged)); + } } diff --git a/backendpanel/application/views/appnotification/index.php b/backendpanel/application/views/appnotification/index.php index 6064464..fbe7942 100755 --- a/backendpanel/application/views/appnotification/index.php +++ b/backendpanel/application/views/appnotification/index.php @@ -17,14 +17,55 @@
- - Topik (broadcast) + + +
+ +
+ +
+ +
diff --git a/backendpanel/application/views/dashboard/detailtransaction.php b/backendpanel/application/views/dashboard/detailtransaction.php index b071a38..42cff95 100755 --- a/backendpanel/application/views/dashboard/detailtransaction.php +++ b/backendpanel/application/views/dashboard/detailtransaction.php @@ -233,6 +233,36 @@


+ + +
+
Log API pesanan
+ +
Pelanggan → backend (raw request)
+
+ + +
Backend → pelanggan (raw response)
+
+ + +
Backend → driver (snapshot / kandidat)
+
+ + +
Driver ↔ backend (accept: raw + response JSON)
+
+ +
+
+ +
diff --git a/backendpanel/sql/alter_transaksi_api_log.sql b/backendpanel/sql/alter_transaksi_api_log.sql new file mode 100644 index 0000000..6ce6a3a --- /dev/null +++ b/backendpanel/sql/alter_transaksi_api_log.sql @@ -0,0 +1,8 @@ +-- Run once on the application database so order detail can show API traces. +-- MySQL / MariaDB + +ALTER TABLE `transaksi` + ADD COLUMN `api_log_pelanggan_request` MEDIUMTEXT NULL COMMENT 'Raw pelanggan app JSON body on order create', + ADD COLUMN `api_log_pelanggan_response` MEDIUMTEXT NULL COMMENT 'JSON response body returned to pelanggan', + ADD COLUMN `api_log_backend_to_driver` MEDIUMTEXT NULL COMMENT 'Backend snapshot: candidate drivers JSON', + ADD COLUMN `api_log_driver_request` MEDIUMTEXT NULL COMMENT 'Driver accept: raw request + backend response JSON lines'; diff --git a/build_all.sh b/build_all.sh new file mode 100755 index 0000000..bc06da0 --- /dev/null +++ b/build_all.sh @@ -0,0 +1,167 @@ +#!/usr/bin/env bash + +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Prefer Java 17 for old Gradle builds to avoid "Could not determine java version from '21.x'" +JAVA17_CANDIDATE="/usr/lib/jvm/java-17-openjdk-amd64" +if [[ -d "${JAVA17_CANDIDATE}" ]]; then + echo "Using Java 17 from ${JAVA17_CANDIDATE}" + export JAVA_HOME="${JAVA17_CANDIDATE}" + export ORG_GRADLE_JAVA_HOME="${JAVA17_CANDIDATE}" + export PATH="${JAVA17_CANDIDATE}/bin:${PATH}" +else + echo "⚠ Java 17 JDK not found at ${JAVA17_CANDIDATE}. Using system default Java:" +fi + +echo "JAVA_HOME=${JAVA_HOME:-}" +echo "ORG_GRADLE_JAVA_HOME=${ORG_GRADLE_JAVA_HOME:-}" +java -version || echo "Java not found on PATH – builds will likely fail." +echo + +OUT_DIR="${ROOT_DIR}/apk_output" +mkdir -p "${OUT_DIR}" + +native_apps=( + "OnTime_User_live" + "OnTime_Driver_live" + "OnTime_Merchant_live" +) + +flutter_candidates=( + "flutter_user_clone" + "flutter_merchant_clone" + "flutter_driver_clone" + "ontime_user_flutter" + "ontime_merchant_flutter" + "ontime_driver_flutter" +) + +declare -a successes=() +declare -a failures=() + +build_flutter_apk() { + local app_dir="$1" + + echo "========================================" + echo "Building Flutter app in: ${app_dir}" + echo "========================================" + + cd "${ROOT_DIR}/${app_dir}" + + if ! flutter clean; then + echo "❌ flutter clean failed for ${app_dir}" + failures+=("${app_dir} (flutter clean)") + return 1 + fi + + if ! flutter pub get; then + echo "❌ flutter pub get failed for ${app_dir}" + failures+=("${app_dir} (flutter pub get)") + return 1 + fi + + if ! flutter build apk --release; then + echo "❌ flutter build apk failed for ${app_dir}" + failures+=("${app_dir} (flutter build apk)") + return 1 + fi + + local apk_path="${ROOT_DIR}/${app_dir}/build/app/outputs/flutter-apk/app-release.apk" + local out_name="${app_dir}_flutter-release.apk" + if [[ -f "${apk_path}" ]]; then + cp "${apk_path}" "${OUT_DIR}/${out_name}" + echo "📦 Copied to: ${OUT_DIR}/${out_name}" + else + echo "❌ Flutter release APK not found at: ${apk_path}" + failures+=("${app_dir} (apk missing)") + return 1 + fi + + echo "✅ Finished building ${app_dir}" + echo + successes+=("${app_dir} (flutter)") + return 0 +} + +build_native_apk() { + local app_dir="$1" + + echo "========================================" + echo "Building native Android app in: ${app_dir}" + echo "========================================" + + cd "${ROOT_DIR}/${app_dir}" + if ! ./gradlew "assembleRelease" --no-daemon; then + echo "❌ Gradle build failed for ${app_dir}" + failures+=("${app_dir} (gradle assembleRelease)") + return 1 + fi + + local apk_path="${ROOT_DIR}/${app_dir}/app/build/outputs/apk/release/app-release.apk" + local out_name="${app_dir}_native-release.apk" + if [[ -f "${apk_path}" ]]; then + cp "${apk_path}" "${OUT_DIR}/${out_name}" + echo "📦 Copied to: ${OUT_DIR}/${out_name}" + else + echo "❌ Native release APK not found at: ${apk_path}" + failures+=("${app_dir} (apk missing)") + return 1 + fi + + echo "✅ Finished building ${app_dir}" + echo + successes+=("${app_dir} (java)") + return 0 +} + +main() { + local found_flutter=0 + for app in "${flutter_candidates[@]}"; do + if [[ -d "${ROOT_DIR}/${app}" ]]; then + found_flutter=1 + build_flutter_apk "${app}" || true + fi + done + + if [[ "${found_flutter}" -eq 0 ]]; then + echo "⚠️ No Flutter app directories found from configured candidates." + echo + fi + + for app in "${native_apps[@]}"; do + if [[ -d "${ROOT_DIR}/${app}" ]]; then + build_native_apk "${app}" || true + else + echo "⚠️ Skipping ${app}: directory not found at ${ROOT_DIR}/${app}" + failures+=("${app} (directory missing)") + fi + done + + echo + echo "========================================" + echo "Build Summary" + echo "========================================" + echo "✅ Success count: ${#successes[@]}" + for item in "${successes[@]}"; do + echo " - ${item}" + done + + echo "❌ Failure count: ${#failures[@]}" + for item in "${failures[@]}"; do + echo " - ${item}" + done + + echo + echo "APK output directory: ${OUT_DIR}" + + if [[ "${#failures[@]}" -gt 0 ]]; then + exit 1 + fi + + echo "🎉 All builds completed successfully." +} + +main "$@" + diff --git a/flutter_conversion_notes.md b/flutter_conversion_notes.md new file mode 100644 index 0000000..df8a596 --- /dev/null +++ b/flutter_conversion_notes.md @@ -0,0 +1,23 @@ +# Flutter Conversion (Initial) + +Created new Flutter app folders: + +- `flutter_driver_clone` +- `flutter_user_clone` +- `flutter_merchant_clone` + +Each app currently includes: + +- `pubspec.yaml` +- `lib/main.dart` with a login screen layout modeled from Java UI + +Run each app: + +1. `cd flutter_driver_clone && flutter pub get && flutter run` +2. `cd flutter_user_clone && flutter pub get && flutter run` +3. `cd flutter_merchant_clone && flutter pub get && flutter run` + +Note: + +- The structure and visual composition are matched to the Java login screens first. +- To make the interface exactly identical on all screens, the next step is converting each remaining XML screen (`register`, `main`, `wallet`, `chat`, etc.) one-by-one into Flutter widgets. diff --git a/ontime_driver_flutter/.gitignore b/flutter_driver_clone/.gitignore similarity index 100% rename from ontime_driver_flutter/.gitignore rename to flutter_driver_clone/.gitignore diff --git a/ontime_driver_flutter/.metadata b/flutter_driver_clone/.metadata similarity index 100% rename from ontime_driver_flutter/.metadata rename to flutter_driver_clone/.metadata diff --git a/flutter_driver_clone/README.md b/flutter_driver_clone/README.md new file mode 100644 index 0000000..6de4c2f --- /dev/null +++ b/flutter_driver_clone/README.md @@ -0,0 +1,17 @@ +# flutter_driver_clone + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Learn Flutter](https://docs.flutter.dev/get-started/learn-flutter) +- [Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Flutter learning resources](https://docs.flutter.dev/reference/learning-resources) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/ontime_driver_flutter/analysis_options.yaml b/flutter_driver_clone/analysis_options.yaml similarity index 100% rename from ontime_driver_flutter/analysis_options.yaml rename to flutter_driver_clone/analysis_options.yaml diff --git a/ontime_driver_flutter/android/.gitignore b/flutter_driver_clone/android/.gitignore similarity index 100% rename from ontime_driver_flutter/android/.gitignore rename to flutter_driver_clone/android/.gitignore diff --git a/ontime_user_flutter/android/app/build.gradle.kts b/flutter_driver_clone/android/app/build.gradle.kts similarity index 91% rename from ontime_user_flutter/android/app/build.gradle.kts rename to flutter_driver_clone/android/app/build.gradle.kts index 58e2ab8..1819a74 100644 --- a/ontime_user_flutter/android/app/build.gradle.kts +++ b/flutter_driver_clone/android/app/build.gradle.kts @@ -3,10 +3,11 @@ plugins { id("kotlin-android") // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. id("dev.flutter.flutter-gradle-plugin") + id("com.google.gms.google-services") } android { - namespace = "com.example.ontime_user_flutter" + namespace = "id.ontime.driver" compileSdk = flutter.compileSdkVersion ndkVersion = flutter.ndkVersion @@ -21,7 +22,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId = "com.example.ontime_user_flutter" + applicationId = "id.ontime.driver" // You can update the following values to match your application needs. // For more information, see: https://flutter.dev/to/review-gradle-config. minSdk = flutter.minSdkVersion diff --git a/ontime_driver_flutter/android/app/src/debug/AndroidManifest.xml b/flutter_driver_clone/android/app/src/debug/AndroidManifest.xml similarity index 100% rename from ontime_driver_flutter/android/app/src/debug/AndroidManifest.xml rename to flutter_driver_clone/android/app/src/debug/AndroidManifest.xml diff --git a/ontime_merchant_flutter/android/app/src/main/AndroidManifest.xml b/flutter_driver_clone/android/app/src/main/AndroidManifest.xml similarity index 92% rename from ontime_merchant_flutter/android/app/src/main/AndroidManifest.xml rename to flutter_driver_clone/android/app/src/main/AndroidManifest.xml index 344bb38..1f3b36e 100644 --- a/ontime_merchant_flutter/android/app/src/main/AndroidManifest.xml +++ b/flutter_driver_clone/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,8 @@ + + main() async { + WidgetsFlutterBinding.ensureInitialized(); + await Firebase.initializeApp(); + runApp(const DriverCloneApp()); +} + +class DriverCloneApp extends StatelessWidget { + const DriverCloneApp({super.key}); + + static const Color primary = Color(0xFF5BC8DA); + + @override + Widget build(BuildContext context) { + return MaterialApp( + debugShowCheckedModeBanner: false, + title: 'OnTime Driver Clone', + theme: ThemeData( + colorScheme: ColorScheme.fromSeed(seedColor: primary), + scaffoldBackgroundColor: Colors.white, + useMaterial3: true, + ), + home: const DriverLoginPage(), + ); + } +} + +class DriverLoginPage extends StatefulWidget { + const DriverLoginPage({super.key}); + + @override + State createState() => _DriverLoginPageState(); +} + +class _DriverLoginPageState extends State { + final _phone = TextEditingController(); + final _password = TextEditingController(); + bool _busy = false; + + @override + void dispose() { + _phone.dispose(); + _password.dispose(); + super.dispose(); + } + + Future _signIn() async { + final pass = _password.text; + final digits = _phone.text.trim(); + if (pass.isEmpty) { + _toast('Isi password'); + return; + } + if (digits.isEmpty) { + _toast('Isi nomor telepon'); + return; + } + + setState(() => _busy = true); + try { + await FirebaseMessaging.instance.requestPermission(); + final token = await FirebaseMessaging.instance.getToken(); + if (token == null || token.length < 50) { + _toast( + 'Token FCM belum siap. Izinkan notifikasi & pastikan google-services.json / API key Firebase valid.', + ); + return; + } + + final result = await OntimeAuth.login( + path: _kLoginPath, + password: pass, + phoneDigits: digits, + dialCodeWithoutPlus: _kDial, + fcmToken: token, + ); + + if (!mounted) return; + if (result.success) { + Navigator.of(context).pushReplacement( + MaterialPageRoute( + builder: (_) => const _HomePlaceholder(title: 'OnTime Driver'), + ), + ); + } else { + _toast(result.message); + } + } catch (e) { + if (mounted) _toast('$e'); + } finally { + if (mounted) setState(() => _busy = false); + } + } + + void _toast(String msg) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(msg))); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: SafeArea( + child: Stack( + children: [ + SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.only(bottom: 92), + child: Column( + children: [ + const SizedBox(height: 20), + const SizedBox( + height: 250, + child: Center( + child: Icon(Icons.local_shipping, size: 120, color: DriverCloneApp.primary), + ), + ), + Container( + margin: const EdgeInsets.fromLTRB(15, 0, 15, 15), + padding: const EdgeInsets.all(15), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16), + boxShadow: const [BoxShadow(color: Color(0x22000000), blurRadius: 8, offset: Offset(0, 4))], + ), + child: Column( + children: [ + const SizedBox(height: 20), + _PhoneField(controller: _phone), + const SizedBox(height: 10), + _PasswordField(controller: _password), + const SizedBox(height: 20), + const Align( + alignment: Alignment.center, + child: Text('Lupa Password?', style: TextStyle(color: DriverCloneApp.primary, fontSize: 14)), + ), + const SizedBox(height: 10), + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + onPressed: _busy ? null : _signIn, + style: ElevatedButton.styleFrom( + backgroundColor: DriverCloneApp.primary, + foregroundColor: Colors.white, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + ), + child: _busy + ? const SizedBox( + height: 22, + width: 22, + child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white), + ) + : const Text('Sign In', style: TextStyle(fontSize: 18)), + ), + ), + const SizedBox(height: 16), + const Text( + 'Dengan masuk, Anda menyetujui kebijakan privasi.', + textAlign: TextAlign.center, + style: TextStyle(fontSize: 13), + ), + ], + ), + ), + ], + ), + ), + ), + Positioned( + top: 15, + left: 15, + child: Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + border: Border.all(color: const Color(0xFFDDDDDD)), + ), + child: const Icon(Icons.arrow_back, size: 20), + ), + ), + Positioned( + left: 0, + right: 0, + bottom: 0, + child: Container( + height: 80, + color: Colors.white, + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8), + child: const Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('Belum punya akun? ', style: TextStyle(fontSize: 15)), + Text('Sign Up', style: TextStyle(fontSize: 18, color: DriverCloneApp.primary, fontWeight: FontWeight.w600)), + ], + ), + SizedBox(height: 8), + Text( + 'Note: Jika sudah mendaftar akun baru, tunggu konfirmasi admin untuk info lebih lanjut.', + textAlign: TextAlign.center, + style: TextStyle(fontSize: 11), + ), + ], + ), + ), + ), + ], + ), + ), + ); + } +} + +class _HomePlaceholder extends StatelessWidget { + const _HomePlaceholder({required this.title}); + + final String title; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: Text(title)), + body: const Center(child: Text('Login berhasil')), + ); + } +} + +class _PhoneField extends StatelessWidget { + const _PhoneField({required this.controller}); + + final TextEditingController controller; + + @override + Widget build(BuildContext context) { + return Container( + height: 50, + decoration: BoxDecoration(border: Border.all(color: const Color(0xFFC4C4C4)), borderRadius: BorderRadius.circular(8)), + child: Row( + children: [ + const SizedBox(width: 80, child: Center(child: Text('+62', style: TextStyle(color: DriverCloneApp.primary, fontSize: 18)))), + Container(width: 1, margin: const EdgeInsets.symmetric(vertical: 6), color: const Color(0xFFC4C4C4)), + Expanded( + child: TextField( + controller: controller, + keyboardType: TextInputType.phone, + decoration: const InputDecoration( + hintText: 'Nomor Telepon', + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(horizontal: 10), + ), + ), + ), + ], + ), + ); + } +} + +class _PasswordField extends StatelessWidget { + const _PasswordField({required this.controller}); + + final TextEditingController controller; + + @override + Widget build(BuildContext context) { + return Container( + height: 50, + decoration: BoxDecoration(border: Border.all(color: const Color(0xFFC4C4C4)), borderRadius: BorderRadius.circular(8)), + child: Row( + children: [ + const SizedBox(width: 80, child: Icon(Icons.lock_outline, color: DriverCloneApp.primary)), + const VerticalDivider(width: 1, thickness: 1, indent: 6, endIndent: 6), + Expanded( + child: TextField( + controller: controller, + obscureText: true, + decoration: const InputDecoration( + hintText: 'Password', + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(horizontal: 10), + ), + ), + ), + ], + ), + ); + } +} diff --git a/flutter_driver_clone/lib/ontime_auth.dart b/flutter_driver_clone/lib/ontime_auth.dart new file mode 100644 index 0000000..09e5abf --- /dev/null +++ b/flutter_driver_clone/lib/ontime_auth.dart @@ -0,0 +1,94 @@ +import 'dart:convert'; + +import 'package:http/http.dart' as http; + +class LoginResult { + LoginResult({required this.success, required this.message}); + + final bool success; + final String message; +} + +/// Matches native `ServiceGenerator.createService` + login POST (Basic auth + JSON body). +class OntimeAuth { + OntimeAuth._(); + + static const String baseUrl = 'https://apitest.semestaterpadu.my.id/api/'; + + static Future login({ + required String path, + required String password, + String email = '', + required String phoneDigits, + required String dialCodeWithoutPlus, + required String fcmToken, + }) async { + final cleanedDial = dialCodeWithoutPlus.replaceAll('+', '').trim(); + final digitsOnly = phoneDigits.replaceAll(RegExp(r'\D'), ''); + final local = digitsOnly.replaceFirst(RegExp(r'^0+'), ''); + final fullPhone = digitsOnly.startsWith(cleanedDial) + ? digitsOnly + : '$cleanedDial$local'; + final useEmail = email.trim().isNotEmpty; + final basicUser = useEmail ? email.trim() : fullPhone; + + final body = { + 'password': password, + 'token': fcmToken, + if (useEmail) ...{ + 'email': email.trim(), + 'no_telepon': '', + } else ...{ + 'email': null, + 'no_telepon': fullPhone, + }, + }; + + final uri = Uri.parse('$baseUrl$path'); + final auth = base64Encode(utf8.encode('$basicUser:$password')); + + final resp = await http + .post( + uri, + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'Authorization': 'Basic $auth', + }, + body: jsonEncode(body), + ) + .timeout(const Duration(seconds: 60)); + + Map? json; + try { + if (resp.body.isNotEmpty) { + final decoded = jsonDecode(resp.body); + if (decoded is Map) json = decoded; + } + } catch (_) {} + + final msg = json?['message']?.toString() ?? ''; + final data = json?['data']; + final hasData = data is List && data.isNotEmpty; + final ok = resp.statusCode >= 200 && + resp.statusCode < 300 && + msg.toLowerCase() == 'found' && + hasData; + + if (ok) return LoginResult(success: true, message: msg); + + if (msg.isNotEmpty) { + return LoginResult(success: false, message: msg); + } + if (resp.statusCode == 401) { + return LoginResult(success: false, message: 'no hp atau password salah!'); + } + if (resp.body.isNotEmpty && resp.body.length < 400) { + return LoginResult(success: false, message: resp.body); + } + return LoginResult( + success: false, + message: 'Login gagal (HTTP ${resp.statusCode})', + ); + } +} diff --git a/flutter_driver_clone/pubspec.lock b/flutter_driver_clone/pubspec.lock new file mode 100644 index 0000000..11e3d06 --- /dev/null +++ b/flutter_driver_clone/pubspec.lock @@ -0,0 +1,306 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _flutterfire_internals: + dependency: transitive + description: + name: _flutterfire_internals + sha256: ff0a84a2734d9e1089f8aedd5c0af0061b82fb94e95260d943404e0ef2134b11 + url: "https://pub.dev" + source: hosted + version: "1.3.59" + async: + dependency: transitive + description: + name: async + sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37 + url: "https://pub.dev" + source: hosted + version: "2.13.1" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + characters: + dependency: transitive + description: + name: characters + sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b + url: "https://pub.dev" + source: hosted + version: "1.4.1" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.dev" + source: hosted + version: "1.3.3" + firebase_core: + dependency: "direct main" + description: + name: firebase_core + sha256: "7be63a3f841fc9663342f7f3a011a42aef6a61066943c90b1c434d79d5c995c5" + url: "https://pub.dev" + source: hosted + version: "3.15.2" + firebase_core_platform_interface: + dependency: transitive + description: + name: firebase_core_platform_interface + sha256: "0ecda14c1bfc9ed8cac303dd0f8d04a320811b479362a9a4efb14fd331a473ce" + url: "https://pub.dev" + source: hosted + version: "6.0.3" + firebase_core_web: + dependency: transitive + description: + name: firebase_core_web + sha256: "0ed0dc292e8f9ac50992e2394e9d336a0275b6ae400d64163fdf0a8a8b556c37" + url: "https://pub.dev" + source: hosted + version: "2.24.1" + firebase_messaging: + dependency: "direct main" + description: + name: firebase_messaging + sha256: "60be38574f8b5658e2f22b7e311ff2064bea835c248424a383783464e8e02fcc" + url: "https://pub.dev" + source: hosted + version: "15.2.10" + firebase_messaging_platform_interface: + dependency: transitive + description: + name: firebase_messaging_platform_interface + sha256: "685e1771b3d1f9c8502771ccc9f91485b376ffe16d553533f335b9183ea99754" + url: "https://pub.dev" + source: hosted + version: "4.6.10" + firebase_messaging_web: + dependency: transitive + description: + name: firebase_messaging_web + sha256: "0d1be17bc89ed3ff5001789c92df678b2e963a51b6fa2bdb467532cc9dbed390" + url: "https://pub.dev" + source: hosted + version: "3.10.10" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + http: + dependency: "direct main" + description: + name: http + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" + url: "https://pub.dev" + source: hosted + version: "1.6.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" + url: "https://pub.dev" + source: hosted + version: "11.0.2" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" + url: "https://pub.dev" + source: hosted + version: "3.0.10" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + lints: + dependency: transitive + description: + name: lints + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 + url: "https://pub.dev" + source: hosted + version: "3.0.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 + url: "https://pub.dev" + source: hosted + version: "0.12.19" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" + url: "https://pub.dev" + source: hosted + version: "0.13.0" + meta: + dependency: transitive + description: + name: meta + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + url: "https://pub.dev" + source: hosted + version: "1.17.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + source_span: + dependency: transitive + description: + name: source_span + sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" + url: "https://pub.dev" + source: hosted + version: "1.10.2" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + test_api: + dependency: transitive + description: + name: test_api + sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" + url: "https://pub.dev" + source: hosted + version: "0.7.10" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + url: "https://pub.dev" + source: hosted + version: "2.2.0" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" + url: "https://pub.dev" + source: hosted + version: "15.0.2" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" +sdks: + dart: ">=3.9.0-0 <4.0.0" + flutter: ">=3.22.0" diff --git a/flutter_driver_clone/pubspec.yaml b/flutter_driver_clone/pubspec.yaml new file mode 100644 index 0000000..4568894 --- /dev/null +++ b/flutter_driver_clone/pubspec.yaml @@ -0,0 +1,22 @@ +name: flutter_driver_clone +description: OnTime Driver Flutter conversion +publish_to: "none" +version: 1.0.0+1 + +environment: + sdk: ">=3.3.0 <4.0.0" + +dependencies: + flutter: + sdk: flutter + http: ^1.2.2 + firebase_core: ^3.8.1 + firebase_messaging: ^15.1.5 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^3.0.0 + +flutter: + uses-material-design: true diff --git a/flutter_driver_clone/test/widget_test.dart b/flutter_driver_clone/test/widget_test.dart new file mode 100644 index 0000000..d7c02f3 --- /dev/null +++ b/flutter_driver_clone/test/widget_test.dart @@ -0,0 +1,11 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:flutter_driver_clone/main.dart'; + +void main() { + testWidgets('app launches', (WidgetTester tester) async { + await tester.pumpWidget(const DriverCloneApp()); + await tester.pumpAndSettle(); + expect(find.text('Sign In'), findsOneWidget); + }); +} diff --git a/ontime_merchant_flutter/.gitignore b/flutter_merchant_clone/.gitignore similarity index 100% rename from ontime_merchant_flutter/.gitignore rename to flutter_merchant_clone/.gitignore diff --git a/ontime_merchant_flutter/.metadata b/flutter_merchant_clone/.metadata similarity index 100% rename from ontime_merchant_flutter/.metadata rename to flutter_merchant_clone/.metadata diff --git a/flutter_merchant_clone/README.md b/flutter_merchant_clone/README.md new file mode 100644 index 0000000..9797528 --- /dev/null +++ b/flutter_merchant_clone/README.md @@ -0,0 +1,17 @@ +# flutter_merchant_clone + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Learn Flutter](https://docs.flutter.dev/get-started/learn-flutter) +- [Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Flutter learning resources](https://docs.flutter.dev/reference/learning-resources) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/ontime_merchant_flutter/analysis_options.yaml b/flutter_merchant_clone/analysis_options.yaml similarity index 100% rename from ontime_merchant_flutter/analysis_options.yaml rename to flutter_merchant_clone/analysis_options.yaml diff --git a/ontime_merchant_flutter/android/.gitignore b/flutter_merchant_clone/android/.gitignore similarity index 100% rename from ontime_merchant_flutter/android/.gitignore rename to flutter_merchant_clone/android/.gitignore diff --git a/ontime_driver_flutter/android/app/build.gradle.kts b/flutter_merchant_clone/android/app/build.gradle.kts similarity index 91% rename from ontime_driver_flutter/android/app/build.gradle.kts rename to flutter_merchant_clone/android/app/build.gradle.kts index b5017be..187e53e 100644 --- a/ontime_driver_flutter/android/app/build.gradle.kts +++ b/flutter_merchant_clone/android/app/build.gradle.kts @@ -3,10 +3,11 @@ plugins { id("kotlin-android") // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. id("dev.flutter.flutter-gradle-plugin") + id("com.google.gms.google-services") } android { - namespace = "com.example.ontime_driver_flutter" + namespace = "id.ontime.merchant" compileSdk = flutter.compileSdkVersion ndkVersion = flutter.ndkVersion @@ -21,7 +22,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId = "com.example.ontime_driver_flutter" + applicationId = "id.ontime.merchant" // You can update the following values to match your application needs. // For more information, see: https://flutter.dev/to/review-gradle-config. minSdk = flutter.minSdkVersion diff --git a/ontime_merchant_flutter/android/app/src/debug/AndroidManifest.xml b/flutter_merchant_clone/android/app/src/debug/AndroidManifest.xml similarity index 100% rename from ontime_merchant_flutter/android/app/src/debug/AndroidManifest.xml rename to flutter_merchant_clone/android/app/src/debug/AndroidManifest.xml diff --git a/ontime_user_flutter/android/app/src/main/AndroidManifest.xml b/flutter_merchant_clone/android/app/src/main/AndroidManifest.xml similarity index 92% rename from ontime_user_flutter/android/app/src/main/AndroidManifest.xml rename to flutter_merchant_clone/android/app/src/main/AndroidManifest.xml index f1db6c6..c05be01 100644 --- a/ontime_user_flutter/android/app/src/main/AndroidManifest.xml +++ b/flutter_merchant_clone/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,8 @@ + + main() async { + WidgetsFlutterBinding.ensureInitialized(); + await Firebase.initializeApp(); + runApp(const MerchantCloneApp()); +} + +class MerchantCloneApp extends StatelessWidget { + const MerchantCloneApp({super.key}); + + static const Color primary = Color(0xFF5BC8DA); + + @override + Widget build(BuildContext context) { + return MaterialApp( + debugShowCheckedModeBanner: false, + title: 'id.ontime.merchant', + theme: ThemeData( + colorScheme: ColorScheme.fromSeed(seedColor: primary), + useMaterial3: true, + ), + home: const MerchantLoginPage(), + ); + } +} + +class MerchantLoginPage extends StatefulWidget { + const MerchantLoginPage({super.key}); + + @override + State createState() => _MerchantLoginPageState(); +} + +class _MerchantLoginPageState extends State { + final _phone = TextEditingController(); + final _password = TextEditingController(); + bool _busy = false; + + @override + void dispose() { + _phone.dispose(); + _password.dispose(); + super.dispose(); + } + + Future _signIn() async { + final pass = _password.text; + final digits = _phone.text.trim(); + if (pass.isEmpty) { + _toast('Isi password'); + return; + } + if (digits.isEmpty) { + _toast('Isi nomor telepon'); + return; + } + + setState(() => _busy = true); + try { + await FirebaseMessaging.instance.requestPermission(); + final token = await FirebaseMessaging.instance.getToken(); + if (token == null || token.length < 50) { + _toast( + 'Token FCM belum siap. Izinkan notifikasi & pastikan google-services.json / API key Firebase valid.', + ); + return; + } + + final result = await OntimeAuth.login( + path: _kLoginPath, + password: pass, + phoneDigits: digits, + dialCodeWithoutPlus: _kDial, + fcmToken: token, + ); + + if (!mounted) return; + if (result.success) { + _toast('Login sukses, token FCM Firebase sudah dikirim ke backend (FCM v1).'); + Navigator.of(context).pushReplacement( + MaterialPageRoute( + builder: (_) => const _HomePlaceholder(title: 'id.ontime.merchant'), + ), + ); + } else { + _toast(result.message); + } + } catch (e) { + if (mounted) _toast('$e'); + } finally { + if (mounted) setState(() => _busy = false); + } + } + + void _toast(String msg) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(msg))); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: SafeArea( + child: Stack( + children: [ + SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.only(bottom: 92), + child: Column( + children: [ + const SizedBox(height: 20), + const SizedBox( + height: 250, + child: Center( + child: Icon(Icons.storefront, size: 120, color: MerchantCloneApp.primary), + ), + ), + Container( + margin: const EdgeInsets.fromLTRB(15, 0, 15, 20), + padding: const EdgeInsets.all(15), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16), + boxShadow: const [BoxShadow(color: Color(0x22000000), blurRadius: 8, offset: Offset(0, 4))], + ), + child: Column( + children: [ + _MerchantInput( + hint: 'Nomor Telepon', + isIcon: false, + controller: _phone, + obscure: false, + ), + const SizedBox(height: 10), + _MerchantInput( + hint: 'Password', + isIcon: true, + controller: _password, + obscure: true, + ), + const SizedBox(height: 20), + const Text('Lupa Password?', style: TextStyle(color: MerchantCloneApp.primary, fontSize: 14)), + const SizedBox(height: 10), + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + onPressed: _busy ? null : _signIn, + style: ElevatedButton.styleFrom( + backgroundColor: MerchantCloneApp.primary, + foregroundColor: Colors.white, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + ), + child: _busy + ? const SizedBox( + height: 22, + width: 22, + child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white), + ) + : const Text('Sign In', style: TextStyle(fontSize: 18)), + ), + ), + const SizedBox(height: 16), + const Text( + 'Dengan masuk, Anda menyetujui kebijakan privasi.', + textAlign: TextAlign.center, + style: TextStyle(fontSize: 13), + ), + ], + ), + ), + ], + ), + ), + ), + const Positioned( + top: 15, + left: 15, + child: CircleAvatar( + radius: 20, + backgroundColor: Colors.white, + child: Icon(Icons.arrow_back, color: Colors.black), + ), + ), + Positioned( + left: 0, + right: 0, + bottom: 0, + child: Container( + height: 80, + color: Colors.white, + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8), + child: const Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('Belum punya akun? ', style: TextStyle(fontSize: 14)), + Text('Daftar disini', style: TextStyle(fontSize: 16, color: MerchantCloneApp.primary, fontWeight: FontWeight.w600)), + ], + ), + SizedBox(height: 8), + Text( + 'Note: Jika sudah mendaftar akun baru, tunggu konfirmasi admin untuk info lebih lanjut.', + textAlign: TextAlign.center, + style: TextStyle(fontSize: 12), + ), + ], + ), + ), + ), + ], + ), + ), + ); + } +} + +class _HomePlaceholder extends StatelessWidget { + const _HomePlaceholder({required this.title}); + + final String title; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: Text(title)), + body: const Center(child: Text('Login berhasil')), + ); + } +} + +class _MerchantInput extends StatelessWidget { + const _MerchantInput({ + required this.hint, + required this.isIcon, + required this.controller, + required this.obscure, + }); + + final String hint; + final bool isIcon; + final TextEditingController controller; + final bool obscure; + + @override + Widget build(BuildContext context) { + return Container( + height: 50, + decoration: BoxDecoration(border: Border.all(color: const Color(0xFFC4C4C4)), borderRadius: BorderRadius.circular(8)), + child: Row( + children: [ + SizedBox( + width: 80, + child: Center( + child: isIcon + ? const Icon(Icons.lock_outline, color: MerchantCloneApp.primary) + : const Text('+62', style: TextStyle(color: MerchantCloneApp.primary, fontSize: 18)), + ), + ), + Container(width: 1, margin: const EdgeInsets.symmetric(vertical: 6), color: const Color(0xFFC4C4C4)), + Expanded( + child: TextField( + controller: controller, + obscureText: obscure, + keyboardType: isIcon ? TextInputType.visiblePassword : TextInputType.phone, + decoration: InputDecoration( + hintText: hint, + border: InputBorder.none, + contentPadding: const EdgeInsets.symmetric(horizontal: 10), + ), + ), + ), + ], + ), + ); + } +} diff --git a/flutter_merchant_clone/lib/ontime_auth.dart b/flutter_merchant_clone/lib/ontime_auth.dart new file mode 100644 index 0000000..d0c9275 --- /dev/null +++ b/flutter_merchant_clone/lib/ontime_auth.dart @@ -0,0 +1,98 @@ +import 'dart:convert'; + +import 'package:http/http.dart' as http; + +class LoginResult { + LoginResult({required this.success, required this.message}); + + final bool success; + final String message; +} + +/// Matches native `ServiceGenerator.createService` + login POST (Basic auth + JSON body). +class OntimeAuth { + OntimeAuth._(); + + static const String baseUrl = 'https://apitest.semestaterpadu.my.id/api/'; + + static Future login({ + required String path, + required String password, + String email = '', + required String phoneDigits, + required String dialCodeWithoutPlus, + required String fcmToken, + }) async { + final cleanedDial = dialCodeWithoutPlus.replaceAll('+', '').trim(); + final digitsOnly = phoneDigits.replaceAll(RegExp(r'\D'), ''); + final local = digitsOnly.replaceFirst(RegExp(r'^0+'), ''); + final fullPhone = digitsOnly.startsWith(cleanedDial) + ? digitsOnly + : '$cleanedDial$local'; + final useEmail = email.trim().isNotEmpty; + final basicUser = useEmail ? email.trim() : fullPhone; + + final body = { + 'password': password, + // Send all common aliases expected by backend FCM v1 helper. + 'firebase_token': fcmToken, + 'fcm_token': fcmToken, + 'reg_id': fcmToken, + 'token': fcmToken, + if (useEmail) ...{ + 'email': email.trim(), + 'no_telepon': '', + } else ...{ + 'email': null, + 'no_telepon': fullPhone, + }, + }; + + final uri = Uri.parse('$baseUrl$path'); + final auth = base64Encode(utf8.encode('$basicUser:$password')); + + final resp = await http + .post( + uri, + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'Authorization': 'Basic $auth', + }, + body: jsonEncode(body), + ) + .timeout(const Duration(seconds: 60)); + + Map? json; + try { + if (resp.body.isNotEmpty) { + final decoded = jsonDecode(resp.body); + if (decoded is Map) json = decoded; + } + } catch (_) {} + + final msg = json?['message']?.toString() ?? ''; + final data = json?['data']; + final hasData = data is List && data.isNotEmpty; + final ok = resp.statusCode >= 200 && + resp.statusCode < 300 && + msg.toLowerCase() == 'found' && + hasData; + + if (ok) return LoginResult(success: true, message: msg); + + if (msg.isNotEmpty) { + return LoginResult(success: false, message: msg); + } + if (resp.statusCode == 401) { + return LoginResult(success: false, message: 'no hp atau password salah!'); + } + if (resp.body.isNotEmpty && resp.body.length < 400) { + return LoginResult(success: false, message: resp.body); + } + return LoginResult( + success: false, + message: 'Login gagal (HTTP ${resp.statusCode})', + ); + } +} diff --git a/flutter_merchant_clone/pubspec.lock b/flutter_merchant_clone/pubspec.lock new file mode 100644 index 0000000..11e3d06 --- /dev/null +++ b/flutter_merchant_clone/pubspec.lock @@ -0,0 +1,306 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _flutterfire_internals: + dependency: transitive + description: + name: _flutterfire_internals + sha256: ff0a84a2734d9e1089f8aedd5c0af0061b82fb94e95260d943404e0ef2134b11 + url: "https://pub.dev" + source: hosted + version: "1.3.59" + async: + dependency: transitive + description: + name: async + sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37 + url: "https://pub.dev" + source: hosted + version: "2.13.1" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + characters: + dependency: transitive + description: + name: characters + sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b + url: "https://pub.dev" + source: hosted + version: "1.4.1" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.dev" + source: hosted + version: "1.3.3" + firebase_core: + dependency: "direct main" + description: + name: firebase_core + sha256: "7be63a3f841fc9663342f7f3a011a42aef6a61066943c90b1c434d79d5c995c5" + url: "https://pub.dev" + source: hosted + version: "3.15.2" + firebase_core_platform_interface: + dependency: transitive + description: + name: firebase_core_platform_interface + sha256: "0ecda14c1bfc9ed8cac303dd0f8d04a320811b479362a9a4efb14fd331a473ce" + url: "https://pub.dev" + source: hosted + version: "6.0.3" + firebase_core_web: + dependency: transitive + description: + name: firebase_core_web + sha256: "0ed0dc292e8f9ac50992e2394e9d336a0275b6ae400d64163fdf0a8a8b556c37" + url: "https://pub.dev" + source: hosted + version: "2.24.1" + firebase_messaging: + dependency: "direct main" + description: + name: firebase_messaging + sha256: "60be38574f8b5658e2f22b7e311ff2064bea835c248424a383783464e8e02fcc" + url: "https://pub.dev" + source: hosted + version: "15.2.10" + firebase_messaging_platform_interface: + dependency: transitive + description: + name: firebase_messaging_platform_interface + sha256: "685e1771b3d1f9c8502771ccc9f91485b376ffe16d553533f335b9183ea99754" + url: "https://pub.dev" + source: hosted + version: "4.6.10" + firebase_messaging_web: + dependency: transitive + description: + name: firebase_messaging_web + sha256: "0d1be17bc89ed3ff5001789c92df678b2e963a51b6fa2bdb467532cc9dbed390" + url: "https://pub.dev" + source: hosted + version: "3.10.10" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + http: + dependency: "direct main" + description: + name: http + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" + url: "https://pub.dev" + source: hosted + version: "1.6.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" + url: "https://pub.dev" + source: hosted + version: "11.0.2" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" + url: "https://pub.dev" + source: hosted + version: "3.0.10" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + lints: + dependency: transitive + description: + name: lints + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 + url: "https://pub.dev" + source: hosted + version: "3.0.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 + url: "https://pub.dev" + source: hosted + version: "0.12.19" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" + url: "https://pub.dev" + source: hosted + version: "0.13.0" + meta: + dependency: transitive + description: + name: meta + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + url: "https://pub.dev" + source: hosted + version: "1.17.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + source_span: + dependency: transitive + description: + name: source_span + sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" + url: "https://pub.dev" + source: hosted + version: "1.10.2" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + test_api: + dependency: transitive + description: + name: test_api + sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" + url: "https://pub.dev" + source: hosted + version: "0.7.10" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + url: "https://pub.dev" + source: hosted + version: "2.2.0" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" + url: "https://pub.dev" + source: hosted + version: "15.0.2" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" +sdks: + dart: ">=3.9.0-0 <4.0.0" + flutter: ">=3.22.0" diff --git a/flutter_merchant_clone/pubspec.yaml b/flutter_merchant_clone/pubspec.yaml new file mode 100644 index 0000000..8d1828f --- /dev/null +++ b/flutter_merchant_clone/pubspec.yaml @@ -0,0 +1,22 @@ +name: flutter_merchant_clone +description: OnTime Merchant Flutter conversion +publish_to: "none" +version: 1.0.0+1 + +environment: + sdk: ">=3.3.0 <4.0.0" + +dependencies: + flutter: + sdk: flutter + http: ^1.2.2 + firebase_core: ^3.8.1 + firebase_messaging: ^15.1.5 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^3.0.0 + +flutter: + uses-material-design: true diff --git a/flutter_merchant_clone/test/widget_test.dart b/flutter_merchant_clone/test/widget_test.dart new file mode 100644 index 0000000..a86054d --- /dev/null +++ b/flutter_merchant_clone/test/widget_test.dart @@ -0,0 +1,11 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:flutter_merchant_clone/main.dart'; + +void main() { + testWidgets('app launches', (WidgetTester tester) async { + await tester.pumpWidget(const MerchantCloneApp()); + await tester.pumpAndSettle(); + expect(find.text('Sign In'), findsOneWidget); + }); +} diff --git a/ontime_user_flutter/.gitignore b/flutter_user_clone/.gitignore similarity index 100% rename from ontime_user_flutter/.gitignore rename to flutter_user_clone/.gitignore diff --git a/ontime_user_flutter/.metadata b/flutter_user_clone/.metadata similarity index 100% rename from ontime_user_flutter/.metadata rename to flutter_user_clone/.metadata diff --git a/flutter_user_clone/README.md b/flutter_user_clone/README.md new file mode 100644 index 0000000..3d69b60 --- /dev/null +++ b/flutter_user_clone/README.md @@ -0,0 +1,17 @@ +# flutter_user_clone + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Learn Flutter](https://docs.flutter.dev/get-started/learn-flutter) +- [Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Flutter learning resources](https://docs.flutter.dev/reference/learning-resources) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/ontime_user_flutter/analysis_options.yaml b/flutter_user_clone/analysis_options.yaml similarity index 100% rename from ontime_user_flutter/analysis_options.yaml rename to flutter_user_clone/analysis_options.yaml diff --git a/ontime_user_flutter/android/.gitignore b/flutter_user_clone/android/.gitignore similarity index 100% rename from ontime_user_flutter/android/.gitignore rename to flutter_user_clone/android/.gitignore diff --git a/ontime_merchant_flutter/android/app/build.gradle.kts b/flutter_user_clone/android/app/build.gradle.kts similarity index 91% rename from ontime_merchant_flutter/android/app/build.gradle.kts rename to flutter_user_clone/android/app/build.gradle.kts index d844d7b..ae4b3d6 100644 --- a/ontime_merchant_flutter/android/app/build.gradle.kts +++ b/flutter_user_clone/android/app/build.gradle.kts @@ -3,10 +3,11 @@ plugins { id("kotlin-android") // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. id("dev.flutter.flutter-gradle-plugin") + id("com.google.gms.google-services") } android { - namespace = "com.example.ontime_merchant_flutter" + namespace = "id.ontime.customer" compileSdk = flutter.compileSdkVersion ndkVersion = flutter.ndkVersion @@ -21,7 +22,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId = "com.example.ontime_merchant_flutter" + applicationId = "id.ontime.customer" // You can update the following values to match your application needs. // For more information, see: https://flutter.dev/to/review-gradle-config. minSdk = flutter.minSdkVersion diff --git a/ontime_user_flutter/android/app/src/debug/AndroidManifest.xml b/flutter_user_clone/android/app/src/debug/AndroidManifest.xml similarity index 100% rename from ontime_user_flutter/android/app/src/debug/AndroidManifest.xml rename to flutter_user_clone/android/app/src/debug/AndroidManifest.xml diff --git a/ontime_driver_flutter/android/app/src/main/AndroidManifest.xml b/flutter_user_clone/android/app/src/main/AndroidManifest.xml similarity index 92% rename from ontime_driver_flutter/android/app/src/main/AndroidManifest.xml rename to flutter_user_clone/android/app/src/main/AndroidManifest.xml index 8b9503d..f23b437 100644 --- a/ontime_driver_flutter/android/app/src/main/AndroidManifest.xml +++ b/flutter_user_clone/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,8 @@ + + main() async { + WidgetsFlutterBinding.ensureInitialized(); + await Firebase.initializeApp(); + runApp(const UserCloneApp()); +} + +class UserCloneApp extends StatelessWidget { + const UserCloneApp({super.key}); + + static const Color primary = Color(0xFF5BC8DA); + + @override + Widget build(BuildContext context) { + return MaterialApp( + debugShowCheckedModeBanner: false, + title: 'OnTime User Clone', + theme: ThemeData( + colorScheme: ColorScheme.fromSeed(seedColor: primary), + scaffoldBackgroundColor: Colors.white, + useMaterial3: true, + ), + home: const UserLoginPage(), + ); + } +} + +class UserLoginPage extends StatefulWidget { + const UserLoginPage({super.key}); + + @override + State createState() => _UserLoginPageState(); +} + +class _UserLoginPageState extends State { + final _phone = TextEditingController(); + final _password = TextEditingController(); + bool _showPassword = false; + bool _busy = false; + + @override + void dispose() { + _phone.dispose(); + _password.dispose(); + super.dispose(); + } + + Future _signIn() async { + final pass = _password.text; + final digits = _phone.text.trim(); + if (pass.isEmpty) { + _toast('Isi password'); + return; + } + if (digits.isEmpty) { + _toast('Isi nomor telepon'); + return; + } + + setState(() => _busy = true); + try { + await FirebaseMessaging.instance.requestPermission(); + final token = await FirebaseMessaging.instance.getToken(); + if (token == null || token.length < 50) { + _toast( + 'Token FCM belum siap. Izinkan notifikasi & pastikan google-services.json / API key Firebase valid.', + ); + return; + } + + final result = await OntimeAuth.login( + path: _kLoginPath, + password: pass, + phoneDigits: digits, + dialCodeWithoutPlus: _kDial, + fcmToken: token, + ); + + if (!mounted) return; + if (result.success) { + Navigator.of(context).pushReplacement( + MaterialPageRoute( + builder: (_) => const _HomePlaceholder(title: 'OnTime Customer'), + ), + ); + } else { + _toast(result.message); + } + } catch (e) { + if (mounted) _toast('$e'); + } finally { + if (mounted) setState(() => _busy = false); + } + } + + void _toast(String msg) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(msg))); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: SafeArea( + child: Stack( + children: [ + SingleChildScrollView( + child: Column( + children: [ + Container( + height: 290, + width: double.infinity, + color: const Color(0xFFE2F2FE), + child: const Center( + child: Icon(Icons.shopping_bag_outlined, size: 120, color: UserCloneApp.primary), + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(15, 0, 15, 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 12), + const Padding( + padding: EdgeInsets.only(left: 15), + child: Text('Log in dulu yuk!', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)), + ), + const Padding( + padding: EdgeInsets.fromLTRB(15, 0, 15, 10), + child: Text( + 'Nikmati belanja kebutuhan, makanan, dan service lainnya, langsung dari hapemu', + style: TextStyle(fontSize: 11), + ), + ), + const Padding( + padding: EdgeInsets.only(left: 15, top: 8), + child: Text('Nomor Telepon', style: TextStyle(fontSize: 11)), + ), + const SizedBox(height: 10), + _PhoneBox(controller: _phone), + const Padding( + padding: EdgeInsets.only(left: 15, top: 8), + child: Text('Password', style: TextStyle(fontSize: 11)), + ), + const SizedBox(height: 8), + Container( + height: 48, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + border: Border.all(color: const Color(0xFFC4C4C4)), + ), + child: TextField( + controller: _password, + obscureText: !_showPassword, + decoration: InputDecoration( + hintText: 'Password', + border: InputBorder.none, + contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + suffixIcon: IconButton( + icon: Icon(_showPassword ? Icons.visibility_off : Icons.visibility), + onPressed: () => setState(() => _showPassword = !_showPassword), + ), + ), + ), + ), + const SizedBox(height: 20), + const Text('Lupa Password?', style: TextStyle(color: UserCloneApp.primary, fontSize: 14)), + const SizedBox(height: 12), + SizedBox( + width: double.infinity, + child: ElevatedButton( + onPressed: _busy ? null : _signIn, + style: ElevatedButton.styleFrom( + backgroundColor: UserCloneApp.primary, + foregroundColor: Colors.white, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + ), + child: _busy + ? const SizedBox( + height: 22, + width: 22, + child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white), + ) + : const Text('Sign In'), + ), + ), + const SizedBox(height: 10), + OutlinedButton( + onPressed: () {}, + style: OutlinedButton.styleFrom( + minimumSize: const Size(double.infinity, 44), + foregroundColor: Colors.black, + side: const BorderSide(color: Colors.black26), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + ), + child: const Text('Belum punya akun? Daftar dulu yuk'), + ), + const SizedBox(height: 12), + const Center( + child: Text( + 'Dengan masuk, Anda menyetujui kebijakan privasi.', + textAlign: TextAlign.center, + style: TextStyle(fontSize: 11), + ), + ), + ], + ), + ), + ], + ), + ), + const Positioned( + top: 15, + left: 15, + child: SizedBox(width: 40, height: 40, child: Icon(Icons.arrow_back)), + ), + ], + ), + ), + ); + } +} + +class _PhoneBox extends StatelessWidget { + const _PhoneBox({required this.controller}); + + final TextEditingController controller; + + @override + Widget build(BuildContext context) { + return Container( + height: 48, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + border: Border.all(color: const Color(0xFFC4C4C4)), + ), + child: Row( + children: [ + const SizedBox(width: 80, child: Center(child: Text('+62', style: TextStyle(color: UserCloneApp.primary)))), + Container(width: 1, margin: const EdgeInsets.symmetric(vertical: 6), color: const Color(0xFFC4C4C4)), + Expanded( + child: TextField( + controller: controller, + keyboardType: TextInputType.phone, + decoration: const InputDecoration( + hintText: 'Nomor Telepon', + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(horizontal: 10), + ), + ), + ), + ], + ), + ); + } +} + +class _HomePlaceholder extends StatelessWidget { + const _HomePlaceholder({required this.title}); + + final String title; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: Text(title)), + body: const Center(child: Text('Login berhasil')), + ); + } +} diff --git a/flutter_user_clone/lib/ontime_auth.dart b/flutter_user_clone/lib/ontime_auth.dart new file mode 100644 index 0000000..09e5abf --- /dev/null +++ b/flutter_user_clone/lib/ontime_auth.dart @@ -0,0 +1,94 @@ +import 'dart:convert'; + +import 'package:http/http.dart' as http; + +class LoginResult { + LoginResult({required this.success, required this.message}); + + final bool success; + final String message; +} + +/// Matches native `ServiceGenerator.createService` + login POST (Basic auth + JSON body). +class OntimeAuth { + OntimeAuth._(); + + static const String baseUrl = 'https://apitest.semestaterpadu.my.id/api/'; + + static Future login({ + required String path, + required String password, + String email = '', + required String phoneDigits, + required String dialCodeWithoutPlus, + required String fcmToken, + }) async { + final cleanedDial = dialCodeWithoutPlus.replaceAll('+', '').trim(); + final digitsOnly = phoneDigits.replaceAll(RegExp(r'\D'), ''); + final local = digitsOnly.replaceFirst(RegExp(r'^0+'), ''); + final fullPhone = digitsOnly.startsWith(cleanedDial) + ? digitsOnly + : '$cleanedDial$local'; + final useEmail = email.trim().isNotEmpty; + final basicUser = useEmail ? email.trim() : fullPhone; + + final body = { + 'password': password, + 'token': fcmToken, + if (useEmail) ...{ + 'email': email.trim(), + 'no_telepon': '', + } else ...{ + 'email': null, + 'no_telepon': fullPhone, + }, + }; + + final uri = Uri.parse('$baseUrl$path'); + final auth = base64Encode(utf8.encode('$basicUser:$password')); + + final resp = await http + .post( + uri, + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'Authorization': 'Basic $auth', + }, + body: jsonEncode(body), + ) + .timeout(const Duration(seconds: 60)); + + Map? json; + try { + if (resp.body.isNotEmpty) { + final decoded = jsonDecode(resp.body); + if (decoded is Map) json = decoded; + } + } catch (_) {} + + final msg = json?['message']?.toString() ?? ''; + final data = json?['data']; + final hasData = data is List && data.isNotEmpty; + final ok = resp.statusCode >= 200 && + resp.statusCode < 300 && + msg.toLowerCase() == 'found' && + hasData; + + if (ok) return LoginResult(success: true, message: msg); + + if (msg.isNotEmpty) { + return LoginResult(success: false, message: msg); + } + if (resp.statusCode == 401) { + return LoginResult(success: false, message: 'no hp atau password salah!'); + } + if (resp.body.isNotEmpty && resp.body.length < 400) { + return LoginResult(success: false, message: resp.body); + } + return LoginResult( + success: false, + message: 'Login gagal (HTTP ${resp.statusCode})', + ); + } +} diff --git a/flutter_user_clone/pubspec.lock b/flutter_user_clone/pubspec.lock new file mode 100644 index 0000000..11e3d06 --- /dev/null +++ b/flutter_user_clone/pubspec.lock @@ -0,0 +1,306 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _flutterfire_internals: + dependency: transitive + description: + name: _flutterfire_internals + sha256: ff0a84a2734d9e1089f8aedd5c0af0061b82fb94e95260d943404e0ef2134b11 + url: "https://pub.dev" + source: hosted + version: "1.3.59" + async: + dependency: transitive + description: + name: async + sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37 + url: "https://pub.dev" + source: hosted + version: "2.13.1" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + characters: + dependency: transitive + description: + name: characters + sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b + url: "https://pub.dev" + source: hosted + version: "1.4.1" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.dev" + source: hosted + version: "1.3.3" + firebase_core: + dependency: "direct main" + description: + name: firebase_core + sha256: "7be63a3f841fc9663342f7f3a011a42aef6a61066943c90b1c434d79d5c995c5" + url: "https://pub.dev" + source: hosted + version: "3.15.2" + firebase_core_platform_interface: + dependency: transitive + description: + name: firebase_core_platform_interface + sha256: "0ecda14c1bfc9ed8cac303dd0f8d04a320811b479362a9a4efb14fd331a473ce" + url: "https://pub.dev" + source: hosted + version: "6.0.3" + firebase_core_web: + dependency: transitive + description: + name: firebase_core_web + sha256: "0ed0dc292e8f9ac50992e2394e9d336a0275b6ae400d64163fdf0a8a8b556c37" + url: "https://pub.dev" + source: hosted + version: "2.24.1" + firebase_messaging: + dependency: "direct main" + description: + name: firebase_messaging + sha256: "60be38574f8b5658e2f22b7e311ff2064bea835c248424a383783464e8e02fcc" + url: "https://pub.dev" + source: hosted + version: "15.2.10" + firebase_messaging_platform_interface: + dependency: transitive + description: + name: firebase_messaging_platform_interface + sha256: "685e1771b3d1f9c8502771ccc9f91485b376ffe16d553533f335b9183ea99754" + url: "https://pub.dev" + source: hosted + version: "4.6.10" + firebase_messaging_web: + dependency: transitive + description: + name: firebase_messaging_web + sha256: "0d1be17bc89ed3ff5001789c92df678b2e963a51b6fa2bdb467532cc9dbed390" + url: "https://pub.dev" + source: hosted + version: "3.10.10" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + http: + dependency: "direct main" + description: + name: http + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" + url: "https://pub.dev" + source: hosted + version: "1.6.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" + url: "https://pub.dev" + source: hosted + version: "11.0.2" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" + url: "https://pub.dev" + source: hosted + version: "3.0.10" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + lints: + dependency: transitive + description: + name: lints + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 + url: "https://pub.dev" + source: hosted + version: "3.0.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 + url: "https://pub.dev" + source: hosted + version: "0.12.19" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" + url: "https://pub.dev" + source: hosted + version: "0.13.0" + meta: + dependency: transitive + description: + name: meta + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + url: "https://pub.dev" + source: hosted + version: "1.17.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + source_span: + dependency: transitive + description: + name: source_span + sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" + url: "https://pub.dev" + source: hosted + version: "1.10.2" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + test_api: + dependency: transitive + description: + name: test_api + sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" + url: "https://pub.dev" + source: hosted + version: "0.7.10" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + url: "https://pub.dev" + source: hosted + version: "2.2.0" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" + url: "https://pub.dev" + source: hosted + version: "15.0.2" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" +sdks: + dart: ">=3.9.0-0 <4.0.0" + flutter: ">=3.22.0" diff --git a/flutter_user_clone/pubspec.yaml b/flutter_user_clone/pubspec.yaml new file mode 100644 index 0000000..2c4879c --- /dev/null +++ b/flutter_user_clone/pubspec.yaml @@ -0,0 +1,22 @@ +name: flutter_user_clone +description: OnTime User Flutter conversion +publish_to: "none" +version: 1.0.0+1 + +environment: + sdk: ">=3.3.0 <4.0.0" + +dependencies: + flutter: + sdk: flutter + http: ^1.2.2 + firebase_core: ^3.8.1 + firebase_messaging: ^15.1.5 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^3.0.0 + +flutter: + uses-material-design: true diff --git a/flutter_user_clone/test/widget_test.dart b/flutter_user_clone/test/widget_test.dart new file mode 100644 index 0000000..b1c096f --- /dev/null +++ b/flutter_user_clone/test/widget_test.dart @@ -0,0 +1,11 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:flutter_user_clone/main.dart'; + +void main() { + testWidgets('app launches', (WidgetTester tester) async { + await tester.pumpWidget(const UserCloneApp()); + await tester.pumpAndSettle(); + expect(find.text('Log in dulu yuk!'), findsOneWidget); + }); +} diff --git a/ontime_driver_flutter/README.md b/ontime_driver_flutter/README.md deleted file mode 100644 index f40610a..0000000 --- a/ontime_driver_flutter/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# Ontime Driver (Flutter) - -Flutter app for Ontime **driver**, using backend API at `https://apitest.semestaterpadu.my.id/api/`. - -## Setup - -- Flutter SDK (e.g. 3.19+) -- Android: run `flutter create . --platforms=android` if needed. - -## Run / Build - -```bash -flutter pub get -flutter run -# or -flutter build apk --release -``` - -## Firebase (FCM) - -Add `google-services.json` to `android/app/` for push. FCM token is sent as `reg_id` on login when available. - -## Validation - -- **Login**: `Driver/login` (no_telepon or email + password, optional reg_id). -- **Home / sync**: `Driver/home`, `Driver/syncronizing_account`. -- **Job flow**: `Driver/accept`, `Driver/start`, `Driver/finish`. -- **Location**: `Driver/update_location`. -- Compare with `OnTime_Driver_live` and backendpanel API responses. diff --git a/ontime_driver_flutter/assets/images/.gitkeep b/ontime_driver_flutter/assets/images/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/ontime_driver_flutter/lib/core/app_config.dart b/ontime_driver_flutter/lib/core/app_config.dart deleted file mode 100644 index 7f2ae5e..0000000 --- a/ontime_driver_flutter/lib/core/app_config.dart +++ /dev/null @@ -1,10 +0,0 @@ -class AppConfig { - static const String baseUrl = 'https://apitest.semestaterpadu.my.id/'; - static const String apiBase = '${baseUrl}api/'; - - static const String imagesFitur = '${baseUrl}images/fitur/'; - static const String imagesBank = '${baseUrl}images/bank/'; - static const String imagesDriver = '${baseUrl}images/fotodriver/'; - static const String imagesUser = '${baseUrl}images/pelanggan/'; - static const String imagesMerchant = '${baseUrl}images/merchant/'; -} diff --git a/ontime_driver_flutter/lib/core/app_router.dart b/ontime_driver_flutter/lib/core/app_router.dart deleted file mode 100644 index db94ce1..0000000 --- a/ontime_driver_flutter/lib/core/app_router.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; -import 'package:ontime_driver_flutter/features/auth/presentation/login_screen.dart'; -import 'package:ontime_driver_flutter/features/home/presentation/home_shell.dart'; - -final _rootNavKey = GlobalKey(debugLabel: 'root'); - -GoRouter createAppRouter() { - return GoRouter( - navigatorKey: _rootNavKey, - initialLocation: '/auth/login', - routes: [ - GoRoute( - path: '/auth/login', - name: 'login', - pageBuilder: (_, __) => const MaterialPage(child: LoginScreen()), - ), - GoRoute( - path: '/home', - name: 'home', - pageBuilder: (_, __) => const MaterialPage(child: HomeShell()), - ), - ], - ); -} diff --git a/ontime_driver_flutter/lib/core/app_theme.dart b/ontime_driver_flutter/lib/core/app_theme.dart deleted file mode 100644 index 5c79913..0000000 --- a/ontime_driver_flutter/lib/core/app_theme.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:flutter/material.dart'; - -class AppTheme { - static ThemeData light() { - final base = ThemeData.light(useMaterial3: true); - return base.copyWith( - colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF00AEEF)), - scaffoldBackgroundColor: Colors.grey[50], - appBarTheme: const AppBarTheme(elevation: 0, centerTitle: true), - inputDecorationTheme: const InputDecorationTheme(border: OutlineInputBorder()), - ); - } -} diff --git a/ontime_driver_flutter/lib/core/fcm_service.dart b/ontime_driver_flutter/lib/core/fcm_service.dart deleted file mode 100644 index 9a6af85..0000000 --- a/ontime_driver_flutter/lib/core/fcm_service.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:firebase_core/firebase_core.dart'; -import 'package:firebase_messaging/firebase_messaging.dart'; - -/// FCM token for backend (Driver uses `reg_id` or `token`). -class FcmService { - static bool _initialized = false; - - static Future init() async { - if (_initialized) return; - try { - await Firebase.initializeApp(); - _initialized = true; - } catch (_) {} - } - - static Future getToken() async { - if (!_initialized) return null; - try { - return await FirebaseMessaging.instance.getToken(); - } catch (_) { - return null; - } - } -} diff --git a/ontime_driver_flutter/lib/data/api/api_client.dart b/ontime_driver_flutter/lib/data/api/api_client.dart deleted file mode 100644 index 33ae34b..0000000 --- a/ontime_driver_flutter/lib/data/api/api_client.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:dio/dio.dart'; -import 'package:ontime_driver_flutter/core/app_config.dart'; - -class ApiClient { - ApiClient({ - required String? basicAuthUser, - required String? basicAuthPassword, - }) : _dio = Dio( - BaseOptions( - baseUrl: AppConfig.apiBase, - connectTimeout: const Duration(seconds: 15), - receiveTimeout: const Duration(seconds: 20), - ), - ) { - if (basicAuthUser != null && basicAuthPassword != null) { - final auth = '$basicAuthUser:$basicAuthPassword'; - final encoded = String.fromCharCodes(auth.codeUnits); - _dio.options.headers['Authorization'] = 'Basic $encoded'; - } - } - - final Dio _dio; - Dio get raw => _dio; -} diff --git a/ontime_driver_flutter/lib/features/auth/application/auth_controller.dart b/ontime_driver_flutter/lib/features/auth/application/auth_controller.dart deleted file mode 100644 index 497e09c..0000000 --- a/ontime_driver_flutter/lib/features/auth/application/auth_controller.dart +++ /dev/null @@ -1,98 +0,0 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:ontime_driver_flutter/data/api/api_client.dart'; -import 'package:ontime_driver_flutter/features/auth/data/driver_auth_api.dart'; -import 'package:ontime_driver_flutter/features/auth/data/models/driver_model.dart'; -import 'package:shared_preferences/shared_preferences.dart'; - -class AuthState { - const AuthState({ - this.driver, - this.isLoading = false, - this.errorMessage, - }); - - final DriverModel? driver; - final bool isLoading; - final String? errorMessage; - - AuthState copyWith({ - DriverModel? driver, - bool? isLoading, - String? errorMessage, - }) { - return AuthState( - driver: driver ?? this.driver, - isLoading: isLoading ?? this.isLoading, - errorMessage: errorMessage, - ); - } -} - -final authControllerProvider = - StateNotifierProvider((ref) => AuthController()); - -class AuthController extends StateNotifier { - AuthController() : super(const AuthState()); - - late final DriverAuthApi _api = - DriverAuthApi(ApiClient(basicAuthUser: null, basicAuthPassword: null)); - - Future login({ - String? phone, - String? email, - required String password, - String? fcmToken, - }) async { - state = state.copyWith(isLoading: true, errorMessage: null); - try { - final driver = await _api.login( - phone: phone, - email: email, - password: password, - fcmToken: fcmToken, - ); - state = state.copyWith(driver: driver, isLoading: false); - final prefs = await SharedPreferences.getInstance(); - await prefs.setString('driver_id', driver.id); - await prefs.setString('driver_reg_id', driver.regId); - await prefs.setString('driver_phone', driver.noTelepon); - await prefs.setString('driver_email', driver.email); - } catch (e) { - state = state.copyWith( - isLoading: false, - errorMessage: e.toString().replaceFirst('Exception: ', ''), - ); - } - } - - Future logout() async { - final driver = state.driver; - if (driver != null) { - try { - await _api.logout(driver.id); - } catch (_) {} - } - state = const AuthState(); - final prefs = await SharedPreferences.getInstance(); - await prefs.remove('driver_id'); - await prefs.remove('driver_reg_id'); - await prefs.remove('driver_phone'); - await prefs.remove('driver_email'); - } - - static Future getStoredDriver() async { - final prefs = await SharedPreferences.getInstance(); - final id = prefs.getString('driver_id'); - final phone = prefs.getString('driver_phone'); - final email = prefs.getString('driver_email'); - final regId = prefs.getString('driver_reg_id') ?? ''; - if (id == null || (phone == null && email == null)) return null; - return DriverModel( - id: id, - namaDriver: '', - noTelepon: phone ?? '', - email: email ?? '', - regId: regId, - ); - } -} diff --git a/ontime_driver_flutter/lib/features/auth/data/driver_auth_api.dart b/ontime_driver_flutter/lib/features/auth/data/driver_auth_api.dart deleted file mode 100644 index 059c648..0000000 --- a/ontime_driver_flutter/lib/features/auth/data/driver_auth_api.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:dio/dio.dart'; -import 'package:ontime_driver_flutter/data/api/api_client.dart'; -import 'package:ontime_driver_flutter/features/auth/data/models/driver_model.dart'; - -class DriverAuthApi { - DriverAuthApi(this._client); - final ApiClient _client; - - Future login({ - String? phone, - String? email, - required String password, - String? fcmToken, - }) async { - final payload = {'password': password}; - if (phone != null && phone.isNotEmpty) payload['no_telepon'] = phone; - if (email != null && email.isNotEmpty) payload['email'] = email; - if (fcmToken != null && fcmToken.isNotEmpty) payload['reg_id'] = fcmToken; - - final Response response = - await _client.raw.post('Driver/login', data: payload); - final data = response.data is Map - ? response.data as Map - : {}; - - if (data['message'] == 'banned') throw Exception('banned'); - if (data['code']?.toString() != '200') { - throw Exception(data['message'] ?? 'Login failed'); - } - - final list = data['data'] as List? ?? []; - if (list.isEmpty) throw Exception('Driver data not found'); - return DriverModel.fromJson(list.first as Map); - } - - Future logout(String driverId) async { - await _client.raw.post('Driver/logout', data: {'id': driverId}); - } -} diff --git a/ontime_driver_flutter/lib/features/auth/data/models/driver_model.dart b/ontime_driver_flutter/lib/features/auth/data/models/driver_model.dart deleted file mode 100644 index e2850f1..0000000 --- a/ontime_driver_flutter/lib/features/auth/data/models/driver_model.dart +++ /dev/null @@ -1,41 +0,0 @@ -import 'package:json_annotation/json_annotation.dart'; - -part 'driver_model.g.dart'; - -@JsonSerializable() -class DriverModel { - DriverModel({ - required this.id, - required this.namaDriver, - required this.noTelepon, - required this.email, - required this.regId, - this.foto, - this.status, - }); - - @JsonKey(name: 'id') - final String id; - - @JsonKey(name: 'nama_driver') - final String namaDriver; - - @JsonKey(name: 'no_telepon') - final String noTelepon; - - @JsonKey(name: 'email') - final String email; - - @JsonKey(name: 'reg_id') - final String regId; - - @JsonKey(name: 'foto') - final String? foto; - - @JsonKey(name: 'status') - final int? status; - - factory DriverModel.fromJson(Map json) => - _$DriverModelFromJson(json); - Map toJson() => _$DriverModelToJson(this); -} diff --git a/ontime_driver_flutter/lib/features/auth/data/models/driver_model.g.dart b/ontime_driver_flutter/lib/features/auth/data/models/driver_model.g.dart deleted file mode 100644 index 9ce867b..0000000 --- a/ontime_driver_flutter/lib/features/auth/data/models/driver_model.g.dart +++ /dev/null @@ -1,24 +0,0 @@ -part of 'driver_model.dart'; - -DriverModel _$DriverModelFromJson(Map json) { - return DriverModel( - id: json['id'] as String? ?? '', - namaDriver: json['nama_driver'] as String? ?? '', - noTelepon: json['no_telepon'] as String? ?? '', - email: json['email'] as String? ?? '', - regId: json['reg_id'] as String? ?? '', - foto: json['foto'] as String?, - status: json['status'] as int?, - ); -} - -Map _$DriverModelToJson(DriverModel instance) => - { - 'id': instance.id, - 'nama_driver': instance.namaDriver, - 'no_telepon': instance.noTelepon, - 'email': instance.email, - 'reg_id': instance.regId, - 'foto': instance.foto, - 'status': instance.status, - }; diff --git a/ontime_driver_flutter/lib/features/auth/presentation/login_screen.dart b/ontime_driver_flutter/lib/features/auth/presentation/login_screen.dart deleted file mode 100644 index 778cdc2..0000000 --- a/ontime_driver_flutter/lib/features/auth/presentation/login_screen.dart +++ /dev/null @@ -1,97 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:go_router/go_router.dart'; -import 'package:ontime_driver_flutter/core/fcm_service.dart'; -import 'package:ontime_driver_flutter/features/auth/application/auth_controller.dart'; - -class LoginScreen extends ConsumerStatefulWidget { - const LoginScreen({super.key}); - - @override - ConsumerState createState() => _LoginScreenState(); -} - -class _LoginScreenState extends ConsumerState { - final _formKey = GlobalKey(); - final _emailController = TextEditingController(); - final _phoneController = TextEditingController(); - final _passwordController = TextEditingController(); - - @override - void dispose() { - _emailController.dispose(); - _phoneController.dispose(); - _passwordController.dispose(); - super.dispose(); - } - - Future _submit() async { - if (!_formKey.currentState!.validate()) return; - final fcmToken = await FcmService.getToken(); - final auth = ref.read(authControllerProvider.notifier); - await auth.login( - email: _emailController.text.trim().isEmpty ? null : _emailController.text.trim(), - phone: _phoneController.text.trim().isEmpty ? null : _phoneController.text.trim(), - password: _passwordController.text, - fcmToken: fcmToken, - ); - final state = ref.read(authControllerProvider); - if (state.driver != null && mounted) context.go('/home'); - } - - @override - Widget build(BuildContext context) { - final state = ref.watch(authControllerProvider); - return Scaffold( - appBar: AppBar(title: const Text('Masuk Driver')), - body: SafeArea( - child: Padding( - padding: const EdgeInsets.all(16), - child: Form( - key: _formKey, - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - TextFormField( - controller: _emailController, - decoration: const InputDecoration(labelText: 'Email'), - ), - const SizedBox(height: 12), - TextFormField( - controller: _phoneController, - decoration: const InputDecoration(labelText: 'No. Telepon'), - keyboardType: TextInputType.phone, - ), - const SizedBox(height: 12), - TextFormField( - controller: _passwordController, - decoration: const InputDecoration(labelText: 'Kata sandi'), - obscureText: true, - validator: (v) => (v == null || v.isEmpty) ? 'Password wajib diisi' : null, - ), - if (state.errorMessage != null) ...[ - const SizedBox(height: 8), - Text( - state.errorMessage!, - style: TextStyle(color: Theme.of(context).colorScheme.error), - ), - ], - const Spacer(), - ElevatedButton( - onPressed: state.isLoading ? null : _submit, - child: state.isLoading - ? const SizedBox( - height: 20, - width: 20, - child: CircularProgressIndicator(strokeWidth: 2), - ) - : const Text('Masuk'), - ), - ], - ), - ), - ), - ), - ); - } -} diff --git a/ontime_driver_flutter/lib/features/home/data/driver_home_api.dart b/ontime_driver_flutter/lib/features/home/data/driver_home_api.dart deleted file mode 100644 index c458980..0000000 --- a/ontime_driver_flutter/lib/features/home/data/driver_home_api.dart +++ /dev/null @@ -1,109 +0,0 @@ -import 'package:dio/dio.dart'; -import 'package:ontime_driver_flutter/data/api/api_client.dart'; - -class DriverHomeApi { - DriverHomeApi(this._client); - final ApiClient _client; - - Future> home({ - required String id, - required String noTelepon, - }) async { - final Response response = await _client.raw.post( - 'Driver/home', - data: {'id': id, 'no_telepon': noTelepon}, - ); - final data = response.data is Map - ? response.data as Map - : {}; - if (data['code']?.toString() != '200') { - throw Exception(data['message'] ?? 'Failed'); - } - return data; - } - - Future> syncronizingAccount({ - required String id, - required String noTelepon, - }) async { - final Response response = await _client.raw.post( - 'Driver/syncronizing_account', - data: {'id': id, 'no_telepon': noTelepon}, - ); - final data = response.data is Map - ? response.data as Map - : {}; - if (data['message'] != 'success') throw Exception(data['message'] ?? 'Failed'); - return data; - } - - Future updateLocation({ - required String idDriver, - required double latitude, - required double longitude, - double bearing = 0, - String? regId, - }) async { - final payload = { - 'id_driver': idDriver, - 'latitude': latitude, - 'longitude': longitude, - 'bearing': bearing, - }; - if (regId != null && regId.isNotEmpty) payload['reg_id'] = regId; - await _client.raw.post('Driver/update_location', data: payload); - } - - Future turningOn({required String id, required bool isTurn}) async { - await _client.raw.post( - 'Driver/turning_on', - data: {'id': id, 'is_turn': isTurn}, - ); - } - - Future accept({required String id, required String idTransaksi}) async { - final Response response = await _client.raw.post( - 'Driver/accept', - data: {'id': id, 'id_transaksi': idTransaksi}, - ); - final data = response.data is Map - ? response.data as Map - : {}; - return data['message']?.toString() ?? 'unknown'; - } - - Future start({required String id, required String idTransaksi}) async { - final Response response = await _client.raw.post( - 'Driver/start', - data: {'id': id, 'id_transaksi': idTransaksi}, - ); - final data = response.data is Map - ? response.data as Map - : {}; - return data['message']?.toString() ?? 'unknown'; - } - - Future finish({required String id, required String idTransaksi}) async { - final Response response = await _client.raw.post( - 'Driver/finish', - data: {'id': id, 'id_transaksi': idTransaksi}, - ); - final data = response.data is Map - ? response.data as Map - : {}; - return data['message']?.toString() ?? 'unknown'; - } - - Future> historyProgress(String driverId) async { - final Response response = await _client.raw.post( - 'Driver/history_progress', - data: {'id': driverId}, - ); - final data = response.data is Map - ? response.data as Map - : {}; - if (data['status'] != true) throw Exception('Failed'); - final list = data['data'] as List? ?? []; - return list; - } -} diff --git a/ontime_driver_flutter/lib/features/home/presentation/home_shell.dart b/ontime_driver_flutter/lib/features/home/presentation/home_shell.dart deleted file mode 100644 index b8fda64..0000000 --- a/ontime_driver_flutter/lib/features/home/presentation/home_shell.dart +++ /dev/null @@ -1,86 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:go_router/go_router.dart'; -import 'package:ontime_driver_flutter/features/auth/application/auth_controller.dart'; - -class HomeShell extends ConsumerStatefulWidget { - const HomeShell({super.key}); - - @override - ConsumerState createState() => _HomeShellState(); -} - -class _HomeShellState extends ConsumerState { - int _index = 0; - - @override - Widget build(BuildContext context) { - final driver = ref.watch(authControllerProvider).driver; - final pages = [ - _HomeTab(driverId: driver?.id ?? '', noTelepon: driver?.noTelepon ?? ''), - const Center(child: Text('Pesanan / Riwayat')), - const Center(child: Text('Dompet')), - _ProfileTab(), - ]; - return Scaffold( - body: SafeArea(child: pages[_index]), - bottomNavigationBar: NavigationBar( - selectedIndex: _index, - onDestinationSelected: (i) => setState(() => _index = i), - destinations: const [ - NavigationDestination(icon: Icon(Icons.home_outlined), selectedIcon: Icon(Icons.home), label: 'Beranda'), - NavigationDestination(icon: Icon(Icons.receipt_long_outlined), selectedIcon: Icon(Icons.receipt_long), label: 'Pesanan'), - NavigationDestination(icon: Icon(Icons.account_balance_wallet_outlined), selectedIcon: Icon(Icons.account_balance_wallet), label: 'Dompet'), - NavigationDestination(icon: Icon(Icons.person_outline), selectedIcon: Icon(Icons.person), label: 'Profil'), - ], - ), - ); - } -} - -class _HomeTab extends StatelessWidget { - const _HomeTab({required this.driverId, required this.noTelepon}); - - final String driverId; - final String noTelepon; - - @override - Widget build(BuildContext context) { - return SingleChildScrollView( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - const Text('Beranda Driver', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), - const SizedBox(height: 8), - const Text('Saldo & status online/offline (toggle) – sync job dari server.'), - ], - ), - ); - } -} - -class _ProfileTab extends ConsumerWidget { - @override - Widget build(BuildContext context, WidgetRef ref) { - final driver = ref.watch(authControllerProvider).driver; - return Padding( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - if (driver != null) - Text('${driver.namaDriver}\n${driver.noTelepon}', style: const TextStyle(fontSize: 16)), - const SizedBox(height: 24), - ElevatedButton( - onPressed: () async { - await ref.read(authControllerProvider.notifier).logout(); - if (context.mounted) context.go('/auth/login'); - }, - child: const Text('Keluar'), - ), - ], - ), - ); - } -} diff --git a/ontime_driver_flutter/lib/main.dart b/ontime_driver_flutter/lib/main.dart deleted file mode 100644 index 6f8504a..0000000 --- a/ontime_driver_flutter/lib/main.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import 'core/app_router.dart'; -import 'core/app_theme.dart'; -import 'core/fcm_service.dart'; - -void main() async { - WidgetsFlutterBinding.ensureInitialized(); - await FcmService.init(); - runApp( - ProviderScope( - child: MaterialApp.router( - debugShowCheckedModeBanner: false, - title: 'Ontime Driver', - theme: AppTheme.light(), - routerConfig: createAppRouter(), - ), - ), - ); -} diff --git a/ontime_driver_flutter/pubspec.lock b/ontime_driver_flutter/pubspec.lock deleted file mode 100644 index 40dd2bb..0000000 --- a/ontime_driver_flutter/pubspec.lock +++ /dev/null @@ -1,1106 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - _fe_analyzer_shared: - dependency: transitive - description: - name: _fe_analyzer_shared - sha256: "8d7ff3948166b8ec5da0fbb5962000926b8e02f2ed9b3e51d1738905fbd4c98d" - url: "https://pub.dev" - source: hosted - version: "93.0.0" - _flutterfire_internals: - dependency: transitive - description: - name: _flutterfire_internals - sha256: ff0a84a2734d9e1089f8aedd5c0af0061b82fb94e95260d943404e0ef2134b11 - url: "https://pub.dev" - source: hosted - version: "1.3.59" - analyzer: - dependency: transitive - description: - name: analyzer - sha256: de7148ed2fcec579b19f122c1800933dfa028f6d9fd38a152b04b1516cec120b - url: "https://pub.dev" - source: hosted - version: "10.0.1" - args: - dependency: transitive - description: - name: args - sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 - url: "https://pub.dev" - source: hosted - version: "2.7.0" - async: - dependency: transitive - description: - name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" - url: "https://pub.dev" - source: hosted - version: "2.13.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - build: - dependency: transitive - description: - name: build - sha256: "275bf6bb2a00a9852c28d4e0b410da1d833a734d57d39d44f94bfc895a484ec3" - url: "https://pub.dev" - source: hosted - version: "4.0.4" - build_config: - dependency: transitive - description: - name: build_config - sha256: "4070d2a59f8eec34c97c86ceb44403834899075f66e8a9d59706f8e7834f6f71" - url: "https://pub.dev" - source: hosted - version: "1.3.0" - build_daemon: - dependency: transitive - description: - name: build_daemon - sha256: bf05f6e12cfea92d3c09308d7bcdab1906cd8a179b023269eed00c071004b957 - url: "https://pub.dev" - source: hosted - version: "4.1.1" - build_runner: - dependency: "direct dev" - description: - name: build_runner - sha256: "7981eb922842c77033026eb4341d5af651562008cdb116bdfa31fc46516b6462" - url: "https://pub.dev" - source: hosted - version: "2.12.2" - built_collection: - dependency: transitive - description: - name: built_collection - sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" - url: "https://pub.dev" - source: hosted - version: "5.1.1" - built_value: - dependency: transitive - description: - name: built_value - sha256: "6ae8a6435a8c6520c7077b107e77f1fb4ba7009633259a4d49a8afd8e7efc5e9" - url: "https://pub.dev" - source: hosted - version: "8.12.4" - cached_network_image: - dependency: "direct main" - description: - name: cached_network_image - sha256: "7c1183e361e5c8b0a0f21a28401eecdbde252441106a9816400dd4c2b2424916" - url: "https://pub.dev" - source: hosted - version: "3.4.1" - cached_network_image_platform_interface: - dependency: transitive - description: - name: cached_network_image_platform_interface - sha256: "35814b016e37fbdc91f7ae18c8caf49ba5c88501813f73ce8a07027a395e2829" - url: "https://pub.dev" - source: hosted - version: "4.1.1" - cached_network_image_web: - dependency: transitive - description: - name: cached_network_image_web - sha256: "980842f4e8e2535b8dbd3d5ca0b1f0ba66bf61d14cc3a17a9b4788a3685ba062" - url: "https://pub.dev" - source: hosted - version: "1.3.1" - characters: - dependency: transitive - description: - name: characters - sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b - url: "https://pub.dev" - source: hosted - version: "1.4.1" - checked_yaml: - dependency: transitive - description: - name: checked_yaml - sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f" - url: "https://pub.dev" - source: hosted - version: "2.0.4" - clock: - dependency: transitive - description: - name: clock - sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b - url: "https://pub.dev" - source: hosted - version: "1.1.2" - code_assets: - dependency: transitive - description: - name: code_assets - sha256: "83ccdaa064c980b5596c35dd64a8d3ecc68620174ab9b90b6343b753aa721687" - url: "https://pub.dev" - source: hosted - version: "1.0.0" - code_builder: - dependency: transitive - description: - name: code_builder - sha256: "6a6cab2ba4680d6423f34a9b972a4c9a94ebe1b62ecec4e1a1f2cba91fd1319d" - url: "https://pub.dev" - source: hosted - version: "4.11.1" - collection: - dependency: transitive - description: - name: collection - sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" - url: "https://pub.dev" - source: hosted - version: "1.19.1" - convert: - dependency: transitive - description: - name: convert - sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 - url: "https://pub.dev" - source: hosted - version: "3.1.2" - crypto: - dependency: transitive - description: - name: crypto - sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf - url: "https://pub.dev" - source: hosted - version: "3.0.7" - csslib: - dependency: transitive - description: - name: csslib - sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" - url: "https://pub.dev" - source: hosted - version: "1.0.2" - cupertino_icons: - dependency: "direct main" - description: - name: cupertino_icons - sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 - url: "https://pub.dev" - source: hosted - version: "1.0.8" - dart_style: - dependency: transitive - description: - name: dart_style - sha256: "29f7ecc274a86d32920b1d9cfc7502fa87220da41ec60b55f329559d5732e2b2" - url: "https://pub.dev" - source: hosted - version: "3.1.7" - dio: - dependency: "direct main" - description: - name: dio - sha256: aff32c08f92787a557dd5c0145ac91536481831a01b4648136373cddb0e64f8c - url: "https://pub.dev" - source: hosted - version: "5.9.2" - dio_web_adapter: - dependency: transitive - description: - name: dio_web_adapter - sha256: "2f9e64323a7c3c7ef69567d5c800424a11f8337b8b228bad02524c9fb3c1f340" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - fake_async: - dependency: transitive - description: - name: fake_async - sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" - url: "https://pub.dev" - source: hosted - version: "1.3.3" - ffi: - dependency: transitive - description: - name: ffi - sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45" - url: "https://pub.dev" - source: hosted - version: "2.2.0" - file: - dependency: transitive - description: - name: file - sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 - url: "https://pub.dev" - source: hosted - version: "7.0.1" - firebase_core: - dependency: "direct main" - description: - name: firebase_core - sha256: "7be63a3f841fc9663342f7f3a011a42aef6a61066943c90b1c434d79d5c995c5" - url: "https://pub.dev" - source: hosted - version: "3.15.2" - firebase_core_platform_interface: - dependency: transitive - description: - name: firebase_core_platform_interface - sha256: cccb4f572325dc14904c02fcc7db6323ad62ba02536833dddb5c02cac7341c64 - url: "https://pub.dev" - source: hosted - version: "6.0.2" - firebase_core_web: - dependency: transitive - description: - name: firebase_core_web - sha256: "0ed0dc292e8f9ac50992e2394e9d336a0275b6ae400d64163fdf0a8a8b556c37" - url: "https://pub.dev" - source: hosted - version: "2.24.1" - firebase_messaging: - dependency: "direct main" - description: - name: firebase_messaging - sha256: "60be38574f8b5658e2f22b7e311ff2064bea835c248424a383783464e8e02fcc" - url: "https://pub.dev" - source: hosted - version: "15.2.10" - firebase_messaging_platform_interface: - dependency: transitive - description: - name: firebase_messaging_platform_interface - sha256: "685e1771b3d1f9c8502771ccc9f91485b376ffe16d553533f335b9183ea99754" - url: "https://pub.dev" - source: hosted - version: "4.6.10" - firebase_messaging_web: - dependency: transitive - description: - name: firebase_messaging_web - sha256: "0d1be17bc89ed3ff5001789c92df678b2e963a51b6fa2bdb467532cc9dbed390" - url: "https://pub.dev" - source: hosted - version: "3.10.10" - fixnum: - dependency: transitive - description: - name: fixnum - sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be - url: "https://pub.dev" - source: hosted - version: "1.1.1" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_cache_manager: - dependency: transitive - description: - name: flutter_cache_manager - sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386" - url: "https://pub.dev" - source: hosted - version: "3.4.1" - flutter_lints: - dependency: "direct dev" - description: - name: flutter_lints - sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" - url: "https://pub.dev" - source: hosted - version: "4.0.0" - flutter_plugin_android_lifecycle: - dependency: transitive - description: - name: flutter_plugin_android_lifecycle - sha256: ee8068e0e1cd16c4a82714119918efdeed33b3ba7772c54b5d094ab53f9b7fd1 - url: "https://pub.dev" - source: hosted - version: "2.0.33" - flutter_riverpod: - dependency: "direct main" - description: - name: flutter_riverpod - sha256: "9532ee6db4a943a1ed8383072a2e3eeda041db5657cdf6d2acecf3c21ecbe7e1" - url: "https://pub.dev" - source: hosted - version: "2.6.1" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - flutter_web_plugins: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - geolocator: - dependency: "direct main" - description: - name: geolocator - sha256: f62bcd90459e63210bbf9c35deb6a51c521f992a78de19a1fe5c11704f9530e2 - url: "https://pub.dev" - source: hosted - version: "13.0.4" - geolocator_android: - dependency: transitive - description: - name: geolocator_android - sha256: fcb1760a50d7500deca37c9a666785c047139b5f9ee15aa5469fae7dbbe3170d - url: "https://pub.dev" - source: hosted - version: "4.6.2" - geolocator_apple: - dependency: transitive - description: - name: geolocator_apple - sha256: dbdd8789d5aaf14cf69f74d4925ad1336b4433a6efdf2fce91e8955dc921bf22 - url: "https://pub.dev" - source: hosted - version: "2.3.13" - geolocator_platform_interface: - dependency: transitive - description: - name: geolocator_platform_interface - sha256: "30cb64f0b9adcc0fb36f628b4ebf4f731a2961a0ebd849f4b56200205056fe67" - url: "https://pub.dev" - source: hosted - version: "4.2.6" - geolocator_web: - dependency: transitive - description: - name: geolocator_web - sha256: b1ae9bdfd90f861fde8fd4f209c37b953d65e92823cb73c7dee1fa021b06f172 - url: "https://pub.dev" - source: hosted - version: "4.1.3" - geolocator_windows: - dependency: transitive - description: - name: geolocator_windows - sha256: "175435404d20278ffd220de83c2ca293b73db95eafbdc8131fe8609be1421eb6" - url: "https://pub.dev" - source: hosted - version: "0.2.5" - glob: - dependency: transitive - description: - name: glob - sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de - url: "https://pub.dev" - source: hosted - version: "2.1.3" - go_router: - dependency: "direct main" - description: - name: go_router - sha256: f02fd7d2a4dc512fec615529824fdd217fecb3a3d3de68360293a551f21634b3 - url: "https://pub.dev" - source: hosted - version: "14.8.1" - google_maps: - dependency: transitive - description: - name: google_maps - sha256: "5d410c32112d7c6eb7858d359275b2aa04778eed3e36c745aeae905fb2fa6468" - url: "https://pub.dev" - source: hosted - version: "8.2.0" - google_maps_flutter: - dependency: "direct main" - description: - name: google_maps_flutter - sha256: "85339d0c9b5c7bac305df0bc1bc72a990f7f59b1846f048c96c022110d5d4a69" - url: "https://pub.dev" - source: hosted - version: "2.15.0" - google_maps_flutter_android: - dependency: transitive - description: - name: google_maps_flutter_android - sha256: ba0947315ddc9107ecc8d95fa26eb3b87b4f27b221606ce72518314d99c7306c - url: "https://pub.dev" - source: hosted - version: "2.19.2" - google_maps_flutter_ios: - dependency: transitive - description: - name: google_maps_flutter_ios - sha256: c855600dce17e77e8af96edcf85cb68501675bb77a72f85009d08c17a8805ace - url: "https://pub.dev" - source: hosted - version: "2.18.0" - google_maps_flutter_platform_interface: - dependency: transitive - description: - name: google_maps_flutter_platform_interface - sha256: "0f8c6674d70c7e9a09cd34f63b18ebaf8a5822e85b558128eae0fdf02b4a3e93" - url: "https://pub.dev" - source: hosted - version: "2.14.2" - google_maps_flutter_web: - dependency: transitive - description: - name: google_maps_flutter_web - sha256: "2321d24f8bce2d01c441b9fd7835a6032ea44b3f0bfcf93bcebd9740fb5a85cd" - url: "https://pub.dev" - source: hosted - version: "0.6.1" - graphs: - dependency: transitive - description: - name: graphs - sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" - url: "https://pub.dev" - source: hosted - version: "2.3.2" - hooks: - dependency: transitive - description: - name: hooks - sha256: e79ed1e8e1929bc6ecb6ec85f0cb519c887aa5b423705ded0d0f2d9226def388 - url: "https://pub.dev" - source: hosted - version: "1.0.2" - html: - dependency: transitive - description: - name: html - sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602" - url: "https://pub.dev" - source: hosted - version: "0.15.6" - http: - dependency: transitive - description: - name: http - sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" - url: "https://pub.dev" - source: hosted - version: "1.6.0" - http_multi_server: - dependency: transitive - description: - name: http_multi_server - sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 - url: "https://pub.dev" - source: hosted - version: "3.2.2" - http_parser: - dependency: transitive - description: - name: http_parser - sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" - url: "https://pub.dev" - source: hosted - version: "4.1.2" - intl: - dependency: "direct main" - description: - name: intl - sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf - url: "https://pub.dev" - source: hosted - version: "0.19.0" - io: - dependency: transitive - description: - name: io - sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b - url: "https://pub.dev" - source: hosted - version: "1.0.5" - json_annotation: - dependency: "direct main" - description: - name: json_annotation - sha256: cb09e7dac6210041fad964ed7fbee004f14258b4eca4040f72d1234062ace4c8 - url: "https://pub.dev" - source: hosted - version: "4.11.0" - json_serializable: - dependency: "direct dev" - description: - name: json_serializable - sha256: "44729f5c45748e6748f6b9a57ab8f7e4336edc8ae41fc295070e3814e616a6c0" - url: "https://pub.dev" - source: hosted - version: "6.13.0" - leak_tracker: - dependency: transitive - description: - name: leak_tracker - sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" - url: "https://pub.dev" - source: hosted - version: "11.0.2" - leak_tracker_flutter_testing: - dependency: transitive - description: - name: leak_tracker_flutter_testing - sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" - url: "https://pub.dev" - source: hosted - version: "3.0.10" - leak_tracker_testing: - dependency: transitive - description: - name: leak_tracker_testing - sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" - url: "https://pub.dev" - source: hosted - version: "3.0.2" - lints: - dependency: transitive - description: - name: lints - sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" - url: "https://pub.dev" - source: hosted - version: "4.0.0" - logging: - dependency: transitive - description: - name: logging - sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 - url: "https://pub.dev" - source: hosted - version: "1.3.0" - matcher: - dependency: transitive - description: - name: matcher - sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 - url: "https://pub.dev" - source: hosted - version: "0.12.19" - material_color_utilities: - dependency: transitive - description: - name: material_color_utilities - sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" - url: "https://pub.dev" - source: hosted - version: "0.13.0" - meta: - dependency: transitive - description: - name: meta - sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" - url: "https://pub.dev" - source: hosted - version: "1.17.0" - mime: - dependency: transitive - description: - name: mime - sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" - url: "https://pub.dev" - source: hosted - version: "2.0.0" - native_toolchain_c: - dependency: transitive - description: - name: native_toolchain_c - sha256: "92b2ca62c8bd2b8d2f267cdfccf9bfbdb7322f778f8f91b3ce5b5cda23a3899f" - url: "https://pub.dev" - source: hosted - version: "0.17.5" - objective_c: - dependency: transitive - description: - name: objective_c - sha256: "100a1c87616ab6ed41ec263b083c0ef3261ee6cd1dc3b0f35f8ddfa4f996fe52" - url: "https://pub.dev" - source: hosted - version: "9.3.0" - octo_image: - dependency: transitive - description: - name: octo_image - sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - package_config: - dependency: transitive - description: - name: package_config - sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc - url: "https://pub.dev" - source: hosted - version: "2.2.0" - path: - dependency: transitive - description: - name: path - sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" - url: "https://pub.dev" - source: hosted - version: "1.9.1" - path_provider: - dependency: transitive - description: - name: path_provider - sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" - url: "https://pub.dev" - source: hosted - version: "2.1.5" - path_provider_android: - dependency: transitive - description: - name: path_provider_android - sha256: f2c65e21139ce2c3dad46922be8272bb5963516045659e71bb16e151c93b580e - url: "https://pub.dev" - source: hosted - version: "2.2.22" - path_provider_foundation: - dependency: transitive - description: - name: path_provider_foundation - sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699" - url: "https://pub.dev" - source: hosted - version: "2.6.0" - path_provider_linux: - dependency: transitive - description: - name: path_provider_linux - sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 - url: "https://pub.dev" - source: hosted - version: "2.2.1" - path_provider_platform_interface: - dependency: transitive - description: - name: path_provider_platform_interface - sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - path_provider_windows: - dependency: transitive - description: - name: path_provider_windows - sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 - url: "https://pub.dev" - source: hosted - version: "2.3.0" - permission_handler: - dependency: "direct main" - description: - name: permission_handler - sha256: "59adad729136f01ea9e35a48f5d1395e25cba6cea552249ddbe9cf950f5d7849" - url: "https://pub.dev" - source: hosted - version: "11.4.0" - permission_handler_android: - dependency: transitive - description: - name: permission_handler_android - sha256: d3971dcdd76182a0c198c096b5db2f0884b0d4196723d21a866fc4cdea057ebc - url: "https://pub.dev" - source: hosted - version: "12.1.0" - permission_handler_apple: - dependency: transitive - description: - name: permission_handler_apple - sha256: f000131e755c54cf4d84a5d8bd6e4149e262cc31c5a8b1d698de1ac85fa41023 - url: "https://pub.dev" - source: hosted - version: "9.4.7" - permission_handler_html: - dependency: transitive - description: - name: permission_handler_html - sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24" - url: "https://pub.dev" - source: hosted - version: "0.1.3+5" - permission_handler_platform_interface: - dependency: transitive - description: - name: permission_handler_platform_interface - sha256: eb99b295153abce5d683cac8c02e22faab63e50679b937fa1bf67d58bb282878 - url: "https://pub.dev" - source: hosted - version: "4.3.0" - permission_handler_windows: - dependency: transitive - description: - name: permission_handler_windows - sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" - url: "https://pub.dev" - source: hosted - version: "0.2.1" - platform: - dependency: transitive - description: - name: platform - sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" - url: "https://pub.dev" - source: hosted - version: "3.1.6" - plugin_platform_interface: - dependency: transitive - description: - name: plugin_platform_interface - sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" - url: "https://pub.dev" - source: hosted - version: "2.1.8" - pool: - dependency: transitive - description: - name: pool - sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d" - url: "https://pub.dev" - source: hosted - version: "1.5.2" - pub_semver: - dependency: transitive - description: - name: pub_semver - sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" - url: "https://pub.dev" - source: hosted - version: "2.2.0" - pubspec_parse: - dependency: transitive - description: - name: pubspec_parse - sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082" - url: "https://pub.dev" - source: hosted - version: "1.5.0" - riverpod: - dependency: transitive - description: - name: riverpod - sha256: "59062512288d3056b2321804332a13ffdd1bf16df70dcc8e506e411280a72959" - url: "https://pub.dev" - source: hosted - version: "2.6.1" - rxdart: - dependency: transitive - description: - name: rxdart - sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" - url: "https://pub.dev" - source: hosted - version: "0.28.0" - sanitize_html: - dependency: transitive - description: - name: sanitize_html - sha256: "12669c4a913688a26555323fb9cec373d8f9fbe091f2d01c40c723b33caa8989" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - shared_preferences: - dependency: "direct main" - description: - name: shared_preferences - sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64" - url: "https://pub.dev" - source: hosted - version: "2.5.4" - shared_preferences_android: - dependency: transitive - description: - name: shared_preferences_android - sha256: "8374d6200ab33ac99031a852eba4c8eb2170c4bf20778b3e2c9eccb45384fb41" - url: "https://pub.dev" - source: hosted - version: "2.4.21" - shared_preferences_foundation: - dependency: transitive - description: - name: shared_preferences_foundation - sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f" - url: "https://pub.dev" - source: hosted - version: "2.5.6" - shared_preferences_linux: - dependency: transitive - description: - name: shared_preferences_linux - sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" - url: "https://pub.dev" - source: hosted - version: "2.4.1" - shared_preferences_platform_interface: - dependency: transitive - description: - name: shared_preferences_platform_interface - sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" - url: "https://pub.dev" - source: hosted - version: "2.4.1" - shared_preferences_web: - dependency: transitive - description: - name: shared_preferences_web - sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 - url: "https://pub.dev" - source: hosted - version: "2.4.3" - shared_preferences_windows: - dependency: transitive - description: - name: shared_preferences_windows - sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" - url: "https://pub.dev" - source: hosted - version: "2.4.1" - shelf: - dependency: transitive - description: - name: shelf - sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 - url: "https://pub.dev" - source: hosted - version: "1.4.2" - shelf_web_socket: - dependency: transitive - description: - name: shelf_web_socket - sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" - url: "https://pub.dev" - source: hosted - version: "3.0.0" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - source_gen: - dependency: transitive - description: - name: source_gen - sha256: "1d562a3c1f713904ebbed50d2760217fd8a51ca170ac4b05b0db490699dbac17" - url: "https://pub.dev" - source: hosted - version: "4.2.0" - source_helper: - dependency: transitive - description: - name: source_helper - sha256: "4a85e90b50694e652075cbe4575665539d253e6ec10e46e76b45368ab5e3caae" - url: "https://pub.dev" - source: hosted - version: "1.3.10" - source_span: - dependency: transitive - description: - name: source_span - sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" - url: "https://pub.dev" - source: hosted - version: "1.10.2" - sqflite: - dependency: transitive - description: - name: sqflite - sha256: e2297b1da52f127bc7a3da11439985d9b536f75070f3325e62ada69a5c585d03 - url: "https://pub.dev" - source: hosted - version: "2.4.2" - sqflite_android: - dependency: transitive - description: - name: sqflite_android - sha256: "881e28efdcc9950fd8e9bb42713dcf1103e62a2e7168f23c9338d82db13dec40" - url: "https://pub.dev" - source: hosted - version: "2.4.2+3" - sqflite_common: - dependency: transitive - description: - name: sqflite_common - sha256: "6ef422a4525ecc601db6c0a2233ff448c731307906e92cabc9ba292afaae16a6" - url: "https://pub.dev" - source: hosted - version: "2.5.6" - sqflite_darwin: - dependency: transitive - description: - name: sqflite_darwin - sha256: "279832e5cde3fe99e8571879498c9211f3ca6391b0d818df4e17d9fff5c6ccb3" - url: "https://pub.dev" - source: hosted - version: "2.4.2" - sqflite_platform_interface: - dependency: transitive - description: - name: sqflite_platform_interface - sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920" - url: "https://pub.dev" - source: hosted - version: "2.4.0" - stack_trace: - dependency: transitive - description: - name: stack_trace - sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" - url: "https://pub.dev" - source: hosted - version: "1.12.1" - state_notifier: - dependency: transitive - description: - name: state_notifier - sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb - url: "https://pub.dev" - source: hosted - version: "1.0.0" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - stream_transform: - dependency: transitive - description: - name: stream_transform - sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 - url: "https://pub.dev" - source: hosted - version: "2.1.1" - string_scanner: - dependency: transitive - description: - name: string_scanner - sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" - url: "https://pub.dev" - source: hosted - version: "1.4.1" - synchronized: - dependency: transitive - description: - name: synchronized - sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0 - url: "https://pub.dev" - source: hosted - version: "3.4.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" - url: "https://pub.dev" - source: hosted - version: "1.2.2" - test_api: - dependency: transitive - description: - name: test_api - sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" - url: "https://pub.dev" - source: hosted - version: "0.7.10" - typed_data: - dependency: transitive - description: - name: typed_data - sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 - url: "https://pub.dev" - source: hosted - version: "1.4.0" - uuid: - dependency: transitive - description: - name: uuid - sha256: "1fef9e8e11e2991bb773070d4656b7bd5d850967a2456cfc83cf47925ba79489" - url: "https://pub.dev" - source: hosted - version: "4.5.3" - vector_math: - dependency: transitive - description: - name: vector_math - sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b - url: "https://pub.dev" - source: hosted - version: "2.2.0" - vm_service: - dependency: transitive - description: - name: vm_service - sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" - url: "https://pub.dev" - source: hosted - version: "15.0.2" - watcher: - dependency: transitive - description: - name: watcher - sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635" - url: "https://pub.dev" - source: hosted - version: "1.2.1" - web: - dependency: transitive - description: - name: web - sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" - url: "https://pub.dev" - source: hosted - version: "1.1.1" - web_socket: - dependency: transitive - description: - name: web_socket - sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" - url: "https://pub.dev" - source: hosted - version: "1.0.1" - web_socket_channel: - dependency: transitive - description: - name: web_socket_channel - sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 - url: "https://pub.dev" - source: hosted - version: "3.0.3" - xdg_directories: - dependency: transitive - description: - name: xdg_directories - sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" - url: "https://pub.dev" - source: hosted - version: "1.1.0" - yaml: - dependency: transitive - description: - name: yaml - sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce - url: "https://pub.dev" - source: hosted - version: "3.1.3" -sdks: - dart: ">=3.10.3 <4.0.0" - flutter: ">=3.38.4" diff --git a/ontime_driver_flutter/pubspec.yaml b/ontime_driver_flutter/pubspec.yaml deleted file mode 100644 index c8cd975..0000000 --- a/ontime_driver_flutter/pubspec.yaml +++ /dev/null @@ -1,38 +0,0 @@ -name: ontime_driver_flutter -description: Ontime driver Flutter app (MVP) consuming backendpanel Driver API. -version: 0.1.0+1 - -environment: - sdk: ">=3.3.0 <4.0.0" - -dependencies: - flutter: - sdk: flutter - - cupertino_icons: ^1.0.8 - - dio: ^5.7.0 - json_annotation: ^4.9.0 - flutter_riverpod: ^2.6.1 - go_router: ^14.3.0 - shared_preferences: ^2.3.3 - geolocator: ^13.0.2 - google_maps_flutter: ^2.10.0 - permission_handler: ^11.3.1 - firebase_core: ^3.6.0 - firebase_messaging: ^15.1.0 - intl: ^0.19.0 - cached_network_image: ^3.4.1 - -dev_dependencies: - flutter_test: - sdk: flutter - flutter_lints: ^4.0.0 - build_runner: ^2.4.13 - json_serializable: ^6.9.0 - -flutter: - uses-material-design: true - assets: - - assets/images/ - diff --git a/ontime_driver_flutter/test/widget_test.dart b/ontime_driver_flutter/test/widget_test.dart deleted file mode 100644 index 82cccc0..0000000 --- a/ontime_driver_flutter/test/widget_test.dart +++ /dev/null @@ -1,30 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility in the flutter_test package. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:ontime_driver_flutter/main.dart'; - -void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - }); -} diff --git a/ontime_merchant_flutter/README.md b/ontime_merchant_flutter/README.md deleted file mode 100644 index 1776c06..0000000 --- a/ontime_merchant_flutter/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# Ontime Merchant (Flutter) - -Flutter app for Ontime **merchant**, using backend API at `https://apitest.semestaterpadu.my.id/api/`. - -## Setup - -- Flutter SDK (e.g. 3.19+) -- Android: run `flutter create . --platforms=android` if needed. - -## Run / Build - -```bash -flutter pub get -flutter run -# or -flutter build apk --release -``` - -## Firebase (FCM) - -Add `google-services.json` to `android/app/` for push. FCM token is sent as `token` on login when available. - -## Validation - -- **Login**: `Merchant/login` (telepon_mitra + password, optional token). -- **Home**: `Merchant/home` (saldo, transaksi, user). -- **Availability**: `Merchant/onoff` (status_merchant). -- **Catalog**: `Merchant/kategori`, `Merchant/item`, add/edit/delete. -- Compare with `OnTime_Merchant_live` and backendpanel API responses. diff --git a/ontime_merchant_flutter/assets/images/.gitkeep b/ontime_merchant_flutter/assets/images/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/ontime_merchant_flutter/lib/core/app_config.dart b/ontime_merchant_flutter/lib/core/app_config.dart deleted file mode 100644 index 5c443b9..0000000 --- a/ontime_merchant_flutter/lib/core/app_config.dart +++ /dev/null @@ -1,7 +0,0 @@ -class AppConfig { - static const String baseUrl = 'https://apitest.semestaterpadu.my.id/'; - static const String apiBase = '${baseUrl}api/'; - static const String imagesMerchant = '${baseUrl}images/merchant/'; - static const String imagesItem = '${baseUrl}images/itemmerchant/'; - static const String imagesBank = '${baseUrl}images/bank/'; -} diff --git a/ontime_merchant_flutter/lib/core/app_router.dart b/ontime_merchant_flutter/lib/core/app_router.dart deleted file mode 100644 index 98fa732..0000000 --- a/ontime_merchant_flutter/lib/core/app_router.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; -import 'package:ontime_merchant_flutter/features/auth/presentation/login_screen.dart'; -import 'package:ontime_merchant_flutter/features/home/presentation/home_shell.dart'; - -final _rootNavKey = GlobalKey(debugLabel: 'root'); - -GoRouter createAppRouter() { - return GoRouter( - navigatorKey: _rootNavKey, - initialLocation: '/auth/login', - routes: [ - GoRoute( - path: '/auth/login', - name: 'login', - pageBuilder: (_, __) => const MaterialPage(child: LoginScreen()), - ), - GoRoute( - path: '/home', - name: 'home', - pageBuilder: (_, __) => const MaterialPage(child: HomeShell()), - ), - ], - ); -} diff --git a/ontime_merchant_flutter/lib/core/app_theme.dart b/ontime_merchant_flutter/lib/core/app_theme.dart deleted file mode 100644 index 5c79913..0000000 --- a/ontime_merchant_flutter/lib/core/app_theme.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:flutter/material.dart'; - -class AppTheme { - static ThemeData light() { - final base = ThemeData.light(useMaterial3: true); - return base.copyWith( - colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF00AEEF)), - scaffoldBackgroundColor: Colors.grey[50], - appBarTheme: const AppBarTheme(elevation: 0, centerTitle: true), - inputDecorationTheme: const InputDecorationTheme(border: OutlineInputBorder()), - ); - } -} diff --git a/ontime_merchant_flutter/lib/core/fcm_service.dart b/ontime_merchant_flutter/lib/core/fcm_service.dart deleted file mode 100644 index faeb2ad..0000000 --- a/ontime_merchant_flutter/lib/core/fcm_service.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:firebase_core/firebase_core.dart'; -import 'package:firebase_messaging/firebase_messaging.dart'; - -/// FCM token for backend (Merchant uses `token` / `token_merchant`). -class FcmService { - static bool _initialized = false; - - static Future init() async { - if (_initialized) return; - try { - await Firebase.initializeApp(); - _initialized = true; - } catch (_) {} - } - - static Future getToken() async { - if (!_initialized) return null; - try { - return await FirebaseMessaging.instance.getToken(); - } catch (_) { - return null; - } - } -} diff --git a/ontime_merchant_flutter/lib/data/api/api_client.dart b/ontime_merchant_flutter/lib/data/api/api_client.dart deleted file mode 100644 index 88eb7d6..0000000 --- a/ontime_merchant_flutter/lib/data/api/api_client.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:dio/dio.dart'; -import 'package:ontime_merchant_flutter/core/app_config.dart'; - -class ApiClient { - ApiClient({ - required String? basicAuthUser, - required String? basicAuthPassword, - }) : _dio = Dio( - BaseOptions( - baseUrl: AppConfig.apiBase, - connectTimeout: const Duration(seconds: 15), - receiveTimeout: const Duration(seconds: 20), - ), - ) { - if (basicAuthUser != null && basicAuthPassword != null) { - final auth = '$basicAuthUser:$basicAuthPassword'; - final encoded = String.fromCharCodes(auth.codeUnits); - _dio.options.headers['Authorization'] = 'Basic $encoded'; - } - } - - final Dio _dio; - Dio get raw => _dio; -} diff --git a/ontime_merchant_flutter/lib/features/auth/application/auth_controller.dart b/ontime_merchant_flutter/lib/features/auth/application/auth_controller.dart deleted file mode 100644 index 419fcac..0000000 --- a/ontime_merchant_flutter/lib/features/auth/application/auth_controller.dart +++ /dev/null @@ -1,77 +0,0 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:ontime_merchant_flutter/data/api/api_client.dart'; -import 'package:ontime_merchant_flutter/features/auth/data/merchant_auth_api.dart'; -import 'package:ontime_merchant_flutter/features/auth/data/models/merchant_model.dart'; -import 'package:shared_preferences/shared_preferences.dart'; - -class AuthState { - const AuthState({ - this.merchant, - this.isLoading = false, - this.errorMessage, - }); - - final MerchantModel? merchant; - final bool isLoading; - final String? errorMessage; - - AuthState copyWith({ - MerchantModel? merchant, - bool? isLoading, - String? errorMessage, - }) { - return AuthState( - merchant: merchant ?? this.merchant, - isLoading: isLoading ?? this.isLoading, - errorMessage: errorMessage, - ); - } -} - -final authControllerProvider = - StateNotifierProvider((ref) => AuthController()); - -class AuthController extends StateNotifier { - AuthController() : super(const AuthState()); - - late final MerchantAuthApi _api = - MerchantAuthApi(ApiClient(basicAuthUser: null, basicAuthPassword: null)); - - Future login({ - required String noTelepon, - required String password, - String? fcmToken, - }) async { - state = state.copyWith(isLoading: true, errorMessage: null); - try { - final merchant = await _api.login( - noTelepon: noTelepon, - password: password, - fcmToken: fcmToken, - ); - state = state.copyWith(merchant: merchant, isLoading: false); - final prefs = await SharedPreferences.getInstance(); - await prefs.setString('merchant_id_mitra', merchant.idMitra); - await prefs.setString('merchant_id_merchant', merchant.idMerchant); - await prefs.setString('merchant_phone', merchant.teleponMitra); - await prefs.setString('merchant_token', merchant.tokenMerchant); - } catch (e) { - state = state.copyWith( - isLoading: false, - errorMessage: e.toString().replaceFirst('Exception: ', ''), - ); - } - } - - void logout() { - state = const AuthState(); - } - - static Future clearStored() async { - final prefs = await SharedPreferences.getInstance(); - await prefs.remove('merchant_id_mitra'); - await prefs.remove('merchant_id_merchant'); - await prefs.remove('merchant_phone'); - await prefs.remove('merchant_token'); - } -} diff --git a/ontime_merchant_flutter/lib/features/auth/data/merchant_auth_api.dart b/ontime_merchant_flutter/lib/features/auth/data/merchant_auth_api.dart deleted file mode 100644 index a4701ba..0000000 --- a/ontime_merchant_flutter/lib/features/auth/data/merchant_auth_api.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:dio/dio.dart'; -import 'package:ontime_merchant_flutter/data/api/api_client.dart'; -import 'package:ontime_merchant_flutter/features/auth/data/models/merchant_model.dart'; - -class MerchantAuthApi { - MerchantAuthApi(this._client); - final ApiClient _client; - - Future login({ - required String noTelepon, - required String password, - String? fcmToken, - }) async { - final payload = { - 'no_telepon': noTelepon, - 'password': password, - }; - if (fcmToken != null && fcmToken.isNotEmpty) payload['token'] = fcmToken; - - final Response response = - await _client.raw.post('Merchant/login', data: payload); - final data = response.data is Map - ? response.data as Map - : {}; - - if (data['message'] == 'banned') throw Exception('banned'); - if (data['code']?.toString() != '200') { - throw Exception(data['message'] ?? 'Login failed'); - } - - final list = data['data'] as List? ?? []; - if (list.isEmpty) throw Exception('Merchant data not found'); - return MerchantModel.fromJson(list.first as Map); - } -} diff --git a/ontime_merchant_flutter/lib/features/auth/data/models/merchant_model.dart b/ontime_merchant_flutter/lib/features/auth/data/models/merchant_model.dart deleted file mode 100644 index 20e00e9..0000000 --- a/ontime_merchant_flutter/lib/features/auth/data/models/merchant_model.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:json_annotation/json_annotation.dart'; - -part 'merchant_model.g.dart'; - -@JsonSerializable() -class MerchantModel { - MerchantModel({ - required this.idMitra, - required this.idMerchant, - required this.teleponMitra, - required this.tokenMerchant, - this.namaMerchant, - this.emailMitra, - }); - - @JsonKey(name: 'id_mitra') - final String idMitra; - - @JsonKey(name: 'id_merchant') - final String idMerchant; - - @JsonKey(name: 'telepon_mitra') - final String teleponMitra; - - @JsonKey(name: 'token_merchant') - final String tokenMerchant; - - @JsonKey(name: 'nama_merchant') - final String? namaMerchant; - - @JsonKey(name: 'email_mitra') - final String? emailMitra; - - factory MerchantModel.fromJson(Map json) => - _$MerchantModelFromJson(json); - Map toJson() => _$MerchantModelToJson(this); -} diff --git a/ontime_merchant_flutter/lib/features/auth/data/models/merchant_model.g.dart b/ontime_merchant_flutter/lib/features/auth/data/models/merchant_model.g.dart deleted file mode 100644 index 4a039dc..0000000 --- a/ontime_merchant_flutter/lib/features/auth/data/models/merchant_model.g.dart +++ /dev/null @@ -1,22 +0,0 @@ -part of 'merchant_model.dart'; - -MerchantModel _$MerchantModelFromJson(Map json) { - return MerchantModel( - idMitra: json['id_mitra'] as String? ?? '', - idMerchant: json['id_merchant'] as String? ?? '', - teleponMitra: json['telepon_mitra'] as String? ?? '', - tokenMerchant: json['token_merchant'] as String? ?? '', - namaMerchant: json['nama_merchant'] as String?, - emailMitra: json['email_mitra'] as String?, - ); -} - -Map _$MerchantModelToJson(MerchantModel instance) => - { - 'id_mitra': instance.idMitra, - 'id_merchant': instance.idMerchant, - 'telepon_mitra': instance.teleponMitra, - 'token_merchant': instance.tokenMerchant, - 'nama_merchant': instance.namaMerchant, - 'email_mitra': instance.emailMitra, - }; diff --git a/ontime_merchant_flutter/lib/features/auth/presentation/login_screen.dart b/ontime_merchant_flutter/lib/features/auth/presentation/login_screen.dart deleted file mode 100644 index 87f3599..0000000 --- a/ontime_merchant_flutter/lib/features/auth/presentation/login_screen.dart +++ /dev/null @@ -1,92 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:go_router/go_router.dart'; -import 'package:ontime_merchant_flutter/core/fcm_service.dart'; -import 'package:ontime_merchant_flutter/features/auth/application/auth_controller.dart'; - -class LoginScreen extends ConsumerStatefulWidget { - const LoginScreen({super.key}); - - @override - ConsumerState createState() => _LoginScreenState(); -} - -class _LoginScreenState extends ConsumerState { - final _formKey = GlobalKey(); - final _phoneController = TextEditingController(); - final _passwordController = TextEditingController(); - - @override - void dispose() { - _phoneController.dispose(); - _passwordController.dispose(); - super.dispose(); - } - - Future _submit() async { - if (!_formKey.currentState!.validate()) return; - final fcmToken = await FcmService.getToken(); - final auth = ref.read(authControllerProvider.notifier); - await auth.login( - noTelepon: _phoneController.text.trim(), - password: _passwordController.text, - fcmToken: fcmToken, - ); - final state = ref.read(authControllerProvider); - if (state.merchant != null && mounted) context.go('/home'); - } - - @override - Widget build(BuildContext context) { - final state = ref.watch(authControllerProvider); - return Scaffold( - appBar: AppBar(title: const Text('Masuk Merchant')), - body: SafeArea( - child: Padding( - padding: const EdgeInsets.all(16), - child: Form( - key: _formKey, - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - TextFormField( - controller: _phoneController, - decoration: const InputDecoration(labelText: 'No. Telepon'), - keyboardType: TextInputType.phone, - validator: (v) => - (v == null || v.isEmpty) ? 'No. telepon wajib diisi' : null, - ), - const SizedBox(height: 12), - TextFormField( - controller: _passwordController, - decoration: const InputDecoration(labelText: 'Kata sandi'), - obscureText: true, - validator: (v) => - (v == null || v.isEmpty) ? 'Password wajib diisi' : null, - ), - if (state.errorMessage != null) ...[ - const SizedBox(height: 8), - Text( - state.errorMessage!, - style: TextStyle(color: Theme.of(context).colorScheme.error), - ), - ], - const Spacer(), - ElevatedButton( - onPressed: state.isLoading ? null : _submit, - child: state.isLoading - ? const SizedBox( - height: 20, - width: 20, - child: CircularProgressIndicator(strokeWidth: 2), - ) - : const Text('Masuk'), - ), - ], - ), - ), - ), - ), - ); - } -} diff --git a/ontime_merchant_flutter/lib/features/home/data/merchant_home_api.dart b/ontime_merchant_flutter/lib/features/home/data/merchant_home_api.dart deleted file mode 100644 index 37d9d16..0000000 --- a/ontime_merchant_flutter/lib/features/home/data/merchant_home_api.dart +++ /dev/null @@ -1,99 +0,0 @@ -import 'package:dio/dio.dart'; -import 'package:ontime_merchant_flutter/data/api/api_client.dart'; - -class MerchantHomeApi { - MerchantHomeApi(this._client); - final ApiClient _client; - - Future> home({ - required String noTelepon, - required String idMitra, - required String idMerchant, - }) async { - final Response response = await _client.raw.post( - 'Merchant/home', - data: { - 'no_telepon': noTelepon, - 'idmitra': idMitra, - 'idmerchant': idMerchant, - }, - ); - final data = response.data is Map - ? response.data as Map - : {}; - if (data['code']?.toString() != '200') { - throw Exception(data['message'] ?? 'Failed'); - } - return data; - } - - Future onoff({ - required String idMerchant, - required String token, - required int status, - }) async { - await _client.raw.post( - 'Merchant/onoff', - data: { - 'idmerchant': idMerchant, - 'token': token, - 'status': status, - }, - ); - } - - Future> history({ - required String noTelepon, - required String idMerchant, - required String day, - }) async { - final Response response = await _client.raw.post( - 'Merchant/history', - data: { - 'no_telepon': noTelepon, - 'idmerchant': idMerchant, - 'day': day, - }, - ); - final data = response.data is Map - ? response.data as Map - : {}; - if (data['code']?.toString() != '200') throw Exception('Failed'); - return data; - } - - Future> kategori({ - required String noTelepon, - required String idMerchant, - }) async { - final Response response = await _client.raw.post( - 'Merchant/kategori', - data: {'no_telepon': noTelepon, 'idmerchant': idMerchant}, - ); - final data = response.data is Map - ? response.data as Map - : {}; - if (data['code']?.toString() != '200') throw Exception('Failed'); - return data; - } - - Future> item({ - required String noTelepon, - required String idMerchant, - required String idKategori, - }) async { - final Response response = await _client.raw.post( - 'Merchant/item', - data: { - 'no_telepon': noTelepon, - 'idmerchant': idMerchant, - 'idkategori': idKategori, - }, - ); - final data = response.data is Map - ? response.data as Map - : {}; - if (data['code']?.toString() != '200') throw Exception('Failed'); - return data['data'] as List? ?? []; - } -} diff --git a/ontime_merchant_flutter/lib/features/home/presentation/home_shell.dart b/ontime_merchant_flutter/lib/features/home/presentation/home_shell.dart deleted file mode 100644 index 76d4cda..0000000 --- a/ontime_merchant_flutter/lib/features/home/presentation/home_shell.dart +++ /dev/null @@ -1,86 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:go_router/go_router.dart'; -import 'package:ontime_merchant_flutter/features/auth/application/auth_controller.dart'; - -class HomeShell extends ConsumerStatefulWidget { - const HomeShell({super.key}); - - @override - ConsumerState createState() => _HomeShellState(); -} - -class _HomeShellState extends ConsumerState { - int _index = 0; - - @override - Widget build(BuildContext context) { - final merchant = ref.watch(authControllerProvider).merchant; - final pages = [ - const Center(child: Text('Toko – pesanan & saldo')), - const Center(child: Text('Riwayat')), - const Center(child: Text('Chat')), - const Center(child: Text('Menu (kategori & item)')), - _ProfileTab(), - ]; - return Scaffold( - appBar: AppBar( - title: Text(merchant?.namaMerchant ?? 'Merchant'), - ), - body: SafeArea(child: pages[_index]), - bottomNavigationBar: NavigationBar( - selectedIndex: _index, - onDestinationSelected: (i) => setState(() => _index = i), - destinations: const [ - NavigationDestination( - icon: Icon(Icons.store_outlined), - selectedIcon: Icon(Icons.store), - label: 'Toko', - ), - NavigationDestination( - icon: Icon(Icons.history), - selectedIcon: Icon(Icons.history), - label: 'Riwayat', - ), - NavigationDestination( - icon: Icon(Icons.chat_outlined), - selectedIcon: Icon(Icons.chat), - label: 'Chat', - ), - NavigationDestination( - icon: Icon(Icons.restaurant_menu_outlined), - selectedIcon: Icon(Icons.restaurant_menu), - label: 'Menu', - ), - NavigationDestination( - icon: Icon(Icons.settings_outlined), - selectedIcon: Icon(Icons.settings), - label: 'Pengaturan', - ), - ], - ), - ); - } -} - -class _ProfileTab extends ConsumerWidget { - @override - Widget build(BuildContext context, WidgetRef ref) { - return Padding( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - ElevatedButton( - onPressed: () async { - ref.read(authControllerProvider.notifier).logout(); - await AuthController.clearStored(); - if (context.mounted) context.go('/auth/login'); - }, - child: const Text('Keluar'), - ), - ], - ), - ); - } -} diff --git a/ontime_merchant_flutter/lib/main.dart b/ontime_merchant_flutter/lib/main.dart deleted file mode 100644 index 418daa0..0000000 --- a/ontime_merchant_flutter/lib/main.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import 'core/app_router.dart'; -import 'core/app_theme.dart'; -import 'core/fcm_service.dart'; - -void main() async { - WidgetsFlutterBinding.ensureInitialized(); - await FcmService.init(); - runApp( - ProviderScope( - child: MaterialApp.router( - debugShowCheckedModeBanner: false, - title: 'Ontime Merchant', - theme: AppTheme.light(), - routerConfig: createAppRouter(), - ), - ), - ); -} diff --git a/ontime_merchant_flutter/pubspec.lock b/ontime_merchant_flutter/pubspec.lock deleted file mode 100644 index e1900dc..0000000 --- a/ontime_merchant_flutter/pubspec.lock +++ /dev/null @@ -1,930 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - _fe_analyzer_shared: - dependency: transitive - description: - name: _fe_analyzer_shared - sha256: "8d7ff3948166b8ec5da0fbb5962000926b8e02f2ed9b3e51d1738905fbd4c98d" - url: "https://pub.dev" - source: hosted - version: "93.0.0" - _flutterfire_internals: - dependency: transitive - description: - name: _flutterfire_internals - sha256: ff0a84a2734d9e1089f8aedd5c0af0061b82fb94e95260d943404e0ef2134b11 - url: "https://pub.dev" - source: hosted - version: "1.3.59" - analyzer: - dependency: transitive - description: - name: analyzer - sha256: de7148ed2fcec579b19f122c1800933dfa028f6d9fd38a152b04b1516cec120b - url: "https://pub.dev" - source: hosted - version: "10.0.1" - args: - dependency: transitive - description: - name: args - sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 - url: "https://pub.dev" - source: hosted - version: "2.7.0" - async: - dependency: transitive - description: - name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" - url: "https://pub.dev" - source: hosted - version: "2.13.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - build: - dependency: transitive - description: - name: build - sha256: "275bf6bb2a00a9852c28d4e0b410da1d833a734d57d39d44f94bfc895a484ec3" - url: "https://pub.dev" - source: hosted - version: "4.0.4" - build_config: - dependency: transitive - description: - name: build_config - sha256: "4070d2a59f8eec34c97c86ceb44403834899075f66e8a9d59706f8e7834f6f71" - url: "https://pub.dev" - source: hosted - version: "1.3.0" - build_daemon: - dependency: transitive - description: - name: build_daemon - sha256: bf05f6e12cfea92d3c09308d7bcdab1906cd8a179b023269eed00c071004b957 - url: "https://pub.dev" - source: hosted - version: "4.1.1" - build_runner: - dependency: "direct dev" - description: - name: build_runner - sha256: "7981eb922842c77033026eb4341d5af651562008cdb116bdfa31fc46516b6462" - url: "https://pub.dev" - source: hosted - version: "2.12.2" - built_collection: - dependency: transitive - description: - name: built_collection - sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" - url: "https://pub.dev" - source: hosted - version: "5.1.1" - built_value: - dependency: transitive - description: - name: built_value - sha256: "6ae8a6435a8c6520c7077b107e77f1fb4ba7009633259a4d49a8afd8e7efc5e9" - url: "https://pub.dev" - source: hosted - version: "8.12.4" - cached_network_image: - dependency: "direct main" - description: - name: cached_network_image - sha256: "7c1183e361e5c8b0a0f21a28401eecdbde252441106a9816400dd4c2b2424916" - url: "https://pub.dev" - source: hosted - version: "3.4.1" - cached_network_image_platform_interface: - dependency: transitive - description: - name: cached_network_image_platform_interface - sha256: "35814b016e37fbdc91f7ae18c8caf49ba5c88501813f73ce8a07027a395e2829" - url: "https://pub.dev" - source: hosted - version: "4.1.1" - cached_network_image_web: - dependency: transitive - description: - name: cached_network_image_web - sha256: "980842f4e8e2535b8dbd3d5ca0b1f0ba66bf61d14cc3a17a9b4788a3685ba062" - url: "https://pub.dev" - source: hosted - version: "1.3.1" - characters: - dependency: transitive - description: - name: characters - sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b - url: "https://pub.dev" - source: hosted - version: "1.4.1" - checked_yaml: - dependency: transitive - description: - name: checked_yaml - sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f" - url: "https://pub.dev" - source: hosted - version: "2.0.4" - clock: - dependency: transitive - description: - name: clock - sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b - url: "https://pub.dev" - source: hosted - version: "1.1.2" - code_assets: - dependency: transitive - description: - name: code_assets - sha256: "83ccdaa064c980b5596c35dd64a8d3ecc68620174ab9b90b6343b753aa721687" - url: "https://pub.dev" - source: hosted - version: "1.0.0" - code_builder: - dependency: transitive - description: - name: code_builder - sha256: "6a6cab2ba4680d6423f34a9b972a4c9a94ebe1b62ecec4e1a1f2cba91fd1319d" - url: "https://pub.dev" - source: hosted - version: "4.11.1" - collection: - dependency: transitive - description: - name: collection - sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" - url: "https://pub.dev" - source: hosted - version: "1.19.1" - convert: - dependency: transitive - description: - name: convert - sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 - url: "https://pub.dev" - source: hosted - version: "3.1.2" - crypto: - dependency: transitive - description: - name: crypto - sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf - url: "https://pub.dev" - source: hosted - version: "3.0.7" - cupertino_icons: - dependency: "direct main" - description: - name: cupertino_icons - sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 - url: "https://pub.dev" - source: hosted - version: "1.0.8" - dart_style: - dependency: transitive - description: - name: dart_style - sha256: "29f7ecc274a86d32920b1d9cfc7502fa87220da41ec60b55f329559d5732e2b2" - url: "https://pub.dev" - source: hosted - version: "3.1.7" - dio: - dependency: "direct main" - description: - name: dio - sha256: aff32c08f92787a557dd5c0145ac91536481831a01b4648136373cddb0e64f8c - url: "https://pub.dev" - source: hosted - version: "5.9.2" - dio_web_adapter: - dependency: transitive - description: - name: dio_web_adapter - sha256: "2f9e64323a7c3c7ef69567d5c800424a11f8337b8b228bad02524c9fb3c1f340" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - fake_async: - dependency: transitive - description: - name: fake_async - sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" - url: "https://pub.dev" - source: hosted - version: "1.3.3" - ffi: - dependency: transitive - description: - name: ffi - sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45" - url: "https://pub.dev" - source: hosted - version: "2.2.0" - file: - dependency: transitive - description: - name: file - sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 - url: "https://pub.dev" - source: hosted - version: "7.0.1" - firebase_core: - dependency: "direct main" - description: - name: firebase_core - sha256: "7be63a3f841fc9663342f7f3a011a42aef6a61066943c90b1c434d79d5c995c5" - url: "https://pub.dev" - source: hosted - version: "3.15.2" - firebase_core_platform_interface: - dependency: transitive - description: - name: firebase_core_platform_interface - sha256: cccb4f572325dc14904c02fcc7db6323ad62ba02536833dddb5c02cac7341c64 - url: "https://pub.dev" - source: hosted - version: "6.0.2" - firebase_core_web: - dependency: transitive - description: - name: firebase_core_web - sha256: "0ed0dc292e8f9ac50992e2394e9d336a0275b6ae400d64163fdf0a8a8b556c37" - url: "https://pub.dev" - source: hosted - version: "2.24.1" - firebase_messaging: - dependency: "direct main" - description: - name: firebase_messaging - sha256: "60be38574f8b5658e2f22b7e311ff2064bea835c248424a383783464e8e02fcc" - url: "https://pub.dev" - source: hosted - version: "15.2.10" - firebase_messaging_platform_interface: - dependency: transitive - description: - name: firebase_messaging_platform_interface - sha256: "685e1771b3d1f9c8502771ccc9f91485b376ffe16d553533f335b9183ea99754" - url: "https://pub.dev" - source: hosted - version: "4.6.10" - firebase_messaging_web: - dependency: transitive - description: - name: firebase_messaging_web - sha256: "0d1be17bc89ed3ff5001789c92df678b2e963a51b6fa2bdb467532cc9dbed390" - url: "https://pub.dev" - source: hosted - version: "3.10.10" - fixnum: - dependency: transitive - description: - name: fixnum - sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be - url: "https://pub.dev" - source: hosted - version: "1.1.1" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_cache_manager: - dependency: transitive - description: - name: flutter_cache_manager - sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386" - url: "https://pub.dev" - source: hosted - version: "3.4.1" - flutter_lints: - dependency: "direct dev" - description: - name: flutter_lints - sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" - url: "https://pub.dev" - source: hosted - version: "4.0.0" - flutter_riverpod: - dependency: "direct main" - description: - name: flutter_riverpod - sha256: "9532ee6db4a943a1ed8383072a2e3eeda041db5657cdf6d2acecf3c21ecbe7e1" - url: "https://pub.dev" - source: hosted - version: "2.6.1" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - flutter_web_plugins: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - glob: - dependency: transitive - description: - name: glob - sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de - url: "https://pub.dev" - source: hosted - version: "2.1.3" - go_router: - dependency: "direct main" - description: - name: go_router - sha256: f02fd7d2a4dc512fec615529824fdd217fecb3a3d3de68360293a551f21634b3 - url: "https://pub.dev" - source: hosted - version: "14.8.1" - graphs: - dependency: transitive - description: - name: graphs - sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" - url: "https://pub.dev" - source: hosted - version: "2.3.2" - hooks: - dependency: transitive - description: - name: hooks - sha256: e79ed1e8e1929bc6ecb6ec85f0cb519c887aa5b423705ded0d0f2d9226def388 - url: "https://pub.dev" - source: hosted - version: "1.0.2" - http: - dependency: transitive - description: - name: http - sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" - url: "https://pub.dev" - source: hosted - version: "1.6.0" - http_multi_server: - dependency: transitive - description: - name: http_multi_server - sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 - url: "https://pub.dev" - source: hosted - version: "3.2.2" - http_parser: - dependency: transitive - description: - name: http_parser - sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" - url: "https://pub.dev" - source: hosted - version: "4.1.2" - intl: - dependency: "direct main" - description: - name: intl - sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf - url: "https://pub.dev" - source: hosted - version: "0.19.0" - io: - dependency: transitive - description: - name: io - sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b - url: "https://pub.dev" - source: hosted - version: "1.0.5" - json_annotation: - dependency: "direct main" - description: - name: json_annotation - sha256: cb09e7dac6210041fad964ed7fbee004f14258b4eca4040f72d1234062ace4c8 - url: "https://pub.dev" - source: hosted - version: "4.11.0" - json_serializable: - dependency: "direct dev" - description: - name: json_serializable - sha256: "44729f5c45748e6748f6b9a57ab8f7e4336edc8ae41fc295070e3814e616a6c0" - url: "https://pub.dev" - source: hosted - version: "6.13.0" - leak_tracker: - dependency: transitive - description: - name: leak_tracker - sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" - url: "https://pub.dev" - source: hosted - version: "11.0.2" - leak_tracker_flutter_testing: - dependency: transitive - description: - name: leak_tracker_flutter_testing - sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" - url: "https://pub.dev" - source: hosted - version: "3.0.10" - leak_tracker_testing: - dependency: transitive - description: - name: leak_tracker_testing - sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" - url: "https://pub.dev" - source: hosted - version: "3.0.2" - lints: - dependency: transitive - description: - name: lints - sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" - url: "https://pub.dev" - source: hosted - version: "4.0.0" - logging: - dependency: transitive - description: - name: logging - sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 - url: "https://pub.dev" - source: hosted - version: "1.3.0" - matcher: - dependency: transitive - description: - name: matcher - sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 - url: "https://pub.dev" - source: hosted - version: "0.12.19" - material_color_utilities: - dependency: transitive - description: - name: material_color_utilities - sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" - url: "https://pub.dev" - source: hosted - version: "0.13.0" - meta: - dependency: transitive - description: - name: meta - sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" - url: "https://pub.dev" - source: hosted - version: "1.17.0" - mime: - dependency: transitive - description: - name: mime - sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" - url: "https://pub.dev" - source: hosted - version: "2.0.0" - native_toolchain_c: - dependency: transitive - description: - name: native_toolchain_c - sha256: "92b2ca62c8bd2b8d2f267cdfccf9bfbdb7322f778f8f91b3ce5b5cda23a3899f" - url: "https://pub.dev" - source: hosted - version: "0.17.5" - objective_c: - dependency: transitive - description: - name: objective_c - sha256: "100a1c87616ab6ed41ec263b083c0ef3261ee6cd1dc3b0f35f8ddfa4f996fe52" - url: "https://pub.dev" - source: hosted - version: "9.3.0" - octo_image: - dependency: transitive - description: - name: octo_image - sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - package_config: - dependency: transitive - description: - name: package_config - sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc - url: "https://pub.dev" - source: hosted - version: "2.2.0" - path: - dependency: transitive - description: - name: path - sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" - url: "https://pub.dev" - source: hosted - version: "1.9.1" - path_provider: - dependency: transitive - description: - name: path_provider - sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" - url: "https://pub.dev" - source: hosted - version: "2.1.5" - path_provider_android: - dependency: transitive - description: - name: path_provider_android - sha256: f2c65e21139ce2c3dad46922be8272bb5963516045659e71bb16e151c93b580e - url: "https://pub.dev" - source: hosted - version: "2.2.22" - path_provider_foundation: - dependency: transitive - description: - name: path_provider_foundation - sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699" - url: "https://pub.dev" - source: hosted - version: "2.6.0" - path_provider_linux: - dependency: transitive - description: - name: path_provider_linux - sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 - url: "https://pub.dev" - source: hosted - version: "2.2.1" - path_provider_platform_interface: - dependency: transitive - description: - name: path_provider_platform_interface - sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - path_provider_windows: - dependency: transitive - description: - name: path_provider_windows - sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 - url: "https://pub.dev" - source: hosted - version: "2.3.0" - platform: - dependency: transitive - description: - name: platform - sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" - url: "https://pub.dev" - source: hosted - version: "3.1.6" - plugin_platform_interface: - dependency: transitive - description: - name: plugin_platform_interface - sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" - url: "https://pub.dev" - source: hosted - version: "2.1.8" - pool: - dependency: transitive - description: - name: pool - sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d" - url: "https://pub.dev" - source: hosted - version: "1.5.2" - pub_semver: - dependency: transitive - description: - name: pub_semver - sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" - url: "https://pub.dev" - source: hosted - version: "2.2.0" - pubspec_parse: - dependency: transitive - description: - name: pubspec_parse - sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082" - url: "https://pub.dev" - source: hosted - version: "1.5.0" - riverpod: - dependency: transitive - description: - name: riverpod - sha256: "59062512288d3056b2321804332a13ffdd1bf16df70dcc8e506e411280a72959" - url: "https://pub.dev" - source: hosted - version: "2.6.1" - rxdart: - dependency: transitive - description: - name: rxdart - sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" - url: "https://pub.dev" - source: hosted - version: "0.28.0" - shared_preferences: - dependency: "direct main" - description: - name: shared_preferences - sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64" - url: "https://pub.dev" - source: hosted - version: "2.5.4" - shared_preferences_android: - dependency: transitive - description: - name: shared_preferences_android - sha256: "8374d6200ab33ac99031a852eba4c8eb2170c4bf20778b3e2c9eccb45384fb41" - url: "https://pub.dev" - source: hosted - version: "2.4.21" - shared_preferences_foundation: - dependency: transitive - description: - name: shared_preferences_foundation - sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f" - url: "https://pub.dev" - source: hosted - version: "2.5.6" - shared_preferences_linux: - dependency: transitive - description: - name: shared_preferences_linux - sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" - url: "https://pub.dev" - source: hosted - version: "2.4.1" - shared_preferences_platform_interface: - dependency: transitive - description: - name: shared_preferences_platform_interface - sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" - url: "https://pub.dev" - source: hosted - version: "2.4.1" - shared_preferences_web: - dependency: transitive - description: - name: shared_preferences_web - sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 - url: "https://pub.dev" - source: hosted - version: "2.4.3" - shared_preferences_windows: - dependency: transitive - description: - name: shared_preferences_windows - sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" - url: "https://pub.dev" - source: hosted - version: "2.4.1" - shelf: - dependency: transitive - description: - name: shelf - sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 - url: "https://pub.dev" - source: hosted - version: "1.4.2" - shelf_web_socket: - dependency: transitive - description: - name: shelf_web_socket - sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" - url: "https://pub.dev" - source: hosted - version: "3.0.0" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - source_gen: - dependency: transitive - description: - name: source_gen - sha256: "1d562a3c1f713904ebbed50d2760217fd8a51ca170ac4b05b0db490699dbac17" - url: "https://pub.dev" - source: hosted - version: "4.2.0" - source_helper: - dependency: transitive - description: - name: source_helper - sha256: "4a85e90b50694e652075cbe4575665539d253e6ec10e46e76b45368ab5e3caae" - url: "https://pub.dev" - source: hosted - version: "1.3.10" - source_span: - dependency: transitive - description: - name: source_span - sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" - url: "https://pub.dev" - source: hosted - version: "1.10.2" - sqflite: - dependency: transitive - description: - name: sqflite - sha256: e2297b1da52f127bc7a3da11439985d9b536f75070f3325e62ada69a5c585d03 - url: "https://pub.dev" - source: hosted - version: "2.4.2" - sqflite_android: - dependency: transitive - description: - name: sqflite_android - sha256: "881e28efdcc9950fd8e9bb42713dcf1103e62a2e7168f23c9338d82db13dec40" - url: "https://pub.dev" - source: hosted - version: "2.4.2+3" - sqflite_common: - dependency: transitive - description: - name: sqflite_common - sha256: "6ef422a4525ecc601db6c0a2233ff448c731307906e92cabc9ba292afaae16a6" - url: "https://pub.dev" - source: hosted - version: "2.5.6" - sqflite_darwin: - dependency: transitive - description: - name: sqflite_darwin - sha256: "279832e5cde3fe99e8571879498c9211f3ca6391b0d818df4e17d9fff5c6ccb3" - url: "https://pub.dev" - source: hosted - version: "2.4.2" - sqflite_platform_interface: - dependency: transitive - description: - name: sqflite_platform_interface - sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920" - url: "https://pub.dev" - source: hosted - version: "2.4.0" - stack_trace: - dependency: transitive - description: - name: stack_trace - sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" - url: "https://pub.dev" - source: hosted - version: "1.12.1" - state_notifier: - dependency: transitive - description: - name: state_notifier - sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb - url: "https://pub.dev" - source: hosted - version: "1.0.0" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - stream_transform: - dependency: transitive - description: - name: stream_transform - sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 - url: "https://pub.dev" - source: hosted - version: "2.1.1" - string_scanner: - dependency: transitive - description: - name: string_scanner - sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" - url: "https://pub.dev" - source: hosted - version: "1.4.1" - synchronized: - dependency: transitive - description: - name: synchronized - sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0 - url: "https://pub.dev" - source: hosted - version: "3.4.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" - url: "https://pub.dev" - source: hosted - version: "1.2.2" - test_api: - dependency: transitive - description: - name: test_api - sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" - url: "https://pub.dev" - source: hosted - version: "0.7.10" - typed_data: - dependency: transitive - description: - name: typed_data - sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 - url: "https://pub.dev" - source: hosted - version: "1.4.0" - uuid: - dependency: transitive - description: - name: uuid - sha256: "1fef9e8e11e2991bb773070d4656b7bd5d850967a2456cfc83cf47925ba79489" - url: "https://pub.dev" - source: hosted - version: "4.5.3" - vector_math: - dependency: transitive - description: - name: vector_math - sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b - url: "https://pub.dev" - source: hosted - version: "2.2.0" - vm_service: - dependency: transitive - description: - name: vm_service - sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" - url: "https://pub.dev" - source: hosted - version: "15.0.2" - watcher: - dependency: transitive - description: - name: watcher - sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635" - url: "https://pub.dev" - source: hosted - version: "1.2.1" - web: - dependency: transitive - description: - name: web - sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" - url: "https://pub.dev" - source: hosted - version: "1.1.1" - web_socket: - dependency: transitive - description: - name: web_socket - sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" - url: "https://pub.dev" - source: hosted - version: "1.0.1" - web_socket_channel: - dependency: transitive - description: - name: web_socket_channel - sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 - url: "https://pub.dev" - source: hosted - version: "3.0.3" - xdg_directories: - dependency: transitive - description: - name: xdg_directories - sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" - url: "https://pub.dev" - source: hosted - version: "1.1.0" - yaml: - dependency: transitive - description: - name: yaml - sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce - url: "https://pub.dev" - source: hosted - version: "3.1.3" -sdks: - dart: ">=3.10.3 <4.0.0" - flutter: ">=3.38.4" diff --git a/ontime_merchant_flutter/pubspec.yaml b/ontime_merchant_flutter/pubspec.yaml deleted file mode 100644 index 4d7ccf6..0000000 --- a/ontime_merchant_flutter/pubspec.yaml +++ /dev/null @@ -1,35 +0,0 @@ -name: ontime_merchant_flutter -description: Ontime merchant Flutter app (MVP) consuming backendpanel Merchant API. -version: 0.1.0+1 - -environment: - sdk: ">=3.3.0 <4.0.0" - -dependencies: - flutter: - sdk: flutter - - cupertino_icons: ^1.0.8 - - dio: ^5.7.0 - json_annotation: ^4.9.0 - flutter_riverpod: ^2.6.1 - go_router: ^14.3.0 - shared_preferences: ^2.3.3 - firebase_core: ^3.6.0 - firebase_messaging: ^15.1.0 - intl: ^0.19.0 - cached_network_image: ^3.4.1 - -dev_dependencies: - flutter_test: - sdk: flutter - flutter_lints: ^4.0.0 - build_runner: ^2.4.13 - json_serializable: ^6.9.0 - -flutter: - uses-material-design: true - assets: - - assets/images/ - diff --git a/ontime_merchant_flutter/test/widget_test.dart b/ontime_merchant_flutter/test/widget_test.dart deleted file mode 100644 index 401a811..0000000 --- a/ontime_merchant_flutter/test/widget_test.dart +++ /dev/null @@ -1,30 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility in the flutter_test package. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:ontime_merchant_flutter/main.dart'; - -void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - }); -} diff --git a/ontime_user_flutter/README.md b/ontime_user_flutter/README.md deleted file mode 100644 index 3eb3d79..0000000 --- a/ontime_user_flutter/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# Ontime User (Flutter) - -Flutter app for Ontime **user** (pelanggan), using backend API at `https://apitest.semestaterpadu.my.id/api/`. - -## Setup - -- Flutter SDK (e.g. 3.19+) -- Android: run `flutter create . --platforms=android` if the project was scaffolded without platform folders. -- API uses HTTP Basic Auth; configure in backendpanel if required. - -## Run / Build - -```bash -flutter pub get -flutter run -# or -flutter build apk --release -``` - -APK output: `build/app/outputs/flutter-apk/app-release.apk`. - -## Firebase (FCM) - -To enable push notifications: - -1. Add `google-services.json` to `android/app/`. -2. Ensure `android/build.gradle` and `android/app/build.gradle` apply the Google services plugin. - -If Firebase is not configured, the app still runs; FCM token is sent to the backend on login when available. - -## Validation - -- **Login**: `Pelanggan/login` (email or no_telepon + password, optional token). -- **Home**: `Pelanggan/home` (slider, fitur, merchant nearby). -- Compare with native app `OnTime_User_live` and backend responses for parity. diff --git a/ontime_user_flutter/assets/images/.gitkeep b/ontime_user_flutter/assets/images/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/ontime_user_flutter/lib/core/app_config.dart b/ontime_user_flutter/lib/core/app_config.dart deleted file mode 100644 index aa21dc2..0000000 --- a/ontime_user_flutter/lib/core/app_config.dart +++ /dev/null @@ -1,18 +0,0 @@ -class AppConfig { - /// Base URL for the Ontime test backend. - static const String baseUrl = 'https://apitest.semestaterpadu.my.id/'; - - /// API root (`/api/`). - static const String apiBase = '${baseUrl}api/'; - - /// Image base URLs (kept consistent with Java Constants). - static const String imagesFitur = '${baseUrl}images/fitur/'; - static const String imagesMerchant = '${baseUrl}images/merchant/'; - static const String imagesBank = '${baseUrl}images/bank/'; - static const String imagesItem = '${baseUrl}images/itemmerchant/'; - static const String imagesBerita = '${baseUrl}images/berita/'; - static const String imagesSlider = '${baseUrl}images/promo/'; - static const String imagesDriver = '${baseUrl}images/fotodriver/'; - static const String imagesUser = '${baseUrl}images/pelanggan/'; -} - diff --git a/ontime_user_flutter/lib/core/app_router.dart b/ontime_user_flutter/lib/core/app_router.dart deleted file mode 100644 index eabac37..0000000 --- a/ontime_user_flutter/lib/core/app_router.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; -import 'package:ontime_user_flutter/features/auth/presentation/login_screen.dart'; -import 'package:ontime_user_flutter/features/home/presentation/home_shell.dart'; - -final GlobalKey _rootNavigatorKey = - GlobalKey(debugLabel: 'rootNavigator'); - -GoRouter createAppRouter() { - return GoRouter( - navigatorKey: _rootNavigatorKey, - initialLocation: '/auth/login', - routes: [ - GoRoute( - path: '/auth/login', - name: 'login', - pageBuilder: (context, state) => - const MaterialPage(child: LoginScreen()), - ), - GoRoute( - path: '/home', - name: 'home', - pageBuilder: (context, state) => - const MaterialPage(child: HomeShell()), - ), - ], - ); -} - diff --git a/ontime_user_flutter/lib/core/app_theme.dart b/ontime_user_flutter/lib/core/app_theme.dart deleted file mode 100644 index 118c26b..0000000 --- a/ontime_user_flutter/lib/core/app_theme.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:flutter/material.dart'; - -class AppTheme { - static ThemeData light() { - final base = ThemeData.light(useMaterial3: true); - return base.copyWith( - colorScheme: ColorScheme.fromSeed( - seedColor: const Color(0xFF00AEEF), - ), - scaffoldBackgroundColor: Colors.grey[50], - appBarTheme: const AppBarTheme( - elevation: 0, - centerTitle: true, - ), - inputDecorationTheme: const InputDecorationTheme( - border: OutlineInputBorder(), - ), - ); - } -} - diff --git a/ontime_user_flutter/lib/core/fcm_service.dart b/ontime_user_flutter/lib/core/fcm_service.dart deleted file mode 100644 index 6d03fcc..0000000 --- a/ontime_user_flutter/lib/core/fcm_service.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:firebase_core/firebase_core.dart'; -import 'package:firebase_messaging/firebase_messaging.dart'; - -/// FCM token for backend (Pelanggan uses `token` or `reg_id`). -/// Initialize in main(), then pass token to login. -class FcmService { - static bool _initialized = false; - - static Future init() async { - if (_initialized) return; - try { - await Firebase.initializeApp(); - _initialized = true; - } catch (_) { - // No Firebase config (e.g. missing google-services.json) - } - } - - static Future getToken() async { - if (!_initialized) return null; - try { - final token = await FirebaseMessaging.instance.getToken(); - return token; - } catch (_) { - return null; - } - } - - static void setBackgroundHandler(Future Function(RemoteMessage) handler) { - FirebaseMessaging.onBackgroundMessage(handler); - } -} diff --git a/ontime_user_flutter/lib/data/api/api_client.dart b/ontime_user_flutter/lib/data/api/api_client.dart deleted file mode 100644 index c4c423a..0000000 --- a/ontime_user_flutter/lib/data/api/api_client.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:dio/dio.dart'; -import 'package:ontime_user_flutter/core/app_config.dart'; - -class ApiClient { - ApiClient({ - required String? basicAuthUser, - required String? basicAuthPassword, - }) : _dio = Dio( - BaseOptions( - baseUrl: AppConfig.apiBase, - connectTimeout: const Duration(seconds: 15), - receiveTimeout: const Duration(seconds: 20), - ), - ) { - if (basicAuthUser != null && basicAuthPassword != null) { - final auth = '$basicAuthUser:$basicAuthPassword'; - final encoded = String.fromCharCodes(auth.codeUnits); - _dio.options.headers['Authorization'] = 'Basic $encoded'; - } - } - - final Dio _dio; - - Dio get raw => _dio; -} - diff --git a/ontime_user_flutter/lib/features/auth/application/auth_controller.dart b/ontime_user_flutter/lib/features/auth/application/auth_controller.dart deleted file mode 100644 index f6ab4c9..0000000 --- a/ontime_user_flutter/lib/features/auth/application/auth_controller.dart +++ /dev/null @@ -1,71 +0,0 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:ontime_user_flutter/data/api/api_client.dart'; -import 'package:ontime_user_flutter/features/auth/data/auth_api.dart'; -import 'package:ontime_user_flutter/features/auth/data/models/user_model.dart'; -import 'package:shared_preferences/shared_preferences.dart'; - -class AuthState { - const AuthState({ - this.user, - this.isLoading = false, - this.errorMessage, - }); - - final UserModel? user; - final bool isLoading; - final String? errorMessage; - - AuthState copyWith({ - UserModel? user, - bool? isLoading, - String? errorMessage, - }) { - return AuthState( - user: user ?? this.user, - isLoading: isLoading ?? this.isLoading, - errorMessage: errorMessage, - ); - } -} - -final authControllerProvider = - StateNotifierProvider((ref) { - return AuthController(); -}); - -class AuthController extends StateNotifier { - AuthController() : super(const AuthState()); - - late final AuthApi _api = - AuthApi(ApiClient(basicAuthUser: null, basicAuthPassword: null)); - - Future login({ - String? phone, - String? email, - required String password, - String? fcmToken, - }) async { - state = state.copyWith(isLoading: true, errorMessage: null); - try { - final user = await _api.login( - phone: phone, - email: email, - password: password, - fcmToken: fcmToken, - ); - state = state.copyWith(user: user, isLoading: false); - - final prefs = await SharedPreferences.getInstance(); - await prefs.setString('user_id', user.id); - await prefs.setString('user_token', user.token); - await prefs.setString('user_phone', user.phone); - await prefs.setString('user_email', user.email); - } catch (e) { - state = state.copyWith( - isLoading: false, - errorMessage: e.toString(), - ); - } - } -} - diff --git a/ontime_user_flutter/lib/features/auth/data/auth_api.dart b/ontime_user_flutter/lib/features/auth/data/auth_api.dart deleted file mode 100644 index fd96373..0000000 --- a/ontime_user_flutter/lib/features/auth/data/auth_api.dart +++ /dev/null @@ -1,47 +0,0 @@ -import 'package:dio/dio.dart'; -import 'package:ontime_user_flutter/data/api/api_client.dart'; -import 'package:ontime_user_flutter/features/auth/data/models/user_model.dart'; - -class AuthApi { - AuthApi(this._client); - - final ApiClient _client; - - Future login({ - String? phone, - String? email, - required String password, - String? fcmToken, - }) async { - final payload = { - 'password': password, - }; - if (phone != null && phone.isNotEmpty) { - payload['no_telepon'] = phone; - } - if (email != null && email.isNotEmpty) { - payload['email'] = email; - } - if (fcmToken != null && fcmToken.isNotEmpty) { - payload['token'] = fcmToken; - } - - final Response response = - await _client.raw.post('Pelanggan/login', data: payload); - - final data = response.data is Map - ? response.data as Map - : {}; - - if (data['code']?.toString() != '200') { - throw Exception(data['message'] ?? 'Login failed'); - } - - final List list = data['data'] as List? ?? []; - if (list.isEmpty) { - throw Exception('User data not found in response'); - } - return UserModel.fromJson(list.first as Map); - } -} - diff --git a/ontime_user_flutter/lib/features/auth/data/models/user_model.dart b/ontime_user_flutter/lib/features/auth/data/models/user_model.dart deleted file mode 100644 index 1a4d4ab..0000000 --- a/ontime_user_flutter/lib/features/auth/data/models/user_model.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:json_annotation/json_annotation.dart'; - -part 'user_model.g.dart'; - -@JsonSerializable() -class UserModel { - UserModel({ - required this.id, - required this.fullName, - required this.email, - required this.phone, - required this.token, - }); - - @JsonKey(name: 'id') - final String id; - - @JsonKey(name: 'fullnama') - final String fullName; - - @JsonKey(name: 'email') - final String email; - - @JsonKey(name: 'no_telepon') - final String phone; - - @JsonKey(name: 'token') - final String token; - - factory UserModel.fromJson(Map json) => - _$UserModelFromJson(json); - - Map toJson() => _$UserModelToJson(this); -} - diff --git a/ontime_user_flutter/lib/features/auth/data/models/user_model.g.dart b/ontime_user_flutter/lib/features/auth/data/models/user_model.g.dart deleted file mode 100644 index 8324605..0000000 --- a/ontime_user_flutter/lib/features/auth/data/models/user_model.g.dart +++ /dev/null @@ -1,22 +0,0 @@ -// GENERATED CODE - placeholder for build_runner. You can regenerate. - -part of 'user_model.dart'; - -UserModel _$UserModelFromJson(Map json) { - return UserModel( - id: json['id'] as String? ?? '', - fullName: json['fullnama'] as String? ?? '', - email: json['email'] as String? ?? '', - phone: json['no_telepon'] as String? ?? '', - token: json['token'] as String? ?? '', - ); -} - -Map _$UserModelToJson(UserModel instance) => { - 'id': instance.id, - 'fullnama': instance.fullName, - 'email': instance.email, - 'no_telepon': instance.phone, - 'token': instance.token, - }; - diff --git a/ontime_user_flutter/lib/features/auth/presentation/login_screen.dart b/ontime_user_flutter/lib/features/auth/presentation/login_screen.dart deleted file mode 100644 index cc2206a..0000000 --- a/ontime_user_flutter/lib/features/auth/presentation/login_screen.dart +++ /dev/null @@ -1,122 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:go_router/go_router.dart'; -import 'package:ontime_user_flutter/core/fcm_service.dart'; -import 'package:ontime_user_flutter/features/auth/application/auth_controller.dart'; - -class LoginScreen extends ConsumerStatefulWidget { - const LoginScreen({super.key}); - - @override - ConsumerState createState() => _LoginScreenState(); -} - -class _LoginScreenState extends ConsumerState { - final _formKey = GlobalKey(); - final _emailController = TextEditingController(); - final _phoneController = TextEditingController(); - final _passwordController = TextEditingController(); - - @override - void dispose() { - _emailController.dispose(); - _phoneController.dispose(); - _passwordController.dispose(); - super.dispose(); - } - - Future _submit() async { - if (!_formKey.currentState!.validate()) return; - - final fcmToken = await FcmService.getToken(); - final auth = ref.read(authControllerProvider.notifier); - await auth.login( - email: _emailController.text.trim().isEmpty - ? null - : _emailController.text.trim(), - phone: _phoneController.text.trim().isEmpty - ? null - : _phoneController.text.trim(), - password: _passwordController.text, - fcmToken: fcmToken, - ); - - final state = ref.read(authControllerProvider); - if (state.user != null) { - if (mounted) { - context.go('/home'); - } - } - } - - @override - Widget build(BuildContext context) { - final state = ref.watch(authControllerProvider); - return Scaffold( - appBar: AppBar( - title: const Text('Masuk'), - ), - body: SafeArea( - child: Padding( - padding: const EdgeInsets.all(16), - child: Form( - key: _formKey, - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - TextFormField( - controller: _emailController, - decoration: const InputDecoration( - labelText: 'Email', - ), - ), - const SizedBox(height: 12), - TextFormField( - controller: _phoneController, - decoration: const InputDecoration( - labelText: 'No. Telepon', - ), - keyboardType: TextInputType.phone, - ), - const SizedBox(height: 12), - TextFormField( - controller: _passwordController, - decoration: const InputDecoration( - labelText: 'Kata sandi', - ), - obscureText: true, - validator: (value) { - if (value == null || value.isEmpty) { - return 'Password wajib diisi'; - } - return null; - }, - ), - const SizedBox(height: 16), - if (state.errorMessage != null) - Text( - state.errorMessage!, - style: TextStyle( - color: Theme.of(context).colorScheme.error, - ), - ), - const Spacer(), - ElevatedButton( - onPressed: state.isLoading ? null : _submit, - child: state.isLoading - ? const SizedBox( - height: 20, - width: 20, - child: CircularProgressIndicator(strokeWidth: 2), - ) - : const Text('Masuk'), - ), - ], - ), - ), - ), - ), - ); - } -} - diff --git a/ontime_user_flutter/lib/features/home/presentation/home_shell.dart b/ontime_user_flutter/lib/features/home/presentation/home_shell.dart deleted file mode 100644 index 7599dbb..0000000 --- a/ontime_user_flutter/lib/features/home/presentation/home_shell.dart +++ /dev/null @@ -1,55 +0,0 @@ -import 'package:flutter/material.dart'; - -class HomeShell extends StatefulWidget { - const HomeShell({super.key}); - - @override - State createState() => _HomeShellState(); -} - -class _HomeShellState extends State { - int _index = 0; - - @override - Widget build(BuildContext context) { - final pages = [ - const Center(child: Text('Beranda (slider, fitur, merchant terdekat)')), - const Center(child: Text('Pesanan (aktif & riwayat)')), - const Center(child: Text('Dompet (saldo & topup/withdraw)')), - const Center(child: Text('Profil')), - ]; - - return Scaffold( - body: SafeArea( - child: pages[_index], - ), - bottomNavigationBar: NavigationBar( - selectedIndex: _index, - onDestinationSelected: (i) => setState(() => _index = i), - destinations: const [ - NavigationDestination( - icon: Icon(Icons.home_outlined), - selectedIcon: Icon(Icons.home), - label: 'Beranda', - ), - NavigationDestination( - icon: Icon(Icons.receipt_long_outlined), - selectedIcon: Icon(Icons.receipt_long), - label: 'Pesanan', - ), - NavigationDestination( - icon: Icon(Icons.account_balance_wallet_outlined), - selectedIcon: Icon(Icons.account_balance_wallet), - label: 'Dompet', - ), - NavigationDestination( - icon: Icon(Icons.person_outline), - selectedIcon: Icon(Icons.person), - label: 'Profil', - ), - ], - ), - ); - } -} - diff --git a/ontime_user_flutter/lib/main.dart b/ontime_user_flutter/lib/main.dart deleted file mode 100644 index 3e73188..0000000 --- a/ontime_user_flutter/lib/main.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import 'core/app_router.dart'; -import 'core/app_theme.dart'; -import 'core/fcm_service.dart'; - -void main() async { - WidgetsFlutterBinding.ensureInitialized(); - await FcmService.init(); - final router = createAppRouter(); - runApp(ProviderScope(child: OntimeUserApp(router: router))); -} - -class OntimeUserApp extends StatelessWidget { - const OntimeUserApp({super.key, required this.router}); - - final Object router; - - @override - Widget build(BuildContext context) { - return MaterialApp.router( - debugShowCheckedModeBanner: false, - title: 'Ontime User', - theme: AppTheme.light(), - routerConfig: router as dynamic, - ); - } -} - diff --git a/ontime_user_flutter/pubspec.lock b/ontime_user_flutter/pubspec.lock deleted file mode 100644 index 40dd2bb..0000000 --- a/ontime_user_flutter/pubspec.lock +++ /dev/null @@ -1,1106 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - _fe_analyzer_shared: - dependency: transitive - description: - name: _fe_analyzer_shared - sha256: "8d7ff3948166b8ec5da0fbb5962000926b8e02f2ed9b3e51d1738905fbd4c98d" - url: "https://pub.dev" - source: hosted - version: "93.0.0" - _flutterfire_internals: - dependency: transitive - description: - name: _flutterfire_internals - sha256: ff0a84a2734d9e1089f8aedd5c0af0061b82fb94e95260d943404e0ef2134b11 - url: "https://pub.dev" - source: hosted - version: "1.3.59" - analyzer: - dependency: transitive - description: - name: analyzer - sha256: de7148ed2fcec579b19f122c1800933dfa028f6d9fd38a152b04b1516cec120b - url: "https://pub.dev" - source: hosted - version: "10.0.1" - args: - dependency: transitive - description: - name: args - sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 - url: "https://pub.dev" - source: hosted - version: "2.7.0" - async: - dependency: transitive - description: - name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" - url: "https://pub.dev" - source: hosted - version: "2.13.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - build: - dependency: transitive - description: - name: build - sha256: "275bf6bb2a00a9852c28d4e0b410da1d833a734d57d39d44f94bfc895a484ec3" - url: "https://pub.dev" - source: hosted - version: "4.0.4" - build_config: - dependency: transitive - description: - name: build_config - sha256: "4070d2a59f8eec34c97c86ceb44403834899075f66e8a9d59706f8e7834f6f71" - url: "https://pub.dev" - source: hosted - version: "1.3.0" - build_daemon: - dependency: transitive - description: - name: build_daemon - sha256: bf05f6e12cfea92d3c09308d7bcdab1906cd8a179b023269eed00c071004b957 - url: "https://pub.dev" - source: hosted - version: "4.1.1" - build_runner: - dependency: "direct dev" - description: - name: build_runner - sha256: "7981eb922842c77033026eb4341d5af651562008cdb116bdfa31fc46516b6462" - url: "https://pub.dev" - source: hosted - version: "2.12.2" - built_collection: - dependency: transitive - description: - name: built_collection - sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" - url: "https://pub.dev" - source: hosted - version: "5.1.1" - built_value: - dependency: transitive - description: - name: built_value - sha256: "6ae8a6435a8c6520c7077b107e77f1fb4ba7009633259a4d49a8afd8e7efc5e9" - url: "https://pub.dev" - source: hosted - version: "8.12.4" - cached_network_image: - dependency: "direct main" - description: - name: cached_network_image - sha256: "7c1183e361e5c8b0a0f21a28401eecdbde252441106a9816400dd4c2b2424916" - url: "https://pub.dev" - source: hosted - version: "3.4.1" - cached_network_image_platform_interface: - dependency: transitive - description: - name: cached_network_image_platform_interface - sha256: "35814b016e37fbdc91f7ae18c8caf49ba5c88501813f73ce8a07027a395e2829" - url: "https://pub.dev" - source: hosted - version: "4.1.1" - cached_network_image_web: - dependency: transitive - description: - name: cached_network_image_web - sha256: "980842f4e8e2535b8dbd3d5ca0b1f0ba66bf61d14cc3a17a9b4788a3685ba062" - url: "https://pub.dev" - source: hosted - version: "1.3.1" - characters: - dependency: transitive - description: - name: characters - sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b - url: "https://pub.dev" - source: hosted - version: "1.4.1" - checked_yaml: - dependency: transitive - description: - name: checked_yaml - sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f" - url: "https://pub.dev" - source: hosted - version: "2.0.4" - clock: - dependency: transitive - description: - name: clock - sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b - url: "https://pub.dev" - source: hosted - version: "1.1.2" - code_assets: - dependency: transitive - description: - name: code_assets - sha256: "83ccdaa064c980b5596c35dd64a8d3ecc68620174ab9b90b6343b753aa721687" - url: "https://pub.dev" - source: hosted - version: "1.0.0" - code_builder: - dependency: transitive - description: - name: code_builder - sha256: "6a6cab2ba4680d6423f34a9b972a4c9a94ebe1b62ecec4e1a1f2cba91fd1319d" - url: "https://pub.dev" - source: hosted - version: "4.11.1" - collection: - dependency: transitive - description: - name: collection - sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" - url: "https://pub.dev" - source: hosted - version: "1.19.1" - convert: - dependency: transitive - description: - name: convert - sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 - url: "https://pub.dev" - source: hosted - version: "3.1.2" - crypto: - dependency: transitive - description: - name: crypto - sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf - url: "https://pub.dev" - source: hosted - version: "3.0.7" - csslib: - dependency: transitive - description: - name: csslib - sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" - url: "https://pub.dev" - source: hosted - version: "1.0.2" - cupertino_icons: - dependency: "direct main" - description: - name: cupertino_icons - sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 - url: "https://pub.dev" - source: hosted - version: "1.0.8" - dart_style: - dependency: transitive - description: - name: dart_style - sha256: "29f7ecc274a86d32920b1d9cfc7502fa87220da41ec60b55f329559d5732e2b2" - url: "https://pub.dev" - source: hosted - version: "3.1.7" - dio: - dependency: "direct main" - description: - name: dio - sha256: aff32c08f92787a557dd5c0145ac91536481831a01b4648136373cddb0e64f8c - url: "https://pub.dev" - source: hosted - version: "5.9.2" - dio_web_adapter: - dependency: transitive - description: - name: dio_web_adapter - sha256: "2f9e64323a7c3c7ef69567d5c800424a11f8337b8b228bad02524c9fb3c1f340" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - fake_async: - dependency: transitive - description: - name: fake_async - sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" - url: "https://pub.dev" - source: hosted - version: "1.3.3" - ffi: - dependency: transitive - description: - name: ffi - sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45" - url: "https://pub.dev" - source: hosted - version: "2.2.0" - file: - dependency: transitive - description: - name: file - sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 - url: "https://pub.dev" - source: hosted - version: "7.0.1" - firebase_core: - dependency: "direct main" - description: - name: firebase_core - sha256: "7be63a3f841fc9663342f7f3a011a42aef6a61066943c90b1c434d79d5c995c5" - url: "https://pub.dev" - source: hosted - version: "3.15.2" - firebase_core_platform_interface: - dependency: transitive - description: - name: firebase_core_platform_interface - sha256: cccb4f572325dc14904c02fcc7db6323ad62ba02536833dddb5c02cac7341c64 - url: "https://pub.dev" - source: hosted - version: "6.0.2" - firebase_core_web: - dependency: transitive - description: - name: firebase_core_web - sha256: "0ed0dc292e8f9ac50992e2394e9d336a0275b6ae400d64163fdf0a8a8b556c37" - url: "https://pub.dev" - source: hosted - version: "2.24.1" - firebase_messaging: - dependency: "direct main" - description: - name: firebase_messaging - sha256: "60be38574f8b5658e2f22b7e311ff2064bea835c248424a383783464e8e02fcc" - url: "https://pub.dev" - source: hosted - version: "15.2.10" - firebase_messaging_platform_interface: - dependency: transitive - description: - name: firebase_messaging_platform_interface - sha256: "685e1771b3d1f9c8502771ccc9f91485b376ffe16d553533f335b9183ea99754" - url: "https://pub.dev" - source: hosted - version: "4.6.10" - firebase_messaging_web: - dependency: transitive - description: - name: firebase_messaging_web - sha256: "0d1be17bc89ed3ff5001789c92df678b2e963a51b6fa2bdb467532cc9dbed390" - url: "https://pub.dev" - source: hosted - version: "3.10.10" - fixnum: - dependency: transitive - description: - name: fixnum - sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be - url: "https://pub.dev" - source: hosted - version: "1.1.1" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_cache_manager: - dependency: transitive - description: - name: flutter_cache_manager - sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386" - url: "https://pub.dev" - source: hosted - version: "3.4.1" - flutter_lints: - dependency: "direct dev" - description: - name: flutter_lints - sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" - url: "https://pub.dev" - source: hosted - version: "4.0.0" - flutter_plugin_android_lifecycle: - dependency: transitive - description: - name: flutter_plugin_android_lifecycle - sha256: ee8068e0e1cd16c4a82714119918efdeed33b3ba7772c54b5d094ab53f9b7fd1 - url: "https://pub.dev" - source: hosted - version: "2.0.33" - flutter_riverpod: - dependency: "direct main" - description: - name: flutter_riverpod - sha256: "9532ee6db4a943a1ed8383072a2e3eeda041db5657cdf6d2acecf3c21ecbe7e1" - url: "https://pub.dev" - source: hosted - version: "2.6.1" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - flutter_web_plugins: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - geolocator: - dependency: "direct main" - description: - name: geolocator - sha256: f62bcd90459e63210bbf9c35deb6a51c521f992a78de19a1fe5c11704f9530e2 - url: "https://pub.dev" - source: hosted - version: "13.0.4" - geolocator_android: - dependency: transitive - description: - name: geolocator_android - sha256: fcb1760a50d7500deca37c9a666785c047139b5f9ee15aa5469fae7dbbe3170d - url: "https://pub.dev" - source: hosted - version: "4.6.2" - geolocator_apple: - dependency: transitive - description: - name: geolocator_apple - sha256: dbdd8789d5aaf14cf69f74d4925ad1336b4433a6efdf2fce91e8955dc921bf22 - url: "https://pub.dev" - source: hosted - version: "2.3.13" - geolocator_platform_interface: - dependency: transitive - description: - name: geolocator_platform_interface - sha256: "30cb64f0b9adcc0fb36f628b4ebf4f731a2961a0ebd849f4b56200205056fe67" - url: "https://pub.dev" - source: hosted - version: "4.2.6" - geolocator_web: - dependency: transitive - description: - name: geolocator_web - sha256: b1ae9bdfd90f861fde8fd4f209c37b953d65e92823cb73c7dee1fa021b06f172 - url: "https://pub.dev" - source: hosted - version: "4.1.3" - geolocator_windows: - dependency: transitive - description: - name: geolocator_windows - sha256: "175435404d20278ffd220de83c2ca293b73db95eafbdc8131fe8609be1421eb6" - url: "https://pub.dev" - source: hosted - version: "0.2.5" - glob: - dependency: transitive - description: - name: glob - sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de - url: "https://pub.dev" - source: hosted - version: "2.1.3" - go_router: - dependency: "direct main" - description: - name: go_router - sha256: f02fd7d2a4dc512fec615529824fdd217fecb3a3d3de68360293a551f21634b3 - url: "https://pub.dev" - source: hosted - version: "14.8.1" - google_maps: - dependency: transitive - description: - name: google_maps - sha256: "5d410c32112d7c6eb7858d359275b2aa04778eed3e36c745aeae905fb2fa6468" - url: "https://pub.dev" - source: hosted - version: "8.2.0" - google_maps_flutter: - dependency: "direct main" - description: - name: google_maps_flutter - sha256: "85339d0c9b5c7bac305df0bc1bc72a990f7f59b1846f048c96c022110d5d4a69" - url: "https://pub.dev" - source: hosted - version: "2.15.0" - google_maps_flutter_android: - dependency: transitive - description: - name: google_maps_flutter_android - sha256: ba0947315ddc9107ecc8d95fa26eb3b87b4f27b221606ce72518314d99c7306c - url: "https://pub.dev" - source: hosted - version: "2.19.2" - google_maps_flutter_ios: - dependency: transitive - description: - name: google_maps_flutter_ios - sha256: c855600dce17e77e8af96edcf85cb68501675bb77a72f85009d08c17a8805ace - url: "https://pub.dev" - source: hosted - version: "2.18.0" - google_maps_flutter_platform_interface: - dependency: transitive - description: - name: google_maps_flutter_platform_interface - sha256: "0f8c6674d70c7e9a09cd34f63b18ebaf8a5822e85b558128eae0fdf02b4a3e93" - url: "https://pub.dev" - source: hosted - version: "2.14.2" - google_maps_flutter_web: - dependency: transitive - description: - name: google_maps_flutter_web - sha256: "2321d24f8bce2d01c441b9fd7835a6032ea44b3f0bfcf93bcebd9740fb5a85cd" - url: "https://pub.dev" - source: hosted - version: "0.6.1" - graphs: - dependency: transitive - description: - name: graphs - sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" - url: "https://pub.dev" - source: hosted - version: "2.3.2" - hooks: - dependency: transitive - description: - name: hooks - sha256: e79ed1e8e1929bc6ecb6ec85f0cb519c887aa5b423705ded0d0f2d9226def388 - url: "https://pub.dev" - source: hosted - version: "1.0.2" - html: - dependency: transitive - description: - name: html - sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602" - url: "https://pub.dev" - source: hosted - version: "0.15.6" - http: - dependency: transitive - description: - name: http - sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" - url: "https://pub.dev" - source: hosted - version: "1.6.0" - http_multi_server: - dependency: transitive - description: - name: http_multi_server - sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 - url: "https://pub.dev" - source: hosted - version: "3.2.2" - http_parser: - dependency: transitive - description: - name: http_parser - sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" - url: "https://pub.dev" - source: hosted - version: "4.1.2" - intl: - dependency: "direct main" - description: - name: intl - sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf - url: "https://pub.dev" - source: hosted - version: "0.19.0" - io: - dependency: transitive - description: - name: io - sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b - url: "https://pub.dev" - source: hosted - version: "1.0.5" - json_annotation: - dependency: "direct main" - description: - name: json_annotation - sha256: cb09e7dac6210041fad964ed7fbee004f14258b4eca4040f72d1234062ace4c8 - url: "https://pub.dev" - source: hosted - version: "4.11.0" - json_serializable: - dependency: "direct dev" - description: - name: json_serializable - sha256: "44729f5c45748e6748f6b9a57ab8f7e4336edc8ae41fc295070e3814e616a6c0" - url: "https://pub.dev" - source: hosted - version: "6.13.0" - leak_tracker: - dependency: transitive - description: - name: leak_tracker - sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" - url: "https://pub.dev" - source: hosted - version: "11.0.2" - leak_tracker_flutter_testing: - dependency: transitive - description: - name: leak_tracker_flutter_testing - sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" - url: "https://pub.dev" - source: hosted - version: "3.0.10" - leak_tracker_testing: - dependency: transitive - description: - name: leak_tracker_testing - sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" - url: "https://pub.dev" - source: hosted - version: "3.0.2" - lints: - dependency: transitive - description: - name: lints - sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" - url: "https://pub.dev" - source: hosted - version: "4.0.0" - logging: - dependency: transitive - description: - name: logging - sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 - url: "https://pub.dev" - source: hosted - version: "1.3.0" - matcher: - dependency: transitive - description: - name: matcher - sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 - url: "https://pub.dev" - source: hosted - version: "0.12.19" - material_color_utilities: - dependency: transitive - description: - name: material_color_utilities - sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" - url: "https://pub.dev" - source: hosted - version: "0.13.0" - meta: - dependency: transitive - description: - name: meta - sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" - url: "https://pub.dev" - source: hosted - version: "1.17.0" - mime: - dependency: transitive - description: - name: mime - sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" - url: "https://pub.dev" - source: hosted - version: "2.0.0" - native_toolchain_c: - dependency: transitive - description: - name: native_toolchain_c - sha256: "92b2ca62c8bd2b8d2f267cdfccf9bfbdb7322f778f8f91b3ce5b5cda23a3899f" - url: "https://pub.dev" - source: hosted - version: "0.17.5" - objective_c: - dependency: transitive - description: - name: objective_c - sha256: "100a1c87616ab6ed41ec263b083c0ef3261ee6cd1dc3b0f35f8ddfa4f996fe52" - url: "https://pub.dev" - source: hosted - version: "9.3.0" - octo_image: - dependency: transitive - description: - name: octo_image - sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - package_config: - dependency: transitive - description: - name: package_config - sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc - url: "https://pub.dev" - source: hosted - version: "2.2.0" - path: - dependency: transitive - description: - name: path - sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" - url: "https://pub.dev" - source: hosted - version: "1.9.1" - path_provider: - dependency: transitive - description: - name: path_provider - sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" - url: "https://pub.dev" - source: hosted - version: "2.1.5" - path_provider_android: - dependency: transitive - description: - name: path_provider_android - sha256: f2c65e21139ce2c3dad46922be8272bb5963516045659e71bb16e151c93b580e - url: "https://pub.dev" - source: hosted - version: "2.2.22" - path_provider_foundation: - dependency: transitive - description: - name: path_provider_foundation - sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699" - url: "https://pub.dev" - source: hosted - version: "2.6.0" - path_provider_linux: - dependency: transitive - description: - name: path_provider_linux - sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 - url: "https://pub.dev" - source: hosted - version: "2.2.1" - path_provider_platform_interface: - dependency: transitive - description: - name: path_provider_platform_interface - sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - path_provider_windows: - dependency: transitive - description: - name: path_provider_windows - sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 - url: "https://pub.dev" - source: hosted - version: "2.3.0" - permission_handler: - dependency: "direct main" - description: - name: permission_handler - sha256: "59adad729136f01ea9e35a48f5d1395e25cba6cea552249ddbe9cf950f5d7849" - url: "https://pub.dev" - source: hosted - version: "11.4.0" - permission_handler_android: - dependency: transitive - description: - name: permission_handler_android - sha256: d3971dcdd76182a0c198c096b5db2f0884b0d4196723d21a866fc4cdea057ebc - url: "https://pub.dev" - source: hosted - version: "12.1.0" - permission_handler_apple: - dependency: transitive - description: - name: permission_handler_apple - sha256: f000131e755c54cf4d84a5d8bd6e4149e262cc31c5a8b1d698de1ac85fa41023 - url: "https://pub.dev" - source: hosted - version: "9.4.7" - permission_handler_html: - dependency: transitive - description: - name: permission_handler_html - sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24" - url: "https://pub.dev" - source: hosted - version: "0.1.3+5" - permission_handler_platform_interface: - dependency: transitive - description: - name: permission_handler_platform_interface - sha256: eb99b295153abce5d683cac8c02e22faab63e50679b937fa1bf67d58bb282878 - url: "https://pub.dev" - source: hosted - version: "4.3.0" - permission_handler_windows: - dependency: transitive - description: - name: permission_handler_windows - sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" - url: "https://pub.dev" - source: hosted - version: "0.2.1" - platform: - dependency: transitive - description: - name: platform - sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" - url: "https://pub.dev" - source: hosted - version: "3.1.6" - plugin_platform_interface: - dependency: transitive - description: - name: plugin_platform_interface - sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" - url: "https://pub.dev" - source: hosted - version: "2.1.8" - pool: - dependency: transitive - description: - name: pool - sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d" - url: "https://pub.dev" - source: hosted - version: "1.5.2" - pub_semver: - dependency: transitive - description: - name: pub_semver - sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" - url: "https://pub.dev" - source: hosted - version: "2.2.0" - pubspec_parse: - dependency: transitive - description: - name: pubspec_parse - sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082" - url: "https://pub.dev" - source: hosted - version: "1.5.0" - riverpod: - dependency: transitive - description: - name: riverpod - sha256: "59062512288d3056b2321804332a13ffdd1bf16df70dcc8e506e411280a72959" - url: "https://pub.dev" - source: hosted - version: "2.6.1" - rxdart: - dependency: transitive - description: - name: rxdart - sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" - url: "https://pub.dev" - source: hosted - version: "0.28.0" - sanitize_html: - dependency: transitive - description: - name: sanitize_html - sha256: "12669c4a913688a26555323fb9cec373d8f9fbe091f2d01c40c723b33caa8989" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - shared_preferences: - dependency: "direct main" - description: - name: shared_preferences - sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64" - url: "https://pub.dev" - source: hosted - version: "2.5.4" - shared_preferences_android: - dependency: transitive - description: - name: shared_preferences_android - sha256: "8374d6200ab33ac99031a852eba4c8eb2170c4bf20778b3e2c9eccb45384fb41" - url: "https://pub.dev" - source: hosted - version: "2.4.21" - shared_preferences_foundation: - dependency: transitive - description: - name: shared_preferences_foundation - sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f" - url: "https://pub.dev" - source: hosted - version: "2.5.6" - shared_preferences_linux: - dependency: transitive - description: - name: shared_preferences_linux - sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" - url: "https://pub.dev" - source: hosted - version: "2.4.1" - shared_preferences_platform_interface: - dependency: transitive - description: - name: shared_preferences_platform_interface - sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" - url: "https://pub.dev" - source: hosted - version: "2.4.1" - shared_preferences_web: - dependency: transitive - description: - name: shared_preferences_web - sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 - url: "https://pub.dev" - source: hosted - version: "2.4.3" - shared_preferences_windows: - dependency: transitive - description: - name: shared_preferences_windows - sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" - url: "https://pub.dev" - source: hosted - version: "2.4.1" - shelf: - dependency: transitive - description: - name: shelf - sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 - url: "https://pub.dev" - source: hosted - version: "1.4.2" - shelf_web_socket: - dependency: transitive - description: - name: shelf_web_socket - sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" - url: "https://pub.dev" - source: hosted - version: "3.0.0" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - source_gen: - dependency: transitive - description: - name: source_gen - sha256: "1d562a3c1f713904ebbed50d2760217fd8a51ca170ac4b05b0db490699dbac17" - url: "https://pub.dev" - source: hosted - version: "4.2.0" - source_helper: - dependency: transitive - description: - name: source_helper - sha256: "4a85e90b50694e652075cbe4575665539d253e6ec10e46e76b45368ab5e3caae" - url: "https://pub.dev" - source: hosted - version: "1.3.10" - source_span: - dependency: transitive - description: - name: source_span - sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" - url: "https://pub.dev" - source: hosted - version: "1.10.2" - sqflite: - dependency: transitive - description: - name: sqflite - sha256: e2297b1da52f127bc7a3da11439985d9b536f75070f3325e62ada69a5c585d03 - url: "https://pub.dev" - source: hosted - version: "2.4.2" - sqflite_android: - dependency: transitive - description: - name: sqflite_android - sha256: "881e28efdcc9950fd8e9bb42713dcf1103e62a2e7168f23c9338d82db13dec40" - url: "https://pub.dev" - source: hosted - version: "2.4.2+3" - sqflite_common: - dependency: transitive - description: - name: sqflite_common - sha256: "6ef422a4525ecc601db6c0a2233ff448c731307906e92cabc9ba292afaae16a6" - url: "https://pub.dev" - source: hosted - version: "2.5.6" - sqflite_darwin: - dependency: transitive - description: - name: sqflite_darwin - sha256: "279832e5cde3fe99e8571879498c9211f3ca6391b0d818df4e17d9fff5c6ccb3" - url: "https://pub.dev" - source: hosted - version: "2.4.2" - sqflite_platform_interface: - dependency: transitive - description: - name: sqflite_platform_interface - sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920" - url: "https://pub.dev" - source: hosted - version: "2.4.0" - stack_trace: - dependency: transitive - description: - name: stack_trace - sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" - url: "https://pub.dev" - source: hosted - version: "1.12.1" - state_notifier: - dependency: transitive - description: - name: state_notifier - sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb - url: "https://pub.dev" - source: hosted - version: "1.0.0" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - stream_transform: - dependency: transitive - description: - name: stream_transform - sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 - url: "https://pub.dev" - source: hosted - version: "2.1.1" - string_scanner: - dependency: transitive - description: - name: string_scanner - sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" - url: "https://pub.dev" - source: hosted - version: "1.4.1" - synchronized: - dependency: transitive - description: - name: synchronized - sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0 - url: "https://pub.dev" - source: hosted - version: "3.4.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" - url: "https://pub.dev" - source: hosted - version: "1.2.2" - test_api: - dependency: transitive - description: - name: test_api - sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" - url: "https://pub.dev" - source: hosted - version: "0.7.10" - typed_data: - dependency: transitive - description: - name: typed_data - sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 - url: "https://pub.dev" - source: hosted - version: "1.4.0" - uuid: - dependency: transitive - description: - name: uuid - sha256: "1fef9e8e11e2991bb773070d4656b7bd5d850967a2456cfc83cf47925ba79489" - url: "https://pub.dev" - source: hosted - version: "4.5.3" - vector_math: - dependency: transitive - description: - name: vector_math - sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b - url: "https://pub.dev" - source: hosted - version: "2.2.0" - vm_service: - dependency: transitive - description: - name: vm_service - sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" - url: "https://pub.dev" - source: hosted - version: "15.0.2" - watcher: - dependency: transitive - description: - name: watcher - sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635" - url: "https://pub.dev" - source: hosted - version: "1.2.1" - web: - dependency: transitive - description: - name: web - sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" - url: "https://pub.dev" - source: hosted - version: "1.1.1" - web_socket: - dependency: transitive - description: - name: web_socket - sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" - url: "https://pub.dev" - source: hosted - version: "1.0.1" - web_socket_channel: - dependency: transitive - description: - name: web_socket_channel - sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 - url: "https://pub.dev" - source: hosted - version: "3.0.3" - xdg_directories: - dependency: transitive - description: - name: xdg_directories - sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" - url: "https://pub.dev" - source: hosted - version: "1.1.0" - yaml: - dependency: transitive - description: - name: yaml - sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce - url: "https://pub.dev" - source: hosted - version: "3.1.3" -sdks: - dart: ">=3.10.3 <4.0.0" - flutter: ">=3.38.4" diff --git a/ontime_user_flutter/pubspec.yaml b/ontime_user_flutter/pubspec.yaml deleted file mode 100644 index 61205d0..0000000 --- a/ontime_user_flutter/pubspec.yaml +++ /dev/null @@ -1,50 +0,0 @@ -name: ontime_user_flutter -description: Ontime user Flutter app (MVP) consuming backendpanel Pelanggan API. -version: 0.1.0+1 - -environment: - sdk: ">=3.3.0 <4.0.0" - -dependencies: - flutter: - sdk: flutter - - cupertino_icons: ^1.0.8 - - # HTTP & JSON - dio: ^5.7.0 - json_annotation: ^4.9.0 - - # State management - flutter_riverpod: ^2.6.1 - - # Navigation - go_router: ^14.3.0 - - # Storage - shared_preferences: ^2.3.3 - - # Location & maps (MVP) - geolocator: ^13.0.2 - google_maps_flutter: ^2.10.0 - permission_handler: ^11.3.1 - - # Firebase / FCM (tokens wired to existing backend) - firebase_core: ^3.6.0 - firebase_messaging: ^15.1.0 - - # UI helpers - intl: ^0.19.0 - cached_network_image: ^3.4.1 - -dev_dependencies: - flutter_test: - sdk: flutter - flutter_lints: ^4.0.0 - build_runner: ^2.4.13 - json_serializable: ^6.9.0 - -flutter: - uses-material-design: true - assets: - - assets/images/ diff --git a/ontime_user_flutter/test/widget_test.dart b/ontime_user_flutter/test/widget_test.dart deleted file mode 100644 index fa91ef1..0000000 --- a/ontime_user_flutter/test/widget_test.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import 'package:ontime_user_flutter/core/app_router.dart'; -import 'package:ontime_user_flutter/main.dart'; - -void main() { - testWidgets('App loads and shows login', (WidgetTester tester) async { - final router = createAppRouter(); - await tester.pumpWidget( - ProviderScope( - child: OntimeUserApp(router: router), - ), - ); - await tester.pumpAndSettle(); - expect(find.text('Masuk'), findsOneWidget); - }); -}