Commit 608834d4 authored by Sugar Pramana's avatar Sugar Pramana

Initial commit

parents
Pipeline #1047 canceled with stages
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Panduan Build APK Android
## Langkah-langkah Build APK
### 1. Install Dependencies
```bash
npm install
```
### 2. Build Aplikasi React
```bash
npm run build
```
### 3. Sync dengan Capacitor
```bash
npx cap sync android
```
### 4. Buka di Android Studio
```bash
npx cap open android
```
### 5. Build APK di Android Studio
Setelah Android Studio terbuka:
1. Tunggu Gradle sync selesai
2. Pilih menu **Build > Build Bundle(s) / APK(s) > Build APK(s)**
3. Tunggu proses build selesai
4. Klik **locate** pada notifikasi untuk membuka folder APK
5. APK tersimpan di: `android/app/build/outputs/apk/debug/app-debug.apk`
## Alternatif: Build via Command Line
Jika sudah setup Android SDK di PATH:
```bash
cd android
./gradlew assembleDebug
```
APK akan tersimpan di `android/app/build/outputs/apk/debug/app-debug.apk`
## Build APK Release (Signed)
Untuk production APK yang bisa di-upload ke Play Store:
1. Generate keystore:
```bash
keytool -genkey -v -keystore dms-sales.keystore -alias dms-sales -keyalg RSA -keysize 2048 -validity 10000
```
2. Edit `android/app/build.gradle`, tambahkan:
```gradle
android {
...
signingConfigs {
release {
storeFile file('../../dms-sales.keystore')
storePassword 'your-password'
keyAlias 'dms-sales'
keyPassword 'your-password'
}
}
buildTypes {
release {
signingConfig signingConfigs.release
...
}
}
}
```
3. Build release APK:
```bash
cd android
./gradlew assembleRelease
```
APK release tersimpan di `android/app/build/outputs/apk/release/app-release.apk`
## Troubleshooting
### Error: ANDROID_HOME not set
Set environment variable ANDROID_HOME ke lokasi Android SDK:
```bash
# Windows
set ANDROID_HOME=C:\Users\YourName\AppData\Local\Android\Sdk
# Linux/Mac
export ANDROID_HOME=$HOME/Android/Sdk
```
### Error: Java version
Pastikan menggunakan JDK 11 atau lebih baru:
```bash
java -version
```
### Error: Gradle sync failed
Buka Android Studio dan sync manual, atau jalankan:
```bash
cd android
./gradlew clean
```
## Testing APK
Install APK ke device/emulator:
```bash
adb install android/app/build/outputs/apk/debug/app-debug.apk
```
Atau transfer file APK ke device dan install manual.
# Cara Install Android SDK dan Build APK
## Opsi 1: Menggunakan Android Studio (Recommended)
### 1. Download dan Install Android Studio
- Download dari: https://developer.android.com/studio
- Install Android Studio
- Saat pertama kali buka, ikuti wizard setup untuk install Android SDK
### 2. Konfigurasi SDK
Setelah Android Studio terinstall, SDK biasanya ada di:
- Windows: `C:\Users\[USERNAME]\AppData\Local\Android\Sdk`
- Mac: `~/Library/Android/sdk`
- Linux: `~/Android/Sdk`
### 3. Set Environment Variable (Optional tapi recommended)
Windows:
```
setx ANDROID_HOME "C:\Users\[USERNAME]\AppData\Local\Android\Sdk"
setx PATH "%PATH%;%ANDROID_HOME%\platform-tools;%ANDROID_HOME%\tools"
```
### 4. Build APK via Android Studio
```bash
# Buka project di Android Studio
npx cap open android
# Di Android Studio:
# Build > Build Bundle(s) / APK(s) > Build APK(s)
```
APK akan tersimpan di: `android/app/build/outputs/apk/debug/app-debug.apk`
### 5. Build APK via Command Line
Setelah Android Studio terinstall:
```bash
cd dms-sales-app/android
./gradlew assembleDebug
```
---
## Opsi 2: Install Command Line Tools Only (Tanpa Android Studio)
### 1. Download Command Line Tools
- Download dari: https://developer.android.com/studio#command-tools
- Extract ke folder, misalnya: `C:\Android\cmdline-tools`
### 2. Install SDK Components
```bash
cd C:\Android\cmdline-tools\bin
sdkmanager "platform-tools" "platforms;android-33" "build-tools;33.0.0"
```
### 3. Buat local.properties
Buat file `dms-sales-app/android/local.properties`:
```
sdk.dir=C:\\Android
```
### 4. Build APK
```bash
cd dms-sales-app/android
./gradlew assembleDebug
```
---
## Opsi 3: Build Online (Tanpa Install SDK)
Gunakan layanan online seperti:
- **Expo EAS Build** (untuk React Native/Capacitor)
- **Appetize.io** (untuk testing)
- **GitHub Actions** (CI/CD dengan Android SDK pre-installed)
---
## Troubleshooting
### Error: SDK location not found
Buat file `android/local.properties`:
```
sdk.dir=C:\\Users\\[USERNAME]\\AppData\\Local\\Android\\Sdk
```
### Error: Java version
Pastikan Java 11 atau lebih baru terinstall:
```bash
java -version
```
Download JDK dari: https://adoptium.net/
### Error: Gradle build failed
Clean dan rebuild:
```bash
cd android
./gradlew clean
./gradlew assembleDebug
```
---
## Quick Start (Jika SDK Sudah Terinstall)
```bash
# 1. Build React app
npm run build
# 2. Sync dengan Capacitor
npx cap sync android
# 3. Build APK
cd android
./gradlew assembleDebug
# APK location:
# android/app/build/outputs/apk/debug/app-debug.apk
```
---
## Install APK ke Device
### Via USB:
```bash
adb install android/app/build/outputs/apk/debug/app-debug.apk
```
### Via File Transfer:
1. Copy file `app-debug.apk` ke device
2. Buka file di device
3. Allow "Install from Unknown Sources" jika diminta
4. Install aplikasi
# Panduan Cepat Build APK - DMS Sales App
## Status Saat Ini ✅
Aplikasi sudah siap dan berhasil di-build! Yang perlu dilakukan:
1. ✅ React app sudah dibuat
2. ✅ Tailwind CSS sudah dikonfigurasi
3. ✅ Capacitor sudah disetup
4. ✅ Build production sudah berhasil (`npm run build`)
5. ✅ Sync dengan Android sudah berhasil (`npx cap sync android`)
6. ⏳ Tinggal build APK (butuh Android SDK)
## Cara Tercepat Build APK
### Metode 1: Menggunakan Android Studio (Paling Mudah)
1. **Install Android Studio**
- Download: https://developer.android.com/studio
- Install dengan default settings (akan otomatis install Android SDK)
2. **Buka Project**
```bash
npx cap open android
```
3. **Build APK di Android Studio**
- Tunggu Gradle sync selesai
- Klik menu: **Build > Build Bundle(s) / APK(s) > Build APK(s)**
- Tunggu proses build (5-10 menit pertama kali)
- Klik **locate** untuk buka folder APK
4. **Lokasi APK**
```
dms-sales-app/android/app/build/outputs/apk/debug/app-debug.apk
```
### Metode 2: Command Line (Setelah Android Studio Terinstall)
```bash
# Dari folder dms-sales-app
cd android
./gradlew assembleDebug
```
APK akan ada di: `android/app/build/outputs/apk/debug/app-debug.apk`
## Install APK ke HP Android
### Cara 1: Via USB
```bash
adb install android/app/build/outputs/apk/debug/app-debug.apk
```
### Cara 2: Transfer File
1. Copy file `app-debug.apk` ke HP (via USB, email, atau cloud)
2. Buka file di HP
3. Izinkan "Install from Unknown Sources" jika diminta
4. Tap Install
## Ukuran APK yang Diharapkan
- Debug APK: ~7-10 MB
- Release APK (signed): ~5-7 MB
## Testing Aplikasi Tanpa Build APK
Jika ingin test dulu sebelum build APK:
```bash
# Test di browser
npm start
```
Buka http://localhost:3000 di browser
## Catatan Penting
- APK debug hanya untuk testing, tidak bisa di-upload ke Play Store
- Untuk production, perlu build release APK dengan signing key
- Aplikasi ini pure prototyping, tidak ada koneksi API/backend
## Butuh Bantuan?
Jika ada error saat build, cek file `INSTALL_ANDROID_SDK.md` untuk troubleshooting lengkap.
# DMS Sales App
Aplikasi prototyping untuk Dealer Management System (DMS) Sales berbasis React JS dengan Tailwind CSS.
## Fitur Aplikasi
1. **Beranda** - Dashboard dengan detail mobil dan simulasi kredit
2. **Prospek** - Daftar dan input data prospek pelanggan
3. **Penawaran** - Detail penawaran harga dan finalisasi
4. **Transaksi** - Riwayat transaksi penjualan
## Cara Menjalankan Aplikasi
### Development Mode
```bash
npm start
```
Aplikasi akan berjalan di `http://localhost:3000`
### Build untuk Production
```bash
npm run build
```
### Build APK Android
1. Build aplikasi React terlebih dahulu:
```bash
npm run build
```
2. Sync dengan Capacitor:
```bash
npx cap sync android
```
3. Buka project Android di Android Studio:
```bash
npx cap open android
```
4. Di Android Studio:
- Pastikan Android SDK sudah terinstall
- Pilih menu **Build > Build Bundle(s) / APK(s) > Build APK(s)**
- APK akan tersimpan di `android/app/build/outputs/apk/debug/app-debug.apk`
## Persyaratan untuk Build APK
- Node.js (v14 atau lebih baru)
- Android Studio
- Java Development Kit (JDK) 11 atau lebih baru
- Android SDK
## Teknologi yang Digunakan
- React JS
- React Router DOM
- Tailwind CSS
- Capacitor (untuk build APK)
## Catatan
Aplikasi ini adalah prototyping tanpa integrasi API. Semua data bersifat statis untuk keperluan demonstrasi.
# Using Android gitignore template: https://github.com/github/gitignore/blob/HEAD/Android.gitignore
# Built application files
*.apk
*.aar
*.ap_
*.aab
# Files for the ART/Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
out/
# Uncomment the following line in case you need and you don't have the release build type files in your app
# release/
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
# Android Studio Navigation editor temp files
.navigation/
# Android Studio captures folder
captures/
# IntelliJ
*.iml
.idea/workspace.xml
.idea/tasks.xml
.idea/gradle.xml
.idea/assetWizardSettings.xml
.idea/dictionaries
.idea/libraries
# Android Studio 3 in .gitignore file.
.idea/caches
.idea/modules.xml
# Comment next line if keeping position of elements in Navigation Editor is relevant for you
.idea/navEditor.xml
# Keystore files
# Uncomment the following lines if you do not want to check your keystore files in.
#*.jks
#*.keystore
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
.cxx/
# Google Services (e.g. APIs or Firebase)
# google-services.json
# Freeline
freeline.py
freeline/
freeline_project_description.json
# fastlane
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
fastlane/readme.md
# Version control
vcs.xml
# lint
lint/intermediates/
lint/generated/
lint/outputs/
lint/tmp/
# lint/reports/
# Android Profiling
*.hprof
# Cordova plugins for Capacitor
capacitor-cordova-android-plugins
# Copied web assets
app/src/main/assets/public
# Generated Config files
app/src/main/assets/capacitor.config.json
app/src/main/assets/capacitor.plugins.json
app/src/main/res/xml/config.xml
/build/*
!/build/.npmkeep
apply plugin: 'com.android.application'
android {
namespace "com.dms.sales"
compileSdk rootProject.ext.compileSdkVersion
defaultConfig {
applicationId "com.dms.sales"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
// Default: https://android.googlesource.com/platform/frameworks/base/+/282e181b58cf72b6ca770dc7ca5f91f135444502/tools/aapt/AaptAssets.cpp#61
ignoreAssetsPattern '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~'
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
repositories {
flatDir{
dirs '../capacitor-cordova-android-plugins/src/main/libs', 'libs'
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion"
implementation "androidx.core:core-splashscreen:$coreSplashScreenVersion"
implementation project(':capacitor-android')
testImplementation "junit:junit:$junitVersion"
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
implementation project(':capacitor-cordova-android-plugins')
}
apply from: 'capacitor.build.gradle'
try {
def servicesJSON = file('google-services.json')
if (servicesJSON.text) {
apply plugin: 'com.google.gms.google-services'
}
} catch(Exception e) {
logger.info("google-services.json not found, google-services plugin not applied. Push Notifications won't work")
}
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
}
apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
dependencies {
}
if (hasProperty('postBuildExtras')) {
postBuildExtras()
}
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
package com.getcapacitor.myapp;
import static org.junit.Assert.*;
import android.content.Context;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("com.getcapacitor.app", appContext.getPackageName());
}
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode"
android:name=".MainActivity"
android:label="@string/title_activity_main"
android:theme="@style/AppTheme.NoActionBarLaunch"
android:launchMode="singleTask"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"></meta-data>
</provider>
</application>
<!-- Permissions -->
<uses-permission android:name="android.permission.INTERNET" />
</manifest>
package com.dms.sales;
import com.getcapacitor.BridgeActivity;
public class MainActivity extends BridgeActivity {}
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillType="evenOdd"
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
android:strokeColor="#00000000"
android:strokeWidth="1">
<aapt:attr name="android:fillColor">
<gradient
android:endX="78.5885"
android:endY="90.9159"
android:startX="48.7653"
android:startY="61.0927"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
</vector>
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillColor="#26A69A"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
</vector>
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<WebView
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#FFFFFF</color>
</resources>
\ No newline at end of file
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="app_name">DMS Sales</string>
<string name="title_activity_main">DMS Sales</string>
<string name="package_name">com.dms.sales</string>
<string name="custom_url_scheme">com.dms.sales</string>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="AppTheme.NoActionBar" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:background">@null</item>
</style>
<style name="AppTheme.NoActionBarLaunch" parent="Theme.SplashScreen">
<item name="android:background">@drawable/splash</item>
</style>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_images" path="." />
<cache-path name="my_cache_images" path="." />
</paths>
\ No newline at end of file
package com.getcapacitor.myapp;
import static org.junit.Assert.*;
import org.junit.Test;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.2.1'
classpath 'com.google.gms:google-services:4.4.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
apply from: "variables.gradle"
allprojects {
repositories {
google()
mavenCentral()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
include ':capacitor-android'
project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor')
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
include ':app'
include ':capacitor-cordova-android-plugins'
project(':capacitor-cordova-android-plugins').projectDir = new File('./capacitor-cordova-android-plugins/')
apply from: 'capacitor.settings.gradle'
\ No newline at end of file
ext {
minSdkVersion = 22
compileSdkVersion = 34
targetSdkVersion = 34
androidxActivityVersion = '1.8.0'
androidxAppCompatVersion = '1.6.1'
androidxCoordinatorLayoutVersion = '1.2.0'
androidxCoreVersion = '1.12.0'
androidxFragmentVersion = '1.6.2'
coreSplashScreenVersion = '1.0.1'
androidxWebkitVersion = '1.9.0'
junitVersion = '4.13.2'
androidxJunitVersion = '1.1.5'
androidxEspressoCoreVersion = '3.5.1'
cordovaAndroidVersion = '10.1.1'
}
\ No newline at end of file
import type { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
appId: 'com.dms.sales',
appName: 'DMS Sales',
webDir: 'build'
};
export default config;
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "dms-sales-app",
"version": "0.1.0",
"private": true,
"dependencies": {
"@capacitor/android": "^6.1.2",
"@capacitor/cli": "^6.1.2",
"@capacitor/core": "^6.1.2",
"@testing-library/dom": "^10.4.1",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^13.5.0",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-router-dom": "^7.14.0",
"react-scripts": "^5.0.1",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"autoprefixer": "^10.4.17",
"postcss": "^8.4.35",
"tailwindcss": "^3.4.1"
}
}
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:
import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Beranda from './pages/Beranda';
import Prospek from './pages/Prospek';
import InputProspek from './pages/InputProspek';
import Penawaran from './pages/Penawaran';
import Transaksi from './pages/Transaksi';
function App() {
return (
<Router>
<div className="min-h-screen bg-gray-50">
<Routes>
<Route path="/" element={<Beranda />} />
<Route path="/prospek" element={<Prospek />} />
<Route path="/prospek/input" element={<InputProspek />} />
<Route path="/penawaran" element={<Penawaran />} />
<Route path="/transaksi" element={<Transaksi />} />
</Routes>
</div>
</Router>
);
}
export default App;
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});
import React from 'react';
import { useNavigate, useLocation } from 'react-router-dom';
const BottomNav = () => {
const navigate = useNavigate();
const location = useLocation();
const navItems = [
{ path: '/', label: 'BERANDA', icon: 'home' },
{ path: '/prospek', label: 'PROSPEK', icon: 'users' },
{ path: '/penawaran', label: 'PENAWARAN', icon: 'file' },
{ path: '/transaksi', label: 'TRANSAKSI', icon: 'repeat' },
];
return (
<div className="fixed bottom-0 left-0 right-0 bg-white border-t border-gray-200 px-2 py-2">
<div className="flex justify-around items-center">
{navItems.map((item) => (
<button
key={item.path}
onClick={() => navigate(item.path)}
className={`flex flex-col items-center gap-1 px-4 py-1 ${
location.pathname === item.path ? 'text-blue-600' : 'text-gray-400'
}`}
>
<div className="w-6 h-6">
{item.icon === 'home' && (
<svg fill="currentColor" viewBox="0 0 20 20">
<path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z" />
</svg>
)}
{item.icon === 'users' && (
<svg fill="currentColor" viewBox="0 0 20 20">
<path d="M9 6a3 3 0 11-6 0 3 3 0 016 0zM17 6a3 3 0 11-6 0 3 3 0 016 0zM12.93 17c.046-.327.07-.66.07-1a6.97 6.97 0 00-1.5-4.33A5 5 0 0119 16v1h-6.07zM6 11a5 5 0 015 5v1H1v-1a5 5 0 015-5z" />
</svg>
)}
{item.icon === 'file' && (
<svg fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4z" clipRule="evenodd" />
</svg>
)}
{item.icon === 'repeat' && (
<svg fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z" clipRule="evenodd" />
</svg>
)}
</div>
<span className="text-xs font-medium">{item.label}</span>
</button>
))}
</div>
</div>
);
};
export default BottomNav;
import React from 'react';
const Header = ({ title = 'DMS Sales' }) => {
return (
<div className="bg-white px-4 py-3 flex items-center justify-between shadow-sm">
<div className="flex items-center gap-3">
<div className="w-8 h-8 bg-gray-800 rounded-full flex items-center justify-center">
<span className="text-white text-sm font-bold">D</span>
</div>
<span className="font-semibold text-gray-800">{title}</span>
</div>
<button className="text-gray-400">
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
</button>
</div>
);
};
export default Header;
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>
\ No newline at end of file
import React from 'react';
import Header from '../components/Header';
import BottomNav from '../components/BottomNav';
const Beranda = () => {
return (
<div className="pb-20">
<Header />
<div className="p-4 space-y-4">
{/* Car Card */}
<div className="bg-gradient-to-br from-gray-800 to-gray-900 rounded-2xl p-6 text-white relative overflow-hidden">
<div className="relative z-10">
<h2 className="text-3xl font-bold">2024</h2>
<h3 className="text-2xl font-bold">Zenith</h3>
<h3 className="text-2xl font-bold mb-2">GT-X</h3>
<p className="text-sm text-gray-300 mb-1">Signature</p>
<p className="text-sm text-gray-300 mb-1">Performance</p>
<p className="text-sm text-gray-300 mb-1">Edition</p>
</div>
<div className="absolute bottom-4 right-4 bg-white text-gray-900 px-4 py-2 rounded-lg">
<p className="text-xs">Rp</p>
<p className="text-lg font-bold">850.000.000</p>
</div>
</div>
{/* Penawaran Harga */}
<div className="bg-white rounded-xl p-4 shadow-sm">
<h3 className="text-xs text-gray-500 mb-3 font-semibold">PENAWARAN HARGA</h3>
<div className="space-y-3">
<div className="flex justify-between">
<span className="text-sm text-gray-600">Harga Kendaraan</span>
<span className="text-sm font-semibold">Rp 850.000.000</span>
</div>
<div className="flex justify-between">
<span className="text-sm text-gray-600">Diskon Dealer</span>
<span className="text-sm font-semibold text-red-600">-Rp 15.000.000</span>
</div>
<div className="flex justify-between">
<span className="text-sm text-gray-600">Uang Muka (20%)</span>
<span className="text-sm font-semibold">Rp 170.000.000</span>
</div>
<div className="flex justify-between">
<span className="text-sm text-gray-600">Biaya Administrasi &</span>
<span className="text-sm font-semibold">Rp</span>
</div>
<div className="flex justify-between -mt-2">
<span className="text-sm text-gray-600">Surat</span>
<span className="text-sm font-semibold">12.500.000</span>
</div>
<div className="border-t pt-3 mt-3">
<p className="text-xs text-gray-500 mb-1">TOTAL PEMBAYARAN</p>
<div className="flex items-end justify-between">
<div>
<p className="text-3xl font-bold text-primary">Rp</p>
<p className="text-3xl font-bold text-primary">677.500.000</p>
</div>
<span className="bg-blue-100 text-blue-700 text-xs px-3 py-1 rounded">
MENUNGGU<br/>KREDIT
</span>
</div>
</div>
</div>
</div>
{/* Spesifikasi */}
<div className="grid grid-cols-2 gap-3">
<div className="bg-white rounded-lg p-3 shadow-sm">
<p className="text-xs text-gray-500 mb-1">WARNA</p>
<p className="font-semibold">Obsidian</p>
<p className="font-semibold">Pearl</p>
</div>
<div className="bg-white rounded-lg p-3 shadow-sm">
<p className="text-xs text-gray-500 mb-1">TRANSMISI</p>
<p className="font-semibold">Dual-Motor</p>
<p className="font-semibold">AWD</p>
</div>
</div>
{/* Simulasi Kredit */}
<div className="bg-primary rounded-xl p-5 text-white">
<div className="flex justify-between items-start mb-4">
<h3 className="font-semibold">Simulasi Kredit</h3>
<button className="text-white">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
</svg>
</button>
</div>
<p className="text-xs mb-2">Perkiraan DP 60 Bulan</p>
<p className="text-xs mb-1">Rp</p>
<p className="text-3xl font-bold mb-4">Rp 12.500.000 <span className="text-sm">/bln</span></p>
<div className="space-y-2 text-sm">
<div className="flex justify-between">
<span>Suku Bunga</span>
<span>3.25% Tetap</span>
</div>
<div className="flex justify-between">
<span>Tenor</span>
<span>60 Bulan</span>
</div>
<div className="flex justify-between">
<span>Asuransi</span>
<span>Komprehensif</span>
</div>
</div>
<button className="w-full bg-white text-primary font-semibold py-3 rounded-lg mt-4 flex items-center justify-center gap-2">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z" />
</svg>
Atur Ketentuan
</button>
</div>
{/* Action Buttons */}
<div className="grid grid-cols-2 gap-3">
<button className="bg-primary text-white py-3 rounded-lg font-semibold flex items-center justify-center gap-2">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8" />
</svg>
Kirim Penawaran
</button>
<button className="bg-white border-2 border-primary text-primary py-3 rounded-lg font-semibold flex items-center justify-center gap-2">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z" />
</svg>
Unduh PDF
</button>
</div>
</div>
<BottomNav />
</div>
);
};
export default Beranda;
import React, { useState } from 'react';
import Header from '../components/Header';
import BottomNav from '../components/BottomNav';
const InputProspek = () => {
const [selectedSource, setSelectedSource] = useState('');
return (
<div className="pb-20 bg-gray-50 min-h-screen">
<Header />
<div className="p-4">
<h1 className="text-3xl font-bold mb-2">Input Prospek</h1>
<p className="text-gray-600 mb-6">Catat detail calon pelanggan dengan akurat.</p>
<div className="space-y-4">
{/* Nama Pelanggan */}
<div>
<label className="block text-sm font-semibold text-gray-700 mb-2">
NAMA PELANGGAN
</label>
<input
type="text"
placeholder="Masukkan nama lengkap"
className="w-full px-4 py-3 bg-gray-100 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</div>
{/* Nomor Telepon */}
<div>
<label className="block text-sm font-semibold text-gray-700 mb-2">
NOMOR TELEPON
</label>
<div className="relative">
<span className="absolute left-4 top-3.5 text-gray-500">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
</svg>
</span>
<input
type="tel"
placeholder="+62 812-0000-0000"
className="w-full pl-12 pr-4 py-3 bg-gray-100 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</div>
</div>
{/* Model Mobil Diminati */}
<div>
<label className="block text-sm font-semibold text-gray-700 mb-2">
MODEL MOBIL DIMINATI
</label>
<div className="relative">
<select className="w-full px-4 py-3 bg-gray-100 rounded-lg appearance-none focus:outline-none focus:ring-2 focus:ring-blue-500 text-gray-500">
<option>Pilih Model</option>
<option>SUV Model X - AWD Edition</option>
<option>GT Hybrid Sedan</option>
<option>Roadster Z - Carbon Pack</option>
<option>Compact E-Crossover</option>
<option>Zenith GT-X</option>
</select>
<svg className="w-5 h-5 text-gray-400 absolute right-4 top-3.5 pointer-events-none" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</div>
</div>
{/* Sumber Prospek */}
<div>
<label className="block text-sm font-semibold text-gray-700 mb-2">
SUMBER PROSPEK
</label>
<div className="flex gap-2">
{['Walk-in', 'Instagram', 'Website'].map((source) => (
<button
key={source}
onClick={() => setSelectedSource(source)}
className={`px-6 py-2 rounded-full border-2 font-medium transition-colors ${
selectedSource === source
? 'bg-primary text-white border-primary'
: 'bg-white text-gray-700 border-gray-300'
}`}
>
{source}
</button>
))}
</div>
</div>
{/* Submit Button */}
<button className="w-full bg-primary text-white py-4 rounded-lg font-semibold flex items-center justify-center gap-2 mt-6">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4" />
</svg>
Simpan Prospek
</button>
{/* Portal Dealer Image */}
<div className="mt-6 rounded-xl overflow-hidden">
<div className="bg-gradient-to-br from-teal-400 to-teal-600 h-48 flex items-center justify-center text-white">
<div className="text-center">
<p className="text-lg font-semibold">Portal Dealer Mewah Nexus</p>
</div>
</div>
</div>
</div>
</div>
<BottomNav />
</div>
);
};
export default InputProspek;
import React from 'react';
import Header from '../components/Header';
import BottomNav from '../components/BottomNav';
const Penawaran = () => {
return (
<div className="pb-20 bg-gray-50 min-h-screen">
<Header />
<div className="p-4">
<div className="bg-white rounded-xl p-4 shadow-sm mb-4">
<div className="flex items-center gap-2 mb-4">
<span className="text-gray-500"> MENUNGGU / DISETUJUI / SELESAI</span>
</div>
<h1 className="text-2xl font-bold mb-1">Finalisasi Transaksi</h1>
<div className="flex items-center gap-2 mb-4">
<div className="w-3 h-3 bg-green-500 rounded-full"></div>
<span className="text-sm font-semibold text-green-600">Status: Disetujui</span>
</div>
<div className="grid grid-cols-2 gap-4 mb-4">
<div>
<p className="text-xs text-gray-500 mb-1">NAMA PELANGGAN</p>
<p className="font-bold">Alexander</p>
<p className="font-bold">Sterling</p>
</div>
<div>
<p className="text-xs text-gray-500 mb-1">ID TRANSAKSI</p>
<p className="font-semibold text-sm">#DMS-2024-</p>
<p className="font-semibold text-sm">8802</p>
</div>
</div>
</div>
{/* Car Image */}
<div className="bg-white rounded-xl p-4 shadow-sm mb-4">
<div className="bg-gradient-to-br from-teal-400 to-teal-600 rounded-lg h-40 flex items-center justify-center mb-3">
<div className="text-white text-center">
<p className="text-lg font-bold">2024 Executive Sport</p>
<p className="text-sm">Sedan LX</p>
</div>
</div>
<p className="text-xs text-gray-500 mb-1">MODEL MOBIL</p>
<p className="font-bold">2024 Executive Sport</p>
<p className="font-bold">Sedan LX</p>
</div>
{/* Harga */}
<div className="bg-white rounded-xl p-4 shadow-sm mb-4">
<div className="grid grid-cols-2 gap-4 mb-4">
<div>
<p className="text-xs text-gray-500 mb-1">HARGA DASAR</p>
<p className="font-bold">Rp</p>
<p className="font-bold">806.250.000</p>
</div>
<div>
<p className="text-xs text-gray-500 mb-1">TAMBAHAN</p>
<p className="font-bold">Rp</p>
<p className="font-bold">28.750.000</p>
</div>
</div>
<div className="bg-gray-50 rounded-lg p-3">
<p className="text-xs text-gray-500 mb-1">HARGA AKHIR</p>
<p className="text-2xl font-bold text-primary">Rp 835.000.000</p>
</div>
</div>
{/* Metode Pembayaran */}
<div className="bg-white rounded-xl p-4 shadow-sm mb-4">
<p className="text-xs text-gray-500 font-semibold mb-3">METODE PEMBAYARAN</p>
<div className="space-y-3">
<div className="flex items-center justify-between p-3 border-2 border-primary rounded-lg bg-blue-50">
<div className="flex items-center gap-3">
<div className="w-5 h-5 rounded-full border-2 border-primary flex items-center justify-center">
<div className="w-3 h-3 rounded-full bg-primary"></div>
</div>
<div>
<p className="font-semibold">Tunai / Langsung</p>
<p className="text-xs text-gray-600">Pelunasan dalam 48 jam</p>
</div>
</div>
<svg className="w-6 h-6 text-primary" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 9V7a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2m2 4h10a2 2 0 002-2v-6a2 2 0 00-2-2H9a2 2 0 00-2 2v6a2 2 0 002 2zm7-5a2 2 0 11-4 0 2 2 0 014 0z" />
</svg>
</div>
<div className="flex items-center justify-between p-3 border-2 border-gray-200 rounded-lg">
<div className="flex items-center gap-3">
<div className="w-5 h-5 rounded-full border-2 border-gray-300"></div>
<div>
<p className="font-semibold">Skema Kredit/Leasing</p>
<p className="text-xs text-gray-600">Tenor 36 Bulan, Bunga 3.5%</p>
</div>
</div>
<svg className="w-6 h-6 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z" />
</svg>
</div>
</div>
</div>
{/* Action Button */}
<div className="bg-primary rounded-xl p-4 text-white mb-4">
<p className="text-sm font-semibold mb-2">LANGKAH SELANJUTNYA</p>
<p className="text-lg font-bold mb-3">Buat Surat Perjanjian Jual Beli dan</p>
<p className="text-lg font-bold mb-4">Tanda Tangan!</p>
<button className="w-full bg-white text-primary font-semibold py-3 rounded-lg flex items-center justify-center gap-2">
Konfirmasi Transaksi
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
</svg>
</button>
</div>
{/* Upload Dokumen */}
<div className="bg-white rounded-xl p-4 shadow-sm">
<div className="flex justify-between items-center mb-3">
<p className="text-xs text-gray-500 font-semibold">UNDOAH DOKUMEN (KTP, SURAT PERJANJIAN)</p>
<p className="text-xs text-gray-500">2 dari 2</p>
<p className="text-xs text-gray-500">Terupload</p>
</div>
<div className="space-y-3">
<div className="flex items-center gap-3 p-3 bg-blue-50 rounded-lg border border-blue-200">
<div className="w-10 h-10 bg-blue-100 rounded flex items-center justify-center">
<svg className="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
</div>
<div className="flex-1">
<p className="font-semibold text-sm">KTP Pelanggan</p>
<p className="text-xs text-gray-600">Menunggu verifikasi (872.pdf)</p>
</div>
<svg className="w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" />
</svg>
</div>
<div className="flex items-center gap-3 p-3 bg-blue-50 rounded-lg border border-blue-200">
<div className="w-10 h-10 bg-blue-100 rounded flex items-center justify-center">
<svg className="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
</div>
<div className="flex-1">
<p className="font-semibold text-sm">Surat Perjanjian</p>
<p className="text-xs text-gray-600">Menunggu unggahan tanda</p>
<p className="text-xs text-gray-600">tangan</p>
</div>
<svg className="w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
</div>
</div>
</div>
</div>
<BottomNav />
</div>
);
};
export default Penawaran;
import React from 'react';
import { useNavigate } from 'react-router-dom';
import Header from '../components/Header';
import BottomNav from '../components/BottomNav';
const Prospek = () => {
const navigate = useNavigate();
const prospekList = [
{
name: 'Alexander Sterling',
model: 'SUV Model X - AWD Edition',
phone: '+1 (555) 012-3456',
status: 'BARU',
statusColor: 'bg-orange-500',
borderColor: 'border-l-orange-500'
},
{
name: 'Sarah Jenkins',
model: 'GT Hybrid Sedan',
phone: '+1 (555) 248-9102',
status: 'TINDAK LANJUT',
statusColor: 'bg-blue-400',
borderColor: 'border-l-blue-400'
},
{
name: 'Marcus Vane',
model: 'Roadster Z - Carbon Pack',
phone: '+1 (555) 732-4411',
status: 'KESEPAKATAN',
statusColor: 'bg-blue-700',
borderColor: 'border-l-blue-700'
},
{
name: 'Eleanor Thorne',
model: 'Compact E-Crossover',
phone: '+1 (555) 901-2233',
status: 'BARU',
statusColor: 'bg-orange-500',
borderColor: 'border-l-orange-500'
},
];
return (
<div className="pb-20 bg-gray-50 min-h-screen">
<Header />
<div className="p-4">
<h1 className="text-2xl font-bold mb-1">Daftar Prospek</h1>
<p className="text-sm text-gray-600 mb-4">
Ada 12 prospek aktif yang memerlukan tindak lanjut hari ini.
</p>
{/* Search Bar */}
<div className="flex gap-2 mb-4">
<div className="flex-1 relative">
<input
type="text"
placeholder="Cari nama atau model..."
className="w-full pl-10 pr-4 py-3 bg-gray-100 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
<svg className="w-5 h-5 text-gray-400 absolute left-3 top-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
</div>
<button className="bg-gray-100 p-3 rounded-lg">
<svg className="w-6 h-6 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" />
</svg>
</button>
</div>
{/* Prospek List */}
<div className="space-y-3">
{prospekList.map((prospek, index) => (
<div key={index} className={`bg-white rounded-lg p-4 border-l-4 ${prospek.borderColor} shadow-sm`}>
<div className="flex justify-between items-start mb-3">
<div className="flex-1">
<div className="flex items-center gap-2 mb-1">
<h3 className="font-bold text-gray-900">{prospek.name}</h3>
<span className={`${prospek.statusColor} text-white text-xs px-2 py-1 rounded-full`}>
{prospek.status}
</span>
</div>
<div className="flex items-center gap-1 text-sm text-gray-600 mb-1">
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7v8a2 2 0 002 2h6M8 7V5a2 2 0 012-2h4.586a1 1 0 01.707.293l4.414 4.414a1 1 0 01.293.707V15a2 2 0 01-2 2h-2M8 7H6a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2v-2" />
</svg>
<span>{prospek.model}</span>
</div>
<div className="flex items-center gap-1 text-sm text-gray-600">
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
</svg>
<span>{prospek.phone}</span>
</div>
</div>
<button className="bg-primary text-white p-2 rounded-lg">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
</svg>
</button>
</div>
<button className="w-full bg-gray-50 text-primary font-semibold py-2 rounded-lg text-sm">
Lihat Detail
</button>
</div>
))}
</div>
{/* Add Button */}
<button
onClick={() => navigate('/prospek/input')}
className="fixed bottom-24 right-4 bg-primary text-white px-6 py-3 rounded-full shadow-lg font-semibold flex items-center gap-2"
>
<span className="text-xl">+</span>
Tambah Prospek
</button>
</div>
<BottomNav />
</div>
);
};
export default Prospek;
import React from 'react';
import Header from '../components/Header';
import BottomNav from '../components/BottomNav';
const Transaksi = () => {
return (
<div className="pb-20 bg-gray-50 min-h-screen">
<Header />
<div className="p-4">
<h1 className="text-2xl font-bold mb-1">Transaksi</h1>
<p className="text-gray-600 mb-6">Riwayat dan status transaksi penjualan</p>
<div className="space-y-4">
{/* Transaksi Card 1 */}
<div className="bg-white rounded-xl p-4 shadow-sm border-l-4 border-green-500">
<div className="flex justify-between items-start mb-3">
<div>
<div className="flex items-center gap-2 mb-1">
<h3 className="font-bold">Alexander Sterling</h3>
<span className="bg-green-100 text-green-700 text-xs px-2 py-1 rounded-full">
SELESAI
</span>
</div>
<p className="text-sm text-gray-600">2024 Executive Sport Sedan LX</p>
<p className="text-xs text-gray-500 mt-1">#DMS-2024-8802</p>
</div>
</div>
<div className="flex justify-between items-center pt-3 border-t">
<div>
<p className="text-xs text-gray-500">Total</p>
<p className="font-bold text-primary">Rp 835.000.000</p>
</div>
<button className="text-primary font-semibold text-sm">
Lihat Detail
</button>
</div>
</div>
{/* Transaksi Card 2 */}
<div className="bg-white rounded-xl p-4 shadow-sm border-l-4 border-blue-500">
<div className="flex justify-between items-start mb-3">
<div>
<div className="flex items-center gap-2 mb-1">
<h3 className="font-bold">Sarah Jenkins</h3>
<span className="bg-blue-100 text-blue-700 text-xs px-2 py-1 rounded-full">
PROSES
</span>
</div>
<p className="text-sm text-gray-600">GT Hybrid Sedan</p>
<p className="text-xs text-gray-500 mt-1">#DMS-2024-8801</p>
</div>
</div>
<div className="flex justify-between items-center pt-3 border-t">
<div>
<p className="text-xs text-gray-500">Total</p>
<p className="font-bold text-primary">Rp 720.000.000</p>
</div>
<button className="text-primary font-semibold text-sm">
Lihat Detail
</button>
</div>
</div>
{/* Transaksi Card 3 */}
<div className="bg-white rounded-xl p-4 shadow-sm border-l-4 border-orange-500">
<div className="flex justify-between items-start mb-3">
<div>
<div className="flex items-center gap-2 mb-1">
<h3 className="font-bold">Marcus Vane</h3>
<span className="bg-orange-100 text-orange-700 text-xs px-2 py-1 rounded-full">
MENUNGGU
</span>
</div>
<p className="text-sm text-gray-600">Roadster Z - Carbon Pack</p>
<p className="text-xs text-gray-500 mt-1">#DMS-2024-8800</p>
</div>
</div>
<div className="flex justify-between items-center pt-3 border-t">
<div>
<p className="text-xs text-gray-500">Total</p>
<p className="font-bold text-primary">Rp 950.000.000</p>
</div>
<button className="text-primary font-semibold text-sm">
Lihat Detail
</button>
</div>
</div>
</div>
</div>
<BottomNav />
</div>
);
};
export default Transaksi;
const reportWebVitals = onPerfEntry => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./src/**/*.{js,jsx,ts,tsx}",
],
theme: {
extend: {
colors: {
primary: '#003D6B',
secondary: '#0066B3',
},
},
},
plugins: [],
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment