diff --git a/.github/workflows/ci-pr.yaml b/.github/workflows/ci-pr.yaml
new file mode 100644
index 0000000..70e9e7e
--- /dev/null
+++ b/.github/workflows/ci-pr.yaml
@@ -0,0 +1,42 @@
+name: Pull Request Simple Sanity Check
+on:
+ pull_request:
+ branches: [develop, main]
+jobs:
+ build-test-lint:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Set up JDK 17
+ uses: actions/setup-java@v4
+ with:
+ distribution: 'temurin'
+ java-version: '17'
+ cache: 'gradle'
+
+ - name: Grant execute permission for gradlew
+ run: chmod +x gradlew
+
+ - name: Build (debug)
+ run: ./gradlew assembleDebug --stacktrace
+
+ - name: Unit Tests
+ run: ./gradlew tests
+
+ - name: Android Lint
+ run: ./gradlew lint
+
+ - name: Kotlin style (ktlint) + static analysis (detekt)
+ run: ./gradlew ktlintCheck detekt
+
+ - name: Dependency updates (report only)
+ run: ./gradlew dependencyUpdates
+
+ - name: Upload reports
+ uses: actions/upload-artifact@v4
+ with:
+ name: ci-reports
+ path: |
+ **/build/reports/**
+ **/build/outputs/apk/debug/*.apk
\ No newline at end of file
diff --git a/.github/workflows/codeql.yaml b/.github/workflows/codeql.yaml
new file mode 100644
index 0000000..d8b91d4
--- /dev/null
+++ b/.github/workflows/codeql.yaml
@@ -0,0 +1,34 @@
+name: CodeQL
+on:
+ pull_request:
+ branches: [develop, main]
+ schedule:
+ - cron: '0 0 * * 0' # Weekly on Sundays at midnight
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+ permissions:
+ actions: read
+ contents: read
+ security-events: write
+ strategy:
+ fail-fast: false
+ matrix:
+ language: [ 'kotlin' ]
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v3
+ with:
+ languages: ${{ matrix.language }}
+
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@v3
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v3
+ with:
+ category: "/language:${{matrix.language}}"
diff --git a/.gitignore b/.gitignore
index ce9982b..783ffbf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,18 @@
-*.db
+
__pycache__/
*.log
-.env
\ No newline at end of file
+.env
+
+# IntelliJ / Android Studio
+.idea/
+!.idea/codeStyles
+!.idea/inspectionProfiles
+
+# Gradle
+.gradle/
+build/
+**/build/
+
+# Kotlin/Android
+local.properties
+captures/
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..7bc07ec
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,10 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Environment-dependent path to Maven home directory
+/mavenHomeManager.xml
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/AndroidProjectSystem.xml b/.idea/AndroidProjectSystem.xml
new file mode 100644
index 0000000..4a53bee
--- /dev/null
+++ b/.idea/AndroidProjectSystem.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/Find-My-Ride.iml b/.idea/Find-My-Ride.iml
new file mode 100644
index 0000000..d6ebd48
--- /dev/null
+++ b/.idea/Find-My-Ride.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/IntelliLang.xml b/.idea/IntelliLang.xml
new file mode 100644
index 0000000..8cfd409
--- /dev/null
+++ b/.idea/IntelliLang.xml
@@ -0,0 +1,286 @@
+
+
+
+
+ Apache HttpClient 4 HTTP Header (org.apache.http)
+
+
+
+
+
+ Apache HttpClient 5 HTTP Header (org.apache.hc.core5)
+
+
+
+
+
+
+
+
+ AsyncQueryRunner (org.apache.commons.dbutils)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Jodd (jodd.db)
+
+
+
+
+
+
+
+ MockServer Header (org.mockserver)
+
+
+
+
+
+
+ MyBatis @Select/@Delete/@Insert/@Update
+
+
+
+
+
+
+
+ QueryRunner (org.apache.commons.dbutils)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ R2DBC (io.r2dbc)
+
+
+
+
+
+ Reactiverse Postgres Client (io.reactiverse)
+
+
+
+
+
+
+
+
+
+
+
+
+ RestAssured HTTP Header (io.restassured)
+
+
+
+
+
+
+
+ SmallRye Axle SqlClient (io.vertx.axle.sqlclient)
+
+
+
+
+
+ SmallRye Mutiny SqlClient (io.vertx.mutiny.sqlclient)
+
+
+
+
+
+ SmallRye Mutiny SqlConnection (io.vertx.mutiny.sqlclient)
+
+
+
+
+
+
+
+ Spring @Cacheable and @CacheEvict
+
+
+
+
+
+
+
+
+
+
+
+ Spring HttpHeaders (org.springframework.http)
+
+
+
+
+
+
+ Spring Integration/Messaging
+
+
+
+
+
+
+ Spring JDBC (org.springframework.jdbc.core.JdbcOperations)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Spring JDBC (org.springframework.jdbc.core.PreparedStatementCreatorFactory)
+
+
+
+
+
+
+ Spring JDBC (org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator)
+
+
+
+
+
+
+
+ Spring Security @PostAuthorize/@PostFilter/@PreAuthorize/@PreFilter/@AuthenticationPrincipal
+
+
+
+
+
+
+
+
+
+ Spring State Machine
+
+
+
+
+
+
+
+ Vert.x SQL Extensions (io.vertx.ext.sql)
+
+
+
+
+
+
+ Vert.x SQL Reactive Extensions (io.vertx.reactivex.ext.sql)
+
+
+
+
+
+
+
+
+
+ Vert.x SqlClient (io.vertx.sqlclient)
+
+
+
+
+
+
+
+
+
+
+ Vert.x SqlClient RxJava2 (io.vertx.reactivex.sqlclient)
+
+
+
+
+
+
+
+
+
+
+
+ WireMock (com.github.tomakehurst.wiremock.client)
+
+
+
+
+
+
+
+ WireMock (com.github.tomakehurst.wiremock.client)
+
+
+
+
+
+
+ WireMock (com.github.tomakehurst.wiremock.client)
+
+
+
+
+
+
+
+ jOOQ (org.jooq.DSLContext)
+
+
+
+
+
+
+
+ rxjava2-jdbc (org.davidmoten.rx.jdbc)
+
+
+
+
+
+
+ SpEL for Spring Cache
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/composeApp_js.xml b/.idea/artifacts/composeApp_js.xml
new file mode 100644
index 0000000..a2c8ee1
--- /dev/null
+++ b/.idea/artifacts/composeApp_js.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/app/composeApp/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/composeApp_jvm.xml b/.idea/artifacts/composeApp_jvm.xml
new file mode 100644
index 0000000..c09eb24
--- /dev/null
+++ b/.idea/artifacts/composeApp_jvm.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/app/composeApp/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/composeApp_wasm_js.xml b/.idea/artifacts/composeApp_wasm_js.xml
new file mode 100644
index 0000000..21b6074
--- /dev/null
+++ b/.idea/artifacts/composeApp_wasm_js.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/app/composeApp/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..6e52cd1
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml
new file mode 100644
index 0000000..87b2916
--- /dev/null
+++ b/.idea/deploymentTargetSelector.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644
index 0000000..5aae76b
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..483affb
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
new file mode 100644
index 0000000..16660f1
--- /dev/null
+++ b/.idea/runConfigurations.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 7ad2e73..fedd083 100644
--- a/README.md
+++ b/README.md
@@ -1,15 +1,487 @@
-# Find-My-Ride
+# Find My Ride – Kotlin Multiplatform Ride-Sharing App
-## Database Setup
+### Drexel University – CS 461 Final Project
+
+### Built by:
+- Mustafa Bookwala
+- Samii Shabuse
+- Kennan Lu
+
+---
+
+## Table of Contents
+- [Overview](#overview)
+- [Tech Stack](#tech-stack)
+- [Project Structure](#project-structure)
+- [Prerequisites & Setup](#prerequisites--setup)
+- [How To Run the Project](#how-to-run-the-project)
+- [Database Setup](#database-setup)
+- [CRUD Implementation Details](#crud-implementation-details)
+- [Key Implementation Notes](#key-implementation-notes)
+- [Documentation & Demo](#documentation--demo)
+- [Future Improvements](#future-improvements)
+
+---
+
+## Overview
+
+**Find My Ride** is a full-stack **Kotlin Multiplatform (KMP)** mobile application that simulates a Drexel-focused ride-sharing service. The app allows students to:
+
+- Create an account & log in
+- View and edit their profile
+- Browse available ride offers
+- Publish a ride offer as a driver
+- Send and receive messages from other users
+- Store all data in a pre-loaded SQLite database
-1. Install SQLite if you don’t have it (`sqlite3 --version` to check).
- - Using Version: 3.50.4 2025-07-30 19:33:53 4d8adfb30e03f9cf27f800a2c1ba3c48fb4ca1b08b0f5ed59a4d5ecbf45e20a3 (64-bit)
-2. In the root project folder, run:
+The purpose of the project is to demonstrate **end-to-end database integration**, clean architecture, multiplatform UI, and **CRUD (Create, Read, Update, Delete) operations** learned in CS461.
+
+---
+
+## Tech Stack
+
+### **Frontend / App UI**
+- **Kotlin Multiplatform (KMP)** – Write once, run on multiple platforms
+- **JetBrains Compose Multiplatform** – Modern declarative UI framework
+- **Material3 Components** – Google's latest Material Design system
+- **Navigation** – Custom implementation using sealed classes for type-safe routing
+
+### **Database Layer**
+- **SQLite** – Lightweight, embedded relational database
+- **Pre-loaded Database** – `findmyride.db` stored in `assets/` folder
+- **Custom Repository Pattern** – Clean separation of concerns for each database table
+- **Android SQLiteOpenHelper** – Native Android database management
+- **Raw SQL Queries** – Direct SQL for maximum control and learning (no ORM framework)
+
+### **Platforms Supported**
+- **Android** (Primary target – fully functional)
+- **Compose Desktop** (Compilation supported, database layer not implemented)
+- **iOS/JS** – KMP structure prepared, but database implementation is Android-only for this assignment
+
+---
+
+## Project Structure
+```
+FindMyRide/
+├── composeApp/
+│ ├── src/
+│ │ ├── commonMain/ # Shared code across platforms
+│ │ │ ├── kotlin/
+│ │ │ │ ├── feature/
+│ │ │ │ │ ├── auth/ # Login & Registration screens
+│ │ │ │ │ ├── profile/ # User profile screen & logic
+│ │ │ │ │ ├── rides/ # Find Ride & Offer Ride features
+│ │ │ │ │ ├── messages/ # Messaging system UI & logic
+│ │ │ │ │ └── db/ # Repository interfaces & data models
+│ │ │ │ ├── App.kt # Root navigation (Login vs Main)
+│ │ │ │ └── MainRoute.kt # In-app navigation hub
+│ │ │ └── resources/ # Shared resources
+│ │ │
+│ │ ├── androidMain/ # Android-specific implementations
+│ │ │ ├── kotlin/
+│ │ │ │ ├── db/
+│ │ │ │ │ ├── FindMyRideDbProvider.kt # Database initialization
+│ │ │ │ │ ├── AndroidAuthRepository.kt # USER table CRUD
+│ │ │ │ │ ├── AndroidProfileRepository.kt # USER + VEHICLE CRUD
+│ │ │ │ │ ├── AndroidMessagesRepository.kt # MESSAGE table CRUD
+│ │ │ │ │ └── AndroidRideRepository.kt # RIDE_OFFER + RIDE_REQUEST CRUD
+│ │ │ │ └── MainActivity.kt # Android app entry point
+│ │ │ └── assets/
+│ │ │ └── findmyride.db # Pre-populated SQLite database
+│ │ │
+│ │ └── iosMain/ # iOS implementations (not used)
+│ │
+│ └── build.gradle.kts # App-level build configuration
+│
+├── database/
+│ └── schema.sql # SQL schema for database creation
+│
+├── docs/
+│ └── Deliverable_4/ # **Contains live demo & CRUD documentation**
+│
+├── gradle/ # Gradle wrapper files
+├── build.gradle.kts # Project-level build configuration
+├── settings.gradle.kts # Project settings
+└── README.md # This file
+```
+
+---
+
+## Prerequisites & Setup
+
+### **System Requirements**
+
+1. **Operating System**: Windows 10/11, macOS 10.15+, or Linux
+2. **RAM**: Minimum 8GB (16GB recommended for smooth emulator performance)
+3. **Disk Space**: At least 10GB free space
+
+### **Required Software**
+
+#### 1. **Install Java Development Kit (JDK)**
+- **Required Version**: JDK 17 or higher
+- **Download**: [Oracle JDK](https://www.oracle.com/java/technologies/downloads/) or [OpenJDK](https://adoptium.net/)
+- **Verify Installation**:
```bash
-sqlite3 findmyride.db < database/schema.sql
+ java -version
+ # Should show version 17+
```
-3. You can view the tables with:
+
+#### 2. **Install Android Studio**
+- **Required Version**: Android Studio Ladybug (2024.2.1) or newer
+- **Download**: [Android Studio Official Site](https://developer.android.com/studio)
+- **Important**: During installation, make sure to install:
+ - Android SDK
+ - Android SDK Platform
+ - Android Virtual Device (AVD)
+
+#### 3. **Install Kotlin Multiplatform Plugin**
+After installing Android Studio:
+1. Open Android Studio
+2. Go to **File → Settings** (or **Android Studio → Preferences** on macOS)
+3. Navigate to **Plugins**
+4. Search for "**Kotlin Multiplatform**"
+5. Click **Install** and restart Android Studio
+
+#### 4. **Configure Android SDK**
+1. In Android Studio, go to **File → Settings → Appearance & Behavior → System Settings → Android SDK**
+2. Ensure these are installed:
+ - **Android SDK Platform 34** (or higher)
+ - **Android SDK Build-Tools 34.0.0** (or higher)
+ - **Android Emulator**
+ - **Android SDK Platform-Tools**
+
+#### 5. **Set Up Android Emulator**
+1. In Android Studio, go to **Tools → Device Manager**
+2. Click **Create Device**
+3. Select a device definition (e.g., **Pixel 5**)
+4. Download and select a system image:
+ - Recommended: **API Level 34** (Android 14.0)
+ - Select **x86_64** or **ARM64** based on your system
+5. Click **Finish** to create the AVD
+
+#### 6. **Install SQLite (Optional - for database development)**
+- **Windows**: Download from [SQLite Download Page](https://www.sqlite.org/download.html)
+- **macOS**: Pre-installed, or install via Homebrew:
```bash
-sqlite3 findmyride.db
-sqlite> .tables
-```
\ No newline at end of file
+ brew install sqlite3
+```
+- **Linux**:
+```bash
+ sudo apt-get install sqlite3 # Ubuntu/Debian
+ sudo yum install sqlite # Fedora/RHEL
+```
+- **Verify Installation**:
+```bash
+ sqlite3 --version
+ # Should show version 3.x.x
+```
+
+---
+
+## How To Run the Project
+
+### **Step 1: Clone the Repository**
+```bash
+git clone https://github.com/bookwalamustafa/Find-My-Ride.git
+cd FindMyRide
+```
+
+### **Step 2: Open Project in Android Studio**
+1. Launch **Android Studio**
+2. Select **File → Open**
+3. Navigate to the `FindMyRide` folder and click **OK**
+4. Wait for **Gradle sync** to complete (this may take 3-5 minutes on first run)
+ - Watch the bottom status bar for "Gradle Build" progress
+ - If prompted, accept any SDK licenses
+
+### **Step 3: Verify Kotlin Multiplatform Setup**
+1. Check that no errors appear in the **Build** output window
+2. Verify the project structure shows `commonMain`, `androidMain`, and `iosMain` folders
+3. If you see any errors related to KMP, ensure the Kotlin Multiplatform plugin is installed
+
+### **Step 4: Select Run Configuration**
+1. In the top toolbar, find the **Run Configuration** dropdown
+2. Select **composeApp:androidApp** (or just **composeApp**)
+3. If you don't see this option, try **File → Sync Project with Gradle Files**
+
+### **Step 5: Launch the App**
+**Option A: Using Android Emulator (Recommended)**
+1. In the device dropdown (next to Run Configuration), select your created AVD
+2. Click the **Run** button (green play icon) or press **Shift + F10**
+3. Wait for the emulator to boot (30-60 seconds on first launch)
+4. The app will automatically install and launch
+
+**Option B: Using Physical Android Device**
+1. Enable **Developer Options** on your Android device:
+ - Go to **Settings → About Phone**
+ - Tap **Build Number** 7 times
+2. Enable **USB Debugging**:
+ - Go to **Settings → Developer Options**
+ - Enable **USB Debugging**
+3. Connect your device via USB
+4. Select your device from the device dropdown
+5. Click **Run**
+
+### **Step 6: First Launch**
+- On first launch, the app will automatically copy the SQLite database from `assets/` to the device storage
+- No additional configuration is needed
+- The database comes pre-populated with sample data for testing
+
+### **Troubleshooting Common Issues**
+
+| Issue | Solution |
+|-------|----------|
+| "SDK not found" error | Install Android SDK Platform 34 via SDK Manager |
+| Gradle sync fails | Check internet connection, clear Gradle cache (**File → Invalidate Caches**) |
+| Emulator won't start | Ensure hardware acceleration (Intel HAXM/AMD WHPX) is enabled in BIOS |
+| "Module not specified" error | Select **composeApp** run configuration |
+| Database not loading | Check that `findmyride.db` exists in `composeApp/src/androidMain/assets/` |
+
+---
+
+## Database Setup
+
+### **Using the Pre-loaded Database**
+The project comes with a ready-to-use SQLite database (`findmyride.db`) that includes:
+- Sample user accounts
+- Pre-populated ride offers
+- Example messages
+- Vehicle information
+
+**No manual database setup is required for normal use.**
+
+### **Creating a Fresh Database (Advanced)**
+
+If you want to create a new database from scratch:
+
+#### **Step 1: Navigate to Project Root**
+```bash
+cd /path/to/FindMyRide
+```
+
+#### **Step 2: Generate Database from Schema**
+Run the command ```python database/generate_database.py```
+
+And the entire database should be populated with mock data.
+---
+
+## CRUD Implementation Details
+
+Our application implements comprehensive **CRUD (Create, Read, Update, Delete)** operations across all major database tables. Below is a detailed breakdown of how each operation is implemented.
+
+### **Architecture Overview**
+```
+UI Layer (Compose)
+ ↓
+Repository Interface (commonMain)
+ ↓
+Repository Implementation (androidMain)
+ ↓
+SQLite Database (findmyride.db)
+```
+
+### **1. USER Table CRUD** (`AndroidAuthRepository.kt`)
+
+#### **Create (Registration)**
+[FILL IN: Explain how user registration works]
+- Example: "When a user registers, the `registerUser()` function..."
+- SQL query used
+- Password hashing approach
+- Validation checks
+
+#### **Read (Login & Profile Loading)**
+[FILL IN: Explain how user authentication and profile retrieval works]
+- Example: "Login validates credentials using..."
+- How user sessions are managed
+- Profile data retrieval process
+
+#### **Update (Profile Editing)**
+[FILL IN: Explain how profile updates work]
+- Example: "Users can update their profile information through..."
+- Fields that can be updated
+- Validation logic
+
+#### **Delete (Account Deletion)**
+[FILL IN: Explain account deletion]
+- Example: "Account deletion is handled by..."
+- Cascade behavior with related data
+- Soft delete vs hard delete approach
+
+---
+
+### **2. VEHICLE Table CRUD** (`AndroidProfileRepository.kt`)
+
+#### **Create (Add Vehicle)**
+[FILL IN: Vehicle creation details]
+
+#### **Read (View Vehicles)**
+[FILL IN: How vehicles are displayed]
+
+#### **Update (Edit Vehicle)**
+[FILL IN: Vehicle editing process]
+
+#### **Delete (Remove Vehicle)**
+[FILL IN: Vehicle removal logic]
+
+---
+
+### **3. RIDE_OFFER Table CRUD** (`AndroidRideRepository.kt`)
+
+#### **Create (Publish Ride)**
+[FILL IN: How drivers create ride offers]
+
+#### **Read (Browse Rides)**
+[FILL IN: How rides are listed and filtered]
+
+#### **Update (Modify Ride Details)**
+[FILL IN: Editing existing ride offers]
+
+#### **Delete (Cancel Ride)**
+[FILL IN: Ride cancellation process]
+
+---
+
+### **4. RIDE_REQUEST Table CRUD** (`AndroidRideRepository.kt`)
+
+#### **Create (Request a Ride)**
+[FILL IN: How passengers request rides]
+
+#### **Read (View Requests)**
+[FILL IN: Displaying ride requests]
+
+#### **Update (Update Request Status)**
+[FILL IN: Accepting/rejecting requests]
+
+#### **Delete (Cancel Request)**
+[FILL IN: Request cancellation]
+
+---
+
+### **5. MESSAGE Table CRUD** (`AndroidMessagesRepository.kt`)
+
+#### **Create (Send Message)**
+[FILL IN: Message sending implementation]
+
+#### **Read (View Messages)**
+[FILL IN: Message retrieval and display]
+
+#### **Update (Edit Message)**
+[FILL IN: If supported, how messages can be edited]
+
+#### **Delete (Delete Message)**
+[FILL IN: Message deletion process]
+
+---
+
+### **Database Transaction Handling**
+
+[FILL IN: Explain how you handle database transactions]
+- Example: "All CRUD operations use database transactions to ensure..."
+- Error handling approach
+- Rollback strategy for failed operations
+
+### **Data Validation**
+
+[FILL IN: Describe validation layers]
+- Example: "Input validation occurs at three levels..."
+- UI-level validation
+- Repository-level validation
+- Database constraints
+
+---
+
+## Key Implementation Notes
+
+### **State Management**
+- **UI State**: Uses Jetpack Compose's `remember { mutableStateOf() }` for reactive UI updates
+- **State Hoisting**: State is lifted to appropriate composable parents for sharing
+- **Side Effects**: `LaunchedEffect` and `DisposableEffect` for lifecycle-aware operations
+
+### **Navigation System**
+- **Sealed Classes**: Type-safe navigation using `sealed class RootScreen` and `sealed class HomePage`
+- **Manual Implementation**: No NavHost – custom navigation logic for learning purposes
+- **State Preservation**: Navigation state maintained across configuration changes
+
+### **Repository Pattern**
+- **Interface Definition**: All repositories defined as interfaces in `commonMain`
+- **Platform Implementation**: Concrete implementations in `androidMain` using SQLite
+- **Dependency Injection**: Repositories passed down through composition
+- **Single Source of Truth**: Database is the authoritative data source
+
+### **Database Management**
+- **Database Provider**: `FindMyRideDbProvider` handles initialization and copying from assets
+- **Connection Pooling**: Single database instance reused across app lifecycle
+- **Raw SQL Queries**: Direct SQL for transparency and learning (no ORM like Room or SQLDelight)
+- **Query Optimization**: Indexed columns for frequently queried fields
+
+### **Code Organization**
+- **Feature-Based Structure**: Each feature (auth, profile, rides, messages) is self-contained
+- **Separation of Concerns**: UI, business logic, and data access are clearly separated
+- **Shared Code**: Maximum code sharing through `commonMain` for cross-platform readiness
+
+### **Android-Specific Considerations**
+- **SQLiteOpenHelper**: Native Android database management for direct control
+- **Context Handling**: Database requires Android `Context` for file operations
+- **Assets Access**: Database file copied from `assets/` to internal storage on first run
+
+---
+
+## Documentation & Demo
+
+### **Live Demo & CRUD Operations**
+
+For a comprehensive demonstration of the application and detailed CRUD operation walkthroughs, please refer to:
+
+ **`docs/Deliverable_4/`**
+
+This folder contains:
+- **Live Demo Video**: Complete walkthrough of all application features
+- **CRUD Operation Examples**: Step-by-step demonstration of Create, Read, Update, and Delete operations for each database table
+- **Database Schema Documentation**: Detailed table structures and relationships
+- **Testing Scenarios**: Various use cases and edge cases tested during development
+
+**Recommended Viewing Order:**
+1. Start with the live demo video for an overview
+2. Review CRUD operation examples for technical details
+3. Reference database schema for understanding data relationships
+
+---
+
+## Future Improvements
+
+### **Feature Enhancements**
+- **Advanced Filtering**: Filter rides by time, price, available seats, and route
+- **Vehicle Picker**: Allow drivers to select from their registered vehicles when offering rides
+- **Enhanced Messaging**: Full chat UI with real-time updates per ride
+- **Notifications**: Push notifications for new ride offers and messages
+- **Matching Algorithm**: Intelligent ride request matching based on routes and preferences
+- **Rating System**: Allow users to rate drivers and passengers
+- **Payment Integration**: Add payment processing for ride fares
+
+### **Technical Improvements**
+- **iOS Support**: Implement database layer using SQLDelight or KMP-SQLite for iOS compatibility
+- **Backend Server**: Move to a centralized server with REST API for real-time data sync
+- **Enhanced Security**: Implement JWT authentication and encrypted data storage
+- **Testing**: Add unit tests, integration tests, and UI tests
+- **Theming**: Dark mode support and customizable themes
+- **Maps Integration**: Visual route display using Google Maps or Mapbox
+- **Analytics**: Track app usage and user behavior for insights
+
+### **Platform Expansion**
+- **iOS Native App**: Full iOS implementation with native database layer
+- **Web Version**: Browser-based version using Kotlin/JS
+- **Desktop Support**: Complete desktop app with database support
+
+---
+
+## Contact & Support
+
+For questions, issues, or contributions:
+
+- **Mustafa Bookwala**: [mmb479@drexel.edu/https://github.com/bookwalamustafa]
+- **Samii Shabuse**: [sus24@drexel.edu/https://github.com/sshabuse]
+- **Kennan Lu**: [kjl354@drexel.edu/kennanLu]
+
+**Course**: CS 461 - Database Systems
+**Institution**: Drexel University
+**Term**: Fall 2025
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..adfa9bf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1,19 @@
+*.iml
+.kotlin
+.gradle
+**/build/
+xcuserdata
+!src/**/build/
+local.properties
+.idea
+.DS_Store
+captures
+.externalNativeBuild
+.cxx
+*.xcodeproj/*
+!*.xcodeproj/project.pbxproj
+!*.xcodeproj/xcshareddata/
+!*.xcodeproj/project.xcworkspace/
+!*.xcworkspace/contents.xcworkspacedata
+**/xcshareddata/WorkspaceSettings.xcsettings
+node_modules/
diff --git a/app/README.md b/app/README.md
new file mode 100644
index 0000000..b405920
--- /dev/null
+++ b/app/README.md
@@ -0,0 +1,80 @@
+This is a Kotlin Multiplatform project targeting Android, iOS, Web, Desktop (JVM).
+
+* [/composeApp](composeApp/src) is for code that will be shared across your Compose Multiplatform applications.
+ It contains several subfolders:
+ - [commonMain](composeApp/src/commonMain/kotlin) is for code that’s common for all targets.
+ - Other folders are for Kotlin code that will be compiled for only the platform indicated in the folder name.
+ For example, if you want to use Apple’s CoreCrypto for the iOS part of your Kotlin app,
+ the [iosMain](composeApp/src/iosMain/kotlin) folder would be the right place for such calls.
+ Similarly, if you want to edit the Desktop (JVM) specific part, the [jvmMain](composeApp/src/jvmMain/kotlin)
+ folder is the appropriate location.
+
+* [/iosApp](iosApp/iosApp) contains iOS applications. Even if you’re sharing your UI with Compose Multiplatform,
+ you need this entry point for your iOS app. This is also where you should add SwiftUI code for your project.
+
+### Build and Run Android Application
+
+To build and run the development version of the Android app, use the run configuration from the run widget
+in your IDE’s toolbar or build it directly from the terminal:
+
+- on macOS/Linux
+ ```shell
+ ./gradlew :composeApp:assembleDebug
+ ```
+- on Windows
+ ```shell
+ .\gradlew.bat :composeApp:assembleDebug
+ ```
+
+### Build and Run Desktop (JVM) Application
+
+To build and run the development version of the desktop app, use the run configuration from the run widget
+in your IDE’s toolbar or run it directly from the terminal:
+
+- on macOS/Linux
+ ```shell
+ ./gradlew :composeApp:run
+ ```
+- on Windows
+ ```shell
+ .\gradlew.bat :composeApp:run
+ ```
+
+### Build and Run Web Application
+
+To build and run the development version of the web app, use the run configuration from the run widget
+in your IDE's toolbar or run it directly from the terminal:
+
+- for the Wasm target (faster, modern browsers):
+ - on macOS/Linux
+ ```shell
+ ./gradlew :composeApp:wasmJsBrowserDevelopmentRun
+ ```
+ - on Windows
+ ```shell
+ .\gradlew.bat :composeApp:wasmJsBrowserDevelopmentRun
+ ```
+- for the JS target (slower, supports older browsers):
+ - on macOS/Linux
+ ```shell
+ ./gradlew :composeApp:jsBrowserDevelopmentRun
+ ```
+ - on Windows
+ ```shell
+ .\gradlew.bat :composeApp:jsBrowserDevelopmentRun
+ ```
+
+### Build and Run iOS Application
+
+To build and run the development version of the iOS app, use the run configuration from the run widget
+in your IDE’s toolbar or open the [/iosApp](iosApp) directory in Xcode and run it from there.
+
+---
+
+Learn more about [Kotlin Multiplatform](https://www.jetbrains.com/help/kotlin-multiplatform-dev/get-started.html),
+[Compose Multiplatform](https://github.com/JetBrains/compose-multiplatform/#compose-multiplatform),
+[Kotlin/Wasm](https://kotl.in/wasm/)…
+
+We would appreciate your feedback on Compose/Web and Kotlin/Wasm in the public Slack
+channel [#compose-web](https://slack-chats.kotlinlang.org/c/compose-web).
+If you face any issues, please report them on [YouTrack](https://youtrack.jetbrains.com/newIssue?project=CMP).
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
new file mode 100644
index 0000000..98ddb8c
--- /dev/null
+++ b/app/build.gradle.kts
@@ -0,0 +1,10 @@
+plugins {
+ // this is necessary to avoid the plugins to be loaded multiple times
+ // in each subproject's classloader
+ alias(libs.plugins.androidApplication) apply false
+ alias(libs.plugins.androidLibrary) apply false
+ alias(libs.plugins.composeHotReload) apply false
+ alias(libs.plugins.composeMultiplatform) apply false
+ alias(libs.plugins.composeCompiler) apply false
+ alias(libs.plugins.kotlinMultiplatform) apply false
+}
\ No newline at end of file
diff --git a/app/composeApp/build.gradle.kts b/app/composeApp/build.gradle.kts
new file mode 100644
index 0000000..d290519
--- /dev/null
+++ b/app/composeApp/build.gradle.kts
@@ -0,0 +1,110 @@
+import org.jetbrains.compose.desktop.application.dsl.TargetFormat
+import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
+import org.jetbrains.kotlin.gradle.dsl.JvmTarget
+
+plugins {
+ alias(libs.plugins.kotlinMultiplatform)
+ alias(libs.plugins.androidApplication)
+ alias(libs.plugins.composeMultiplatform)
+ alias(libs.plugins.composeCompiler)
+ alias(libs.plugins.composeHotReload)
+}
+
+kotlin {
+ androidTarget {
+ compilerOptions {
+ jvmTarget.set(JvmTarget.JVM_21)
+ }
+ }
+
+ listOf(
+ iosArm64(),
+ iosSimulatorArm64()
+ ).forEach { iosTarget ->
+ iosTarget.binaries.framework {
+ baseName = "ComposeApp"
+ isStatic = true
+ }
+ }
+
+ jvm()
+
+ js {
+ browser()
+ binaries.executable()
+ }
+
+ @OptIn(ExperimentalWasmDsl::class)
+ wasmJs {
+ browser()
+ binaries.executable()
+ }
+
+ sourceSets {
+ androidMain.dependencies {
+ implementation(compose.preview)
+ implementation(libs.androidx.activity.compose)
+ }
+ commonMain.dependencies {
+ implementation(compose.runtime)
+ implementation(compose.foundation)
+ implementation(compose.material3)
+ implementation(compose.ui)
+ implementation(compose.components.resources)
+ implementation(compose.components.uiToolingPreview)
+ implementation(libs.androidx.lifecycle.viewmodelCompose)
+ implementation(libs.androidx.lifecycle.runtimeCompose)
+ implementation(compose.materialIconsExtended)
+ }
+ commonTest.dependencies {
+ implementation(libs.kotlin.test)
+ }
+ jvmMain.dependencies {
+ implementation(compose.desktop.currentOs)
+ implementation(libs.kotlinx.coroutinesSwing)
+ }
+ }
+}
+
+android {
+ namespace = "com.example.app"
+ compileSdk = libs.versions.android.compileSdk.get().toInt()
+
+ defaultConfig {
+ applicationId = "com.example.app"
+ minSdk = libs.versions.android.minSdk.get().toInt()
+ targetSdk = libs.versions.android.targetSdk.get().toInt()
+ versionCode = 1
+ versionName = "1.0"
+ }
+ packaging {
+ resources {
+ excludes += "/META-INF/{AL2.0,LGPL2.1}"
+ }
+ }
+ buildTypes {
+ getByName("release") {
+ isMinifyEnabled = false
+ }
+ }
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_21
+ targetCompatibility = JavaVersion.VERSION_21
+ }
+}
+
+dependencies {
+ debugImplementation(compose.uiTooling)
+}
+
+compose.desktop {
+ application {
+ mainClass = "com.example.app.MainKt"
+
+ nativeDistributions {
+ targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
+ packageName = "com.example.app"
+ packageVersion = "1.0.0"
+ }
+ }
+}
diff --git a/app/composeApp/src/androidMain/AndroidManifest.xml b/app/composeApp/src/androidMain/AndroidManifest.xml
new file mode 100644
index 0000000..d973028
--- /dev/null
+++ b/app/composeApp/src/androidMain/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/composeApp/src/androidMain/assets/findmyride.db b/app/composeApp/src/androidMain/assets/findmyride.db
new file mode 100644
index 0000000..afaffe5
Binary files /dev/null and b/app/composeApp/src/androidMain/assets/findmyride.db differ
diff --git a/app/composeApp/src/androidMain/kotlin/com/example/demo/AndroidAuthRepository.kt b/app/composeApp/src/androidMain/kotlin/com/example/demo/AndroidAuthRepository.kt
new file mode 100644
index 0000000..8e74451
--- /dev/null
+++ b/app/composeApp/src/androidMain/kotlin/com/example/demo/AndroidAuthRepository.kt
@@ -0,0 +1,116 @@
+package com.example.demo
+
+import app.composeapp.generated.resources.Res
+import com.example.demo.feature.auth.data.AuthRepository
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+
+/**
+ * Android implementation of AuthRepository that talks to the findmyride.db SQLite database.
+ */
+class AndroidAuthRepository(
+ private val dbProvider: FindMyRideDbProvider
+) : AuthRepository {
+
+
+ override suspend fun login(email: String, password: String): Result =
+ withContext(Dispatchers.IO) {
+ val db = dbProvider.getReadableDatabase()
+
+ val cursor = db.rawQuery(
+ """
+ SELECT user_id, email, username, role
+ FROM "USER"
+ WHERE email = ? AND password_hash = ?
+ """.trimIndent(),
+ arrayOf(email, password)
+ )
+
+ cursor.use {
+ return@withContext if (it.moveToFirst()) {
+ val idxId = it.getColumnIndexOrThrow("user_id")
+ val idxEmail = it.getColumnIndexOrThrow("email")
+ val idxUsername = it.getColumnIndexOrThrow("username")
+ val idxRole = it.getColumnIndexOrThrow("role")
+
+ // Remember who is logged in
+ CurrentUserStore.userId = it.getLong(idxId)
+ CurrentUserStore.email = it.getString(idxEmail)
+ CurrentUserStore.username = it.getString(idxUsername)
+ CurrentUserStore.role = it.getString(idxRole)
+
+ Result.success(Unit)
+ } else {
+ // Clear any previous user
+ CurrentUserStore.userId = null
+ CurrentUserStore.email = null
+ CurrentUserStore.username = null
+ CurrentUserStore.role = null
+
+ Result.failure(IllegalArgumentException("Invalid email or password"))
+ }
+ }
+ }
+
+ // Basic implementation so Sign Up / Forgot Password don't crash,
+ // we can improve these later.
+ override suspend fun signUp(
+ fullName: String,
+ email: String,
+ password: String
+ ): Result =
+ withContext(Dispatchers.IO) {
+ val db = dbProvider.getWritableDatabase()
+
+ // Check if email already exists
+ db.rawQuery(
+ """SELECT 1 FROM "USER" WHERE email = ? LIMIT 1;""",
+ arrayOf(email)
+ ).use { c ->
+ if (c.moveToFirst()) {
+ return@withContext Result.failure(
+ IllegalArgumentException("Email already registered")
+ )
+ }
+ }
+
+ val username = if (fullName.isNotBlank()) {
+ fullName.trim().lowercase().replace(" ", "_")
+ } else {
+ email.substringBefore('@')
+ }
+
+ val stmt = db.compileStatement(
+ """
+ INSERT INTO "USER"(email, username, password_hash, role)
+ VALUES(?, ?, ?, 'rider');
+ """.trimIndent()
+ )
+ stmt.bindString(1, email)
+ stmt.bindString(2, username)
+ stmt.bindString(3, password)
+
+ val rowId = stmt.executeInsert()
+ if (rowId == -1L) {
+ Result.failure(IllegalStateException("Failed to create account"))
+ } else {
+ Result.success(Unit)
+ }
+ }
+
+ override suspend fun sendPasswordReset(email: String): Result =
+ withContext(Dispatchers.IO) {
+ val db = dbProvider.getReadableDatabase()
+ db.rawQuery(
+ """SELECT 1 FROM "USER" WHERE email = ? LIMIT 1;""",
+ arrayOf(email)
+ ).use { c ->
+ return@withContext if (c.moveToFirst()) {
+ // Pretend an email was sent
+ Result.success(Unit)
+ } else {
+ Result.failure(IllegalArgumentException("No account found for this email"))
+ }
+ }
+ }
+}
diff --git a/app/composeApp/src/androidMain/kotlin/com/example/demo/AndroidMessagesRepository.kt b/app/composeApp/src/androidMain/kotlin/com/example/demo/AndroidMessagesRepository.kt
new file mode 100644
index 0000000..600f517
--- /dev/null
+++ b/app/composeApp/src/androidMain/kotlin/com/example/demo/AndroidMessagesRepository.kt
@@ -0,0 +1,239 @@
+package com.example.demo.feature.messages.data
+
+import com.example.demo.CurrentUserStore
+import com.example.demo.FindMyRideDbProvider
+import com.example.demo.feature.messages.chat.ChatMessageUi
+import com.example.demo.feature.messages.list.MessageThreadUi
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+
+class AndroidMessagesRepository(
+ private val dbProvider: FindMyRideDbProvider
+) : MessagesRepository {
+
+ /**
+ * Use logged-in user if available, otherwise fall back to parameter.
+ */
+ private fun resolveCurrentUserId(paramUserId: Int): Int {
+// val stored = CurrentUserStore.userId
+// return stored?.toInt() ?: paramUserId
+ return 1;
+ }
+
+// override suspend fun getThreadsForUser(userId: Int): List =
+// withContext(Dispatchers.IO) {
+// listOf(
+// MessageThreadUi(
+// id = 1,
+// senderName = "Debug User",
+// initials = "DU",
+// lastMessage = "If you see this, repo wiring works!",
+// timeAgo = "now",
+// unreadCount = 0
+// )
+// )
+// }
+
+ private fun seedFakeMessagesIfEmpty(db: android.database.sqlite.SQLiteDatabase) {
+ // Check if we already seeded
+ db.rawQuery("SELECT COUNT(*) FROM MESSAGE_THREAD;", null).use { c ->
+ if (c.moveToFirst()) {
+ val count = c.getInt(0)
+ if (count > 0) return // already has data, no need to seed
+ }
+ }
+
+ // ---- THREADS ----
+ // All involving user_id = 1 so they'll show when you log in as that user
+ db.execSQL("""
+ INSERT INTO MESSAGE_THREAD (thread_id, user1_id, user2_id)
+ VALUES
+ (1, 1, 2),
+ (2, 1, 3),
+ (3, 1, 4);
+ """.trimIndent())
+
+ // ---- MESSAGES ----
+ // Thread 1: Abdul <-> Quincy
+ db.execSQL("""
+ INSERT INTO MESSAGE (thread_id, sender_id, body) VALUES
+ (1, 1, 'Hey Quincy, are we still on for 5:30 PM?'),
+ (1, 2, 'Yes! I''ll be there in 10 minutes.'),
+ (1, 1, 'Perfect, see you soon.');
+ """.trimIndent())
+
+ // Thread 2: Abdul <-> Ame
+ db.execSQL("""
+ INSERT INTO MESSAGE (thread_id, sender_id, body) VALUES
+ (2, 3, 'Hey Abdul, do you still need a ride tomorrow?'),
+ (2, 1, 'Yeah! Morning around 9 would be amazing.'),
+ (2, 3, 'Got you, I''ll swing by then.');
+ """.trimIndent())
+
+ // Thread 3: Abdul <-> Kennan
+ db.execSQL("""
+ INSERT INTO MESSAGE (thread_id, sender_id, body) VALUES
+ (3, 1, 'Thanks again for the last ride!'),
+ (3, 4, 'No problem, happy to help.'),
+ (3, 1, 'I left you a 5-star rating too :)');
+ """.trimIndent())
+ }
+
+
+ override suspend fun getThreadsForUser(userId: Int): List =
+ withContext(Dispatchers.IO) {
+ val db = dbProvider.getWritableDatabase()
+ val currentUserId = 1 // you’re logging in as user_id = 1
+
+ seedFakeMessagesIfEmpty(db)
+
+ val sql = """
+ SELECT
+ t.thread_id,
+ CASE
+ WHEN t.user1_id = ? THEN u2.username
+ ELSE u1.username
+ END AS contact_name,
+ COALESCE(last_msg.body, 'No messages yet') AS last_message,
+ last_msg.sent_at AS last_sent_at
+ FROM MESSAGE_THREAD t
+ JOIN "USER" u1 ON t.user1_id = u1.user_id
+ JOIN "USER" u2 ON t.user2_id = u2.user_id
+ LEFT JOIN MESSAGE last_msg ON last_msg.message_id = (
+ SELECT m.message_id
+ FROM MESSAGE m
+ WHERE m.thread_id = t.thread_id
+ ORDER BY m.sent_at DESC, m.message_id DESC
+ LIMIT 1
+ )
+ WHERE t.user1_id = ? OR t.user2_id = ?
+ ORDER BY
+ (last_sent_at IS NULL),
+ last_sent_at DESC,
+ t.thread_id DESC;
+ """.trimIndent()
+
+ val args = arrayOf(
+ currentUserId.toString(),
+ currentUserId.toString(),
+ currentUserId.toString()
+ )
+
+ val cursor = db.rawQuery(sql, args)
+ val result = mutableListOf()
+
+ cursor.use { c ->
+ val idxThreadId = c.getColumnIndexOrThrow("thread_id")
+ val idxName = c.getColumnIndexOrThrow("contact_name")
+ val idxLastMsg = c.getColumnIndexOrThrow("last_message")
+ val idxLastSentAt = c.getColumnIndexOrThrow("last_sent_at")
+
+ while (c.moveToNext()) {
+ val name = c.getString(idxName)
+ val lastMsg = c.getString(idxLastMsg)
+ val sentAt = c.getString(idxLastSentAt) ?: ""
+
+ result += MessageThreadUi(
+ id = c.getInt(idxThreadId),
+ senderName = name,
+ initials = initialsFromName(name),
+ lastMessage = lastMsg,
+ timeAgo = sentAt,
+ unreadCount = 0
+ )
+ }
+ }
+
+ result
+ }
+
+ override suspend fun getMessagesForThread(threadId: Int): List =
+ withContext(Dispatchers.IO) {
+ val db = dbProvider.getReadableDatabase()
+ val currentUserId = resolveCurrentUserId(1)
+
+ val cursor = db.rawQuery(
+ """
+ SELECT message_id, sender_id, body, sent_at
+ FROM MESSAGE
+ WHERE thread_id = ?
+ ORDER BY sent_at ASC, message_id ASC;
+ """.trimIndent(),
+ arrayOf(threadId.toString())
+ )
+
+ val result = mutableListOf()
+ cursor.use { c ->
+ val idxId = c.getColumnIndexOrThrow("message_id")
+ val idxSender= c.getColumnIndexOrThrow("sender_id")
+ val idxBody = c.getColumnIndexOrThrow("body")
+ val idxTime = c.getColumnIndexOrThrow("sent_at")
+
+ while (c.moveToNext()) {
+ val senderId = c.getInt(idxSender)
+ result += ChatMessageUi(
+ id = c.getInt(idxId),
+ isMe = (senderId == currentUserId),
+ text = c.getString(idxBody),
+ time = c.getString(idxTime)
+ )
+ }
+ }
+ result
+ }
+
+ override suspend fun sendMessage(threadId: Int, text: String): ChatMessageUi =
+ withContext(Dispatchers.IO) {
+ val db = dbProvider.getWritableDatabase()
+ val currentUserId = resolveCurrentUserId(1)
+
+ // Insert message
+ val insertStmt = db.compileStatement(
+ """
+ INSERT INTO MESSAGE(thread_id, sender_id, body)
+ VALUES (?, ?, ?);
+ """.trimIndent()
+ )
+ insertStmt.bindLong(1, threadId.toLong())
+ insertStmt.bindLong(2, currentUserId.toLong())
+ insertStmt.bindString(3, text)
+ val newIdLong = insertStmt.executeInsert()
+
+ // Get timestamp (sent_at)
+ val cursor = db.rawQuery(
+ """
+ SELECT sent_at
+ FROM MESSAGE
+ WHERE message_id = ?;
+ """.trimIndent(),
+ arrayOf(newIdLong.toString())
+ )
+
+ var sentAt = "Now"
+ cursor.use { c ->
+ if (c.moveToFirst()) {
+ sentAt = c.getString(c.getColumnIndexOrThrow("sent_at"))
+ }
+ }
+
+ ChatMessageUi(
+ id = newIdLong.toInt(),
+ isMe = true,
+ text = text,
+ time = sentAt
+ )
+ }
+
+ private fun initialsFromName(name: String): String {
+ val parts = name.trim().split(" ")
+ .filter { it.isNotBlank() }
+
+ val chars = when {
+ parts.isEmpty() -> listOf('U')
+ parts.size == 1 -> listOf(parts[0].first())
+ else -> listOf(parts[0].first(), parts[1].first())
+ }
+
+ return chars.joinToString("").uppercase()
+ }
+}
diff --git a/app/composeApp/src/androidMain/kotlin/com/example/demo/AndroidProfileRepository.kt b/app/composeApp/src/androidMain/kotlin/com/example/demo/AndroidProfileRepository.kt
new file mode 100644
index 0000000..1c9ba60
--- /dev/null
+++ b/app/composeApp/src/androidMain/kotlin/com/example/demo/AndroidProfileRepository.kt
@@ -0,0 +1,197 @@
+package com.example.demo.feature.profile.data
+
+import com.example.demo.CurrentUserStore
+import com.example.demo.FindMyRideDbProvider
+import com.example.demo.feature.profile.ProfileUiState
+import com.example.demo.feature.profile.VehicleUi
+
+/**
+ * Android implementation of ProfileRepository that reads/writes
+ * the USER and VEHICLE tables from findmyride.db
+ */
+class AndroidProfileRepository(
+ private val dbProvider: FindMyRideDbProvider
+) : ProfileRepository {
+
+ private fun requireCurrentUserId(): Long {
+ return CurrentUserStore.userId
+ ?: error("No logged-in user. Make sure login ran successfully before opening Profile.")
+ }
+
+ override fun loadInitialProfile(): ProfileUiState {
+ val currentUserId = requireCurrentUserId()
+ val db = dbProvider.getReadableDatabase()
+
+ // ----- USER -----
+ var name = "Unknown"
+ var email = ""
+ var phone = ""
+ var rating = 0.0
+
+ db.rawQuery(
+ """
+ SELECT username, email, phone_number, rating_avg
+ FROM "USER"
+ WHERE user_id = ?
+ LIMIT 1;
+ """.trimIndent(),
+ arrayOf(currentUserId.toString())
+ ).use { c ->
+ if (c.moveToFirst()) {
+ name = c.getString(c.getColumnIndexOrThrow("username"))
+ email = c.getString(c.getColumnIndexOrThrow("email"))
+ phone = c.getString(c.getColumnIndexOrThrow("phone_number")) ?: ""
+ rating = c.getDouble(c.getColumnIndexOrThrow("rating_avg"))
+ }
+ }
+
+ // ----- VEHICLES -----
+ val vehicles = mutableListOf()
+ db.rawQuery(
+ """
+ SELECT vehicle_id, make, model, color, plate, seats_total, year, fun_fact
+ FROM "VEHICLE"
+ WHERE owner_user_id = ?
+ ORDER BY vehicle_id;
+ """.trimIndent(),
+ arrayOf(currentUserId.toString())
+ ).use { c ->
+ val idxId = c.getColumnIndexOrThrow("vehicle_id")
+ val idxMake = c.getColumnIndexOrThrow("make")
+ val idxModel = c.getColumnIndexOrThrow("model")
+ val idxColor = c.getColumnIndexOrThrow("color")
+ val idxPlate = c.getColumnIndexOrThrow("plate")
+ val idxSeats = c.getColumnIndexOrThrow("seats_total")
+ val idxYear = c.getColumnIndexOrThrow("year")
+ val idxFunFact = c.getColumnIndexOrThrow("fun_fact")
+
+ while (c.moveToNext()) {
+ vehicles += VehicleUi(
+ id = c.getInt(idxId),
+ ownerUserId = currentUserId.toInt(),
+ make = c.getString(idxMake),
+ model = c.getString(idxModel),
+ color = c.getString(idxColor) ?: "",
+ plate = c.getString(idxPlate),
+ seatsTotal = c.getInt(idxSeats),
+ year = c.getInt(idxYear),
+ funFact = c.getString(idxFunFact) ?: ""
+ )
+ }
+ }
+
+ // Build ProfileUiState using DB values
+ return ProfileUiState(
+ name = name,
+ email = email,
+ rating = rating,
+ vehicles = vehicles,
+ account = ProfileUiState().account.copy(
+ fullName = name,
+ email = email,
+ phone = phone,
+ // we don't show real password here
+ password = "*******"
+ )
+ )
+ }
+
+ override fun saveProfile(state: ProfileUiState) {
+ val currentUserId = requireCurrentUserId()
+ val db = dbProvider.getWritableDatabase()
+ db.beginTransaction()
+ try {
+ // ----- UPDATE USER -----
+ db.compileStatement(
+ """
+ UPDATE "USER"
+ SET username = ?, email = ?, phone_number = ?
+ WHERE user_id = ?;
+ """.trimIndent()
+ ).apply {
+ bindString(1, state.account.fullName)
+ bindString(2, state.account.email)
+ bindString(3, state.account.phone)
+ bindLong(4, currentUserId)
+ executeUpdateDelete()
+ }
+
+ // ----- SYNC VEHICLES -----
+ // Get existing vehicle ids in DB
+ val existingIds = mutableSetOf()
+ db.rawQuery(
+ """
+ SELECT vehicle_id
+ FROM "VEHICLE"
+ WHERE owner_user_id = ?;
+ """.trimIndent(),
+ arrayOf(currentUserId.toString())
+ ).use { c ->
+ val idx = c.getColumnIndexOrThrow("vehicle_id")
+ while (c.moveToNext()) existingIds += c.getInt(idx)
+ }
+
+ val desiredIds = state.vehicles.map { it.id }.toSet()
+
+ // Upsert each vehicle from UI state
+ state.vehicles.forEach { v ->
+ if (existingIds.contains(v.id)) {
+ // UPDATE
+ db.compileStatement(
+ """
+ UPDATE "VEHICLE"
+ SET make = ?, model = ?, color = ?, plate = ?,
+ seats_total = ?, year = ?, fun_fact = ?
+ WHERE vehicle_id = ?;
+ """.trimIndent()
+ ).apply {
+ bindString(1, v.make)
+ bindString(2, v.model)
+ bindString(3, v.color)
+ bindString(4, v.plate)
+ bindLong(5, v.seatsTotal.toLong())
+ bindLong(6, v.year.toLong())
+ bindString(7, v.funFact)
+ bindLong(8, v.id.toLong())
+ executeUpdateDelete()
+ }
+ } else {
+ // INSERT with explicit vehicle_id (matches the id created in ViewModel)
+ db.compileStatement(
+ """
+ INSERT INTO "VEHICLE"(
+ vehicle_id, owner_user_id, make, model, color,
+ plate, seats_total, year, fun_fact
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);
+ """.trimIndent()
+ ).apply {
+ bindLong(1, v.id.toLong())
+ bindLong(2, currentUserId)
+ bindString(3, v.make)
+ bindString(4, v.model)
+ bindString(5, v.color)
+ bindString(6, v.plate)
+ bindLong(7, v.seatsTotal.toLong())
+ bindLong(8, v.year.toLong())
+ bindString(9, v.funFact)
+ executeInsert()
+ }
+ }
+ }
+
+ // Delete vehicles that were removed in the UI
+ (existingIds - desiredIds).forEach { idToDelete ->
+ db.compileStatement(
+ """DELETE FROM "VEHICLE" WHERE vehicle_id = ?;""".trimIndent()
+ ).apply {
+ bindLong(1, idToDelete.toLong())
+ executeUpdateDelete()
+ }
+ }
+
+ db.setTransactionSuccessful()
+ } finally {
+ db.endTransaction()
+ }
+ }
+}
diff --git a/app/composeApp/src/androidMain/kotlin/com/example/demo/AndroidRideRepository.kt b/app/composeApp/src/androidMain/kotlin/com/example/demo/AndroidRideRepository.kt
new file mode 100644
index 0000000..335ba12
--- /dev/null
+++ b/app/composeApp/src/androidMain/kotlin/com/example/demo/AndroidRideRepository.kt
@@ -0,0 +1,117 @@
+package com.example.demo
+
+import android.content.ContentValues
+import com.example.demo.feature.db.*
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+
+class AndroidRideRepository(
+ private val dbProvider: FindMyRideDbProvider
+) : RideRepository {
+
+ override suspend fun getOpenRideOffers(): List =
+ withContext(Dispatchers.IO) {
+ val db = dbProvider.getReadableDatabase()
+
+ // Example: join RIDE_OFFER with LOCATION to get names
+ val sql = """
+ SELECT
+ o.offer_id,
+ o.driver_id,
+ o.vehicle_id,
+ lo_from.name AS from_name,
+ lo_to.name AS to_name,
+ o.depart_at,
+ o.seats_available,
+ o.price_base
+ FROM RIDE_OFFER o
+ JOIN LOCATION lo_from ON o.original_location_id = lo_from.location_id
+ JOIN LOCATION lo_to ON o.dest_location_id = lo_to.location_id
+ WHERE o.status = 'open'
+ ORDER BY o.depart_at ASC;
+ """.trimIndent()
+
+ val cursor = db.rawQuery(sql, null)
+
+ val list = mutableListOf()
+ cursor.use {
+ val idxOfferId = it.getColumnIndexOrThrow("offer_id")
+ val idxDriverId = it.getColumnIndexOrThrow("driver_id")
+ val idxVehicleId = it.getColumnIndexOrThrow("vehicle_id")
+ val idxFromName = it.getColumnIndexOrThrow("from_name")
+ val idxToName = it.getColumnIndexOrThrow("to_name")
+ val idxDepartAt = it.getColumnIndexOrThrow("depart_at")
+ val idxSeatsAvail = it.getColumnIndexOrThrow("seats_available")
+ val idxPriceBase = it.getColumnIndexOrThrow("price_base")
+
+ while (it.moveToNext()) {
+ list.add(
+ RideOffer(
+ offerId = it.getLong(idxOfferId),
+ driverId = it.getLong(idxDriverId),
+ vehicleId = it.getLong(idxVehicleId),
+ fromName = it.getString(idxFromName),
+ toName = it.getString(idxToName),
+ departAt = it.getString(idxDepartAt),
+ seatsAvailable = it.getInt(idxSeatsAvail),
+ priceBase = it.getDouble(idxPriceBase)
+ )
+ )
+ }
+ }
+ list
+ }
+
+ override suspend fun createRideRequest(
+ riderId: Long,
+ pickupLocationId: Long,
+ dropoffLocationId: Long,
+ earliestPickup: String,
+ latestPickup: String?,
+ seatsNeeded: Int
+ ) {
+ withContext(Dispatchers.IO) {
+ val db = dbProvider.getWritableDatabase()
+ val values = ContentValues().apply {
+ put("rider_id", riderId)
+ put("pickup_location_id", pickupLocationId)
+ put("dropoff_location_id", dropoffLocationId)
+ put("earliest_pickup", earliestPickup)
+ put("latest_pickup", latestPickup)
+ put("seats_needed", seatsNeeded)
+ put("status", "open")
+ }
+ db.insert("RIDE_REQUEST", null, values)
+ }
+ }
+
+ override suspend fun createRideOffer(
+ driverId: Long,
+ vehicleId: Long,
+ originalLocationId: Long,
+ destLocationId: Long,
+ departAt: String,
+ seatsAvailable: Int,
+ priceBase: Double,
+ pricePerMile: Double
+ ) {
+ withContext(Dispatchers.IO) {
+ val db = dbProvider.getWritableDatabase()
+
+ val values = ContentValues().apply {
+ put("driver_id", driverId)
+ put("vehicle_id", vehicleId)
+ put("original_location_id", originalLocationId)
+ put("dest_location_id", destLocationId)
+ put("depart_at", departAt)
+ put("seats_available", seatsAvailable)
+ put("price_base", priceBase)
+ put("price_per_mile", pricePerMile)
+ put("status", "open")
+ }
+
+ db.insert("RIDE_OFFER", null, values)
+ }
+ }
+
+}
diff --git a/app/composeApp/src/androidMain/kotlin/com/example/demo/CurrentUserStore.kt b/app/composeApp/src/androidMain/kotlin/com/example/demo/CurrentUserStore.kt
new file mode 100644
index 0000000..cf175de
--- /dev/null
+++ b/app/composeApp/src/androidMain/kotlin/com/example/demo/CurrentUserStore.kt
@@ -0,0 +1,14 @@
+package com.example.demo
+
+/**
+ * Simple in-memory store for the currently logged-in user.
+ * Android only; commonMain doesn't see this directly.
+ */
+object CurrentUserStore {
+ var userId: Long? = null
+ var email: String? = null
+ var username: String? = null
+ var role: String? = null
+}
+
+// This is just a global holder on the Android side.
\ No newline at end of file
diff --git a/app/composeApp/src/androidMain/kotlin/com/example/demo/FindMyRideDbProvider.kt b/app/composeApp/src/androidMain/kotlin/com/example/demo/FindMyRideDbProvider.kt
new file mode 100644
index 0000000..6adb08a
--- /dev/null
+++ b/app/composeApp/src/androidMain/kotlin/com/example/demo/FindMyRideDbProvider.kt
@@ -0,0 +1,48 @@
+package com.example.demo
+
+import android.content.Context
+import android.database.sqlite.SQLiteDatabase
+import java.io.FileOutputStream
+
+class FindMyRideDbProvider(private val context: Context) {
+ private val dbName = "findmyride.db"
+
+ private fun ensureDbCopied() {
+ val dbFile = context.getDatabasePath(dbName)
+
+ // Always overwrite old database to ensure latest version is used
+ dbFile.parentFile?.mkdirs()
+
+ // Delete any existing DB
+ if (dbFile.exists()) {
+ dbFile.delete()
+ }
+
+ // Copy fresh DB from assets
+ context.assets.open(dbName).use { input ->
+ dbFile.outputStream().use { output ->
+ input.copyTo(output)
+ }
+ }
+ }
+
+ fun getReadableDatabase(): SQLiteDatabase {
+ ensureDbCopied()
+ val dbFile = context.getDatabasePath(dbName)
+ return SQLiteDatabase.openDatabase(
+ dbFile.path,
+ null,
+ SQLiteDatabase.OPEN_READONLY
+ )
+ }
+
+ fun getWritableDatabase(): SQLiteDatabase {
+ ensureDbCopied()
+ val dbFile = context.getDatabasePath(dbName)
+ return SQLiteDatabase.openDatabase(
+ dbFile.path,
+ null,
+ SQLiteDatabase.OPEN_READWRITE
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/composeApp/src/androidMain/kotlin/com/example/demo/MainActivity.kt b/app/composeApp/src/androidMain/kotlin/com/example/demo/MainActivity.kt
new file mode 100644
index 0000000..9ff986d
--- /dev/null
+++ b/app/composeApp/src/androidMain/kotlin/com/example/demo/MainActivity.kt
@@ -0,0 +1,43 @@
+package com.example.rideshare
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalContext
+import com.example.demo.App
+import com.example.demo.AndroidRideRepository
+import com.example.demo.AndroidAuthRepository
+import com.example.demo.FindMyRideDbProvider
+import com.example.demo.CurrentUserStore
+import com.example.demo.feature.profile.data.AndroidProfileRepository
+import com.example.demo.feature.messages.data.AndroidMessagesRepository
+
+class MainActivity : ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContent {
+ MaterialTheme {
+ Surface {
+ val context = LocalContext.current
+ val dbProvider = remember { FindMyRideDbProvider(context) }
+
+ val rideRepo = remember { AndroidRideRepository(dbProvider) }
+ val profileRepo = remember { AndroidProfileRepository(dbProvider) }
+ val authRepo = remember { AndroidAuthRepository(dbProvider) }
+ val messagesRepo = remember { AndroidMessagesRepository(dbProvider) }
+
+ App(
+ rideRepository = rideRepo,
+ profileRepository = profileRepo,
+ authRepository = authRepo,
+ messagesRepository = messagesRepo
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/app/composeApp/src/androidMain/kotlin/com/example/demo/Platform.android.kt b/app/composeApp/src/androidMain/kotlin/com/example/demo/Platform.android.kt
new file mode 100644
index 0000000..ba841d1
--- /dev/null
+++ b/app/composeApp/src/androidMain/kotlin/com/example/demo/Platform.android.kt
@@ -0,0 +1,9 @@
+package com.example.app
+
+import android.os.Build
+
+class AndroidPlatform : Platform {
+ override val name: String = "Android ${Build.VERSION.SDK_INT}"
+}
+
+actual fun getPlatform(): Platform = AndroidPlatform()
\ No newline at end of file
diff --git a/app/composeApp/src/androidMain/kotlin/com/example/demo/RideShareDbHelper.kt b/app/composeApp/src/androidMain/kotlin/com/example/demo/RideShareDbHelper.kt
new file mode 100644
index 0000000..777c33d
--- /dev/null
+++ b/app/composeApp/src/androidMain/kotlin/com/example/demo/RideShareDbHelper.kt
@@ -0,0 +1,29 @@
+package com.example.demo
+
+import android.content.Context
+import android.database.sqlite.SQLiteDatabase
+import android.database.sqlite.SQLiteOpenHelper
+
+class RideShareDbHelper(context: Context) :
+ SQLiteOpenHelper(context, "rideshare.db", null, 1) {
+
+ override fun onCreate(db: SQLiteDatabase) {
+ // Create a super simple table for now
+ db.execSQL(
+ """
+ CREATE TABLE IF NOT EXISTS rides(
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ pickup TEXT NOT NULL,
+ dropoff TEXT NOT NULL,
+ ride_time TEXT NOT NULL
+ );
+ """.trimIndent()
+ )
+ }
+
+ override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
+ // Easiest upgrade strategy for school project
+ db.execSQL("DROP TABLE IF EXISTS rides;")
+ onCreate(db)
+ }
+}
diff --git a/app/composeApp/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml b/app/composeApp/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..bda706c
--- /dev/null
+++ b/app/composeApp/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/composeApp/src/androidMain/res/drawable/ic_launcher_background.xml b/app/composeApp/src/androidMain/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..3c40f44
--- /dev/null
+++ b/app/composeApp/src/androidMain/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml b/app/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..bbd3e02
--- /dev/null
+++ b/app/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..bbd3e02
--- /dev/null
+++ b/app/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher.png b/app/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..a571e60
Binary files /dev/null and b/app/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png b/app/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..61da551
Binary files /dev/null and b/app/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/app/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher.png b/app/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c41dd28
Binary files /dev/null and b/app/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher_round.png b/app/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..db5080a
Binary files /dev/null and b/app/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/app/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher.png b/app/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..6dba46d
Binary files /dev/null and b/app/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.png b/app/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..da31a87
Binary files /dev/null and b/app/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/app/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher.png b/app/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..15ac681
Binary files /dev/null and b/app/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.png b/app/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..b216f2d
Binary files /dev/null and b/app/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/app/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.png b/app/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..f25a419
Binary files /dev/null and b/app/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..e96783c
Binary files /dev/null and b/app/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/app/composeApp/src/androidMain/res/values/strings.xml b/app/composeApp/src/androidMain/res/values/strings.xml
new file mode 100644
index 0000000..61e5c6a
--- /dev/null
+++ b/app/composeApp/src/androidMain/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ app
+
\ No newline at end of file
diff --git a/app/composeApp/src/commonMain/composeResources/drawable/compose-multiplatform.xml b/app/composeApp/src/commonMain/composeResources/drawable/compose-multiplatform.xml
new file mode 100644
index 0000000..eba956a
--- /dev/null
+++ b/app/composeApp/src/commonMain/composeResources/drawable/compose-multiplatform.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/composeApp/src/commonMain/composeResources/drawable/ic_back_arrow.xml b/app/composeApp/src/commonMain/composeResources/drawable/ic_back_arrow.xml
new file mode 100644
index 0000000..125cf9c
--- /dev/null
+++ b/app/composeApp/src/commonMain/composeResources/drawable/ic_back_arrow.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/composeApp/src/commonMain/composeResources/drawable/ic_calendar.xml b/app/composeApp/src/commonMain/composeResources/drawable/ic_calendar.xml
new file mode 100644
index 0000000..0cd1ed0
--- /dev/null
+++ b/app/composeApp/src/commonMain/composeResources/drawable/ic_calendar.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/composeApp/src/commonMain/composeResources/drawable/ic_clock.xml b/app/composeApp/src/commonMain/composeResources/drawable/ic_clock.xml
new file mode 100644
index 0000000..df68293
--- /dev/null
+++ b/app/composeApp/src/commonMain/composeResources/drawable/ic_clock.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/composeApp/src/commonMain/composeResources/drawable/ic_dollar_sign.xml b/app/composeApp/src/commonMain/composeResources/drawable/ic_dollar_sign.xml
new file mode 100644
index 0000000..21e87fc
--- /dev/null
+++ b/app/composeApp/src/commonMain/composeResources/drawable/ic_dollar_sign.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/composeApp/src/commonMain/composeResources/drawable/ic_location_ping.xml b/app/composeApp/src/commonMain/composeResources/drawable/ic_location_ping.xml
new file mode 100644
index 0000000..fae4e07
--- /dev/null
+++ b/app/composeApp/src/commonMain/composeResources/drawable/ic_location_ping.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/composeApp/src/commonMain/composeResources/drawable/ic_two_people.xml b/app/composeApp/src/commonMain/composeResources/drawable/ic_two_people.xml
new file mode 100644
index 0000000..05f423f
--- /dev/null
+++ b/app/composeApp/src/commonMain/composeResources/drawable/ic_two_people.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/composeApp/src/commonMain/composeResources/drawable/ic_vehicle.xml b/app/composeApp/src/commonMain/composeResources/drawable/ic_vehicle.xml
new file mode 100644
index 0000000..b50b2b1
--- /dev/null
+++ b/app/composeApp/src/commonMain/composeResources/drawable/ic_vehicle.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/App.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/App.kt
new file mode 100644
index 0000000..129cb15
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/App.kt
@@ -0,0 +1,57 @@
+package com.example.demo
+
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.*
+import com.example.demo.feature.auth.data.AuthRepository
+import com.example.demo.feature.auth.login.LoginRoute
+import com.example.demo.feature.auth.signup.SignUpRoute
+import com.example.demo.feature.auth.forgot.ForgotPasswordRoute
+import com.example.demo.feature.db.RideRepository
+import com.example.demo.feature.main.MainRoute
+import com.example.demo.feature.messages.data.MessagesRepository
+import com.example.demo.feature.profile.data.ProfileRepository
+
+// Top-level screens in your app
+enum class RootScreen {
+ Login,
+ SignUp,
+ ForgotPassword,
+ Main
+}
+
+@Composable
+fun App(
+ rideRepository: RideRepository,
+ profileRepository: ProfileRepository,
+ authRepository: AuthRepository,
+ messagesRepository: MessagesRepository
+) {
+ var currentScreen by remember { mutableStateOf(RootScreen.Login) }
+
+ MaterialTheme {
+ when (currentScreen) {
+
+ RootScreen.Login -> LoginRoute(
+ onNavigateToSignUp = { currentScreen = RootScreen.SignUp },
+ onNavigateToForgotPassword = { currentScreen = RootScreen.ForgotPassword },
+ onLoginSuccess = { currentScreen = RootScreen.Main },
+ authRepository = authRepository,
+ )
+
+ RootScreen.SignUp -> SignUpRoute(
+ onNavigateToLogin = { currentScreen = RootScreen.Login }
+ )
+
+ RootScreen.ForgotPassword -> ForgotPasswordRoute(
+ onNavigateBack = { currentScreen = RootScreen.Login }
+ )
+
+ RootScreen.Main -> MainRoute(
+ rideRepository = rideRepository,
+ profileRepository = profileRepository,
+ messagesRepository = messagesRepository,
+
+ )
+ }
+ }
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/Greeting.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/Greeting.kt
new file mode 100644
index 0000000..035f767
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/Greeting.kt
@@ -0,0 +1,9 @@
+package com.example.app
+
+class Greeting {
+ private val platform = getPlatform()
+
+ fun greet(): String {
+ return "Hello, ${platform.name}!"
+ }
+}
\ No newline at end of file
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/Platform.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/Platform.kt
new file mode 100644
index 0000000..6b7133c
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/Platform.kt
@@ -0,0 +1,7 @@
+package com.example.app
+
+interface Platform {
+ val name: String
+}
+
+expect fun getPlatform(): Platform
\ No newline at end of file
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/AuthComponents.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/AuthComponents.kt
new file mode 100644
index 0000000..d222478
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/AuthComponents.kt
@@ -0,0 +1,73 @@
+package com.example.demo.feature.auth
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.OutlinedTextFieldDefaults
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.input.PasswordVisualTransformation
+import androidx.compose.ui.text.input.VisualTransformation
+import androidx.compose.ui.unit.dp
+import com.example.demo.ui.theme.DrexelGold
+import com.example.demo.ui.theme.FieldBackground
+import com.example.demo.ui.theme.HintGrey
+
+@Composable
+fun AppLogo() {
+ Box(
+ modifier = Modifier
+ .size(80.dp)
+ .background(DrexelGold, CircleShape),
+ contentAlignment = Alignment.Center
+ ) {
+ Text("🚗", fontSize = MaterialTheme.typography.headlineMedium.fontSize)
+ }
+}
+
+@Composable
+fun AuthTextField(
+ label: String,
+ value: String,
+ onValueChange: (String) -> Unit,
+ placeholder: String,
+ isPassword: Boolean
+) {
+ Text(
+ text = label,
+ style = MaterialTheme.typography.labelMedium,
+ color = Color.White
+ )
+ Spacer(Modifier.height(4.dp))
+
+ OutlinedTextField(
+ value = value,
+ onValueChange = onValueChange,
+ placeholder = { Text(placeholder, color = HintGrey) },
+ singleLine = true,
+ visualTransformation = if (isPassword) {
+ PasswordVisualTransformation()
+ } else {
+ VisualTransformation.None
+ },
+ modifier = Modifier.fillMaxWidth(),
+ shape = RoundedCornerShape(8.dp),
+ colors = OutlinedTextFieldDefaults.colors(
+ focusedBorderColor = Color.Transparent,
+ unfocusedBorderColor = Color.Transparent,
+ disabledBorderColor = Color.Transparent,
+ errorBorderColor = Color.Transparent,
+ focusedContainerColor = FieldBackground,
+ unfocusedContainerColor = FieldBackground,
+ cursorColor = DrexelGold,
+ focusedTextColor = Color.White,
+ unfocusedTextColor = Color.White
+ )
+ )
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/components/AppLogo.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/components/AppLogo.kt
new file mode 100644
index 0000000..9a469c2
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/components/AppLogo.kt
@@ -0,0 +1,48 @@
+package com.example.demo.feature.auth.components
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import com.example.demo.ui.theme.DrexelBlue
+import com.example.demo.ui.theme.DrexelGold
+
+@Composable
+fun AppLogo() {
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Box(
+ modifier = Modifier
+ .size(80.dp)
+ .clip(CircleShape)
+ .background(DrexelGold),
+ contentAlignment = Alignment.Center
+ ) {
+ Text("🚗", fontSize = MaterialTheme.typography.headlineMedium.fontSize)
+ }
+
+ Spacer(Modifier.height(16.dp))
+
+ Text(
+ text = "Find My Ride",
+ style = MaterialTheme.typography.titleMedium,
+ color = Color.White
+ )
+
+ Spacer(Modifier.height(4.dp))
+
+ Text(
+ text = "Share the journey, save the planet",
+ style = MaterialTheme.typography.bodyMedium,
+ color = Color.White.copy(alpha = 0.8f)
+ )
+ }
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/components/AuthScaffold.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/components/AuthScaffold.kt
new file mode 100644
index 0000000..ebdbc48
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/components/AuthScaffold.kt
@@ -0,0 +1,29 @@
+package com.example.demo.feature.auth.components
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.*
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.example.demo.ui.theme.DrexelBlue
+
+@Composable
+fun AuthScaffold(
+ content: @Composable ColumnScope.() -> Unit
+) {
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(DrexelBlue),
+ contentAlignment = Alignment.Center
+ ) {
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 32.dp),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ content = content
+ )
+ }
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/components/AuthTextField.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/components/AuthTextField.kt
new file mode 100644
index 0000000..71a811e
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/components/AuthTextField.kt
@@ -0,0 +1,53 @@
+package com.example.demo.feature.auth.components
+
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.OutlinedTextFieldDefaults
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.input.PasswordVisualTransformation
+import androidx.compose.ui.text.input.VisualTransformation
+import androidx.compose.ui.unit.dp
+import com.example.demo.ui.theme.DrexelGold
+import com.example.demo.ui.theme.FieldBackground
+import com.example.demo.ui.theme.HintGrey
+
+@Composable
+fun AuthTextField(
+ label: String,
+ value: String,
+ onValueChange: (String) -> Unit,
+ placeholder: String,
+ isPassword: Boolean,
+ modifier: Modifier = Modifier
+) {
+ Text(
+ text = label,
+ style = MaterialTheme.typography.labelMedium,
+ color = Color.White
+ )
+
+ OutlinedTextField(
+ value = value,
+ onValueChange = onValueChange,
+ placeholder = { Text(placeholder, color = HintGrey) },
+ singleLine = true,
+ visualTransformation = if (isPassword) PasswordVisualTransformation() else VisualTransformation.None ,
+ modifier = modifier,
+ shape = RoundedCornerShape(8.dp),
+ colors = OutlinedTextFieldDefaults.colors(
+ focusedBorderColor = Color.Transparent,
+ unfocusedBorderColor = Color.Transparent,
+ disabledBorderColor = Color.Transparent,
+ errorBorderColor = Color.Transparent,
+ focusedContainerColor = FieldBackground,
+ unfocusedContainerColor = FieldBackground,
+ cursorColor = DrexelGold,
+ focusedTextColor = Color.White,
+ unfocusedTextColor = Color.White
+ )
+ )
+}
\ No newline at end of file
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/data/AuthRepository.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/data/AuthRepository.kt
new file mode 100644
index 0000000..66b6688
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/data/AuthRepository.kt
@@ -0,0 +1,28 @@
+package com.example.demo.feature.auth.data
+
+import kotlinx.coroutines.delay
+
+// Later we will change this to connect to our database
+interface AuthRepository {
+
+ suspend fun login(email: String, password: String): Result
+
+ suspend fun signUp(
+ fullName: String,
+ email: String,
+ password: String
+ ): Result
+
+ suspend fun sendPasswordReset(email: String): Result
+}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/data/FakeAuthRepository.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/data/FakeAuthRepository.kt
new file mode 100644
index 0000000..2235753
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/data/FakeAuthRepository.kt
@@ -0,0 +1,46 @@
+import com.example.demo.feature.auth.data.AuthRepository
+import kotlinx.coroutines.delay
+
+/**
+ * In-memory / fake implementation for now
+ * Pretend everything works, with generic validation
+ */
+class FakeAuthRepository : AuthRepository {
+
+ private val existingUsers = mutableSetOf()
+
+ override suspend fun login(email: String, password: String): Result {
+ delay(1000)
+ if (email.isBlank() || password.isBlank()) {
+ return Result.failure(IllegalArgumentException("Email and password required"))
+ }
+
+ // Auto-sign-in new users
+ existingUsers.add(email)
+
+ return Result.success(Unit)
+ }
+
+
+ override suspend fun signUp(
+ fullName: String,
+ email: String,
+ password: String
+ ): Result {
+ delay(1000)
+ if (email.isBlank() || password.length < 6) {
+ return Result.failure(IllegalArgumentException("Invalid sign up data"))
+ }
+ existingUsers.add(email)
+ return Result.success(Unit)
+ }
+
+ override suspend fun sendPasswordReset(email: String): Result {
+ delay(1000)
+ return if (existingUsers.contains(email)) {
+ Result.success(Unit)
+ } else {
+ Result.failure(IllegalArgumentException("No account found for this email"))
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/forgot/ForgotPasswordEvent.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/forgot/ForgotPasswordEvent.kt
new file mode 100644
index 0000000..509629f
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/forgot/ForgotPasswordEvent.kt
@@ -0,0 +1,6 @@
+package com.example.demo.feature.auth.forgot
+
+sealed interface ForgotPasswordEvent {
+ data class EmailChanged(val value: String) : ForgotPasswordEvent
+ data object Submit : ForgotPasswordEvent
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/forgot/ForgotPasswordRoute.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/forgot/ForgotPasswordRoute.kt
new file mode 100644
index 0000000..239d87d
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/forgot/ForgotPasswordRoute.kt
@@ -0,0 +1,20 @@
+package com.example.demo.feature.auth.forgot
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+
+@Composable
+fun ForgotPasswordRoute(
+ onNavigateBack: () -> Unit,
+ viewModel: ForgotPasswordViewModel = remember { ForgotPasswordViewModel() }
+) {
+ val state by viewModel.uiState.collectAsState()
+
+ ForgotPasswordScreen(
+ state = state,
+ onEvent = viewModel::onEvent,
+ onNavigateBack = onNavigateBack
+ )
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/forgot/ForgotPasswordScreen.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/forgot/ForgotPasswordScreen.kt
new file mode 100644
index 0000000..853cf6d
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/forgot/ForgotPasswordScreen.kt
@@ -0,0 +1,88 @@
+package com.example.demo.feature.auth.forgot
+
+import androidx.compose.foundation.layout.*
+import androidx.compose.material3.*
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import com.example.demo.feature.auth.components.AppLogo
+import com.example.demo.feature.auth.components.AuthScaffold
+import com.example.demo.feature.auth.components.AuthTextField
+import com.example.demo.ui.theme.DrexelBlue
+import com.example.demo.ui.theme.DrexelGold
+
+@Composable
+fun ForgotPasswordScreen(
+ state: ForgotPasswordUiState,
+ onEvent: (ForgotPasswordEvent) -> Unit,
+ onNavigateBack: () -> Unit
+) {
+ AuthScaffold {
+
+ AppLogo()
+
+ Spacer(Modifier.height(16.dp))
+
+ Text(
+ text = "Reset Password",
+ style = MaterialTheme.typography.titleMedium,
+ color = Color.White
+ )
+
+ Spacer(Modifier.height(4.dp))
+
+ Text(
+ text = "Enter your email to receive a reset link.",
+ style = MaterialTheme.typography.bodyMedium,
+ color = Color.White.copy(alpha = 0.8f)
+ )
+
+ Spacer(Modifier.height(32.dp))
+
+ AuthTextField(
+ label = "Email",
+ value = state.email,
+ onValueChange = { onEvent(ForgotPasswordEvent.EmailChanged(it)) },
+ placeholder = "your@email.com",
+ isPassword = false,
+ modifier = Modifier.fillMaxWidth()
+ )
+
+ Spacer(Modifier.height(16.dp))
+
+ Button(
+ onClick = { onEvent(ForgotPasswordEvent.Submit) },
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(52.dp),
+ enabled = !state.isLoading,
+ colors = ButtonDefaults.buttonColors(
+ containerColor = DrexelGold,
+ contentColor = DrexelBlue
+ )
+ ) {
+ if (state.isLoading) {
+ Text("Sending...")
+ } else {
+ Text("Send Reset Link")
+ }
+ }
+
+ state.errorMessage?.let {
+ Spacer(Modifier.height(8.dp))
+ Text(it, color = Color.Red)
+ }
+
+ state.successMessage?.let {
+ Spacer(Modifier.height(8.dp))
+ Text(it, color = Color.Green)
+ }
+
+ Spacer(Modifier.height(24.dp))
+
+ TextButton(onClick = onNavigateBack) {
+ Text("Back to Login", color = DrexelGold)
+ }
+ }
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/forgot/ForgotPasswordUiState.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/forgot/ForgotPasswordUiState.kt
new file mode 100644
index 0000000..7d52ce8
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/forgot/ForgotPasswordUiState.kt
@@ -0,0 +1,8 @@
+package com.example.demo.feature.auth.forgot
+
+data class ForgotPasswordUiState(
+ val email: String = "",
+ val isLoading: Boolean = false,
+ val successMessage: String? = null,
+ val errorMessage: String? = null
+)
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/forgot/ForgotPasswordViewModel.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/forgot/ForgotPasswordViewModel.kt
new file mode 100644
index 0000000..13c4a3f
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/forgot/ForgotPasswordViewModel.kt
@@ -0,0 +1,94 @@
+package com.example.demo.feature.auth.forgot
+
+import FakeAuthRepository
+import com.example.demo.feature.auth.data.AuthRepository
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.launch
+
+class ForgotPasswordViewModel(
+ private val repository: AuthRepository = FakeAuthRepository()
+) {
+
+ private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
+
+ private val _uiState = MutableStateFlow(ForgotPasswordUiState())
+ val uiState: StateFlow = _uiState
+
+ fun onEvent(event: ForgotPasswordEvent) {
+ when (event) {
+ is ForgotPasswordEvent.EmailChanged ->
+ _uiState.value = _uiState.value.copy(
+ email = event.value,
+ errorMessage = null,
+ successMessage = null
+ )
+
+ ForgotPasswordEvent.Submit -> submit()
+ }
+ }
+
+ private fun submit() {
+ val current = _uiState.value
+ val email = current.email.trim()
+
+ // 1) Empty check
+ if (email.isBlank()) {
+ _uiState.value = current.copy(
+ errorMessage = "Please enter your email.",
+ successMessage = null
+ )
+ return
+ }
+
+ // 2) Very simple email validation (KMP-friendly)
+ if (!isValidEmail(email)) {
+ _uiState.value = current.copy(
+ errorMessage = "Please enter a valid email address.",
+ successMessage = null
+ )
+ return
+ }
+
+ // 3) Set loading state
+ _uiState.value = current.copy(
+ isLoading = true,
+ errorMessage = null,
+ successMessage = null
+ )
+
+ // 4) Call repository
+ scope.launch {
+ val result = repository.sendPasswordReset(email)
+
+ _uiState.value = if (result.isSuccess) {
+ _uiState.value.copy(
+ isLoading = false,
+ successMessage = "If an account exists for $email, a reset link has been sent.",
+ errorMessage = null
+ )
+ } else {
+ _uiState.value.copy(
+ isLoading = false,
+ errorMessage = result.exceptionOrNull()?.message
+ ?: "Could not send reset link. Please try again.",
+ successMessage = null
+ )
+ }
+ }
+ }
+
+ // Simple, KMP-safe email validator (no Android APIs)
+ private fun isValidEmail(email: String): Boolean {
+ val atIndex = email.indexOf('@')
+ if (atIndex <= 0 || atIndex == email.lastIndex) return false
+
+ val dotIndex = email.lastIndexOf('.')
+ if (dotIndex <= atIndex + 1 || dotIndex == email.lastIndex) return false
+
+ return true
+ }
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/login/LoginEvent.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/login/LoginEvent.kt
new file mode 100644
index 0000000..0db7b04
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/login/LoginEvent.kt
@@ -0,0 +1,10 @@
+package com.example.demo.feature.auth.login
+
+
+// Contract of what the user can do on the screen.
+// navigation to signup/forgot stay in app / higher level not events
+sealed interface LoginEvent {
+ data class EmailChanged(val value: String) : LoginEvent
+ data class PasswordChanged(val value: String) : LoginEvent
+ data object Submit : LoginEvent
+}
\ No newline at end of file
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/login/LoginRoute.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/login/LoginRoute.kt
new file mode 100644
index 0000000..a75e553
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/login/LoginRoute.kt
@@ -0,0 +1,33 @@
+package com.example.demo.feature.auth.login
+
+import FakeAuthRepository
+import androidx.compose.runtime.*
+import androidx.compose.runtime.collectAsState
+import com.example.demo.feature.auth.data.AuthRepository
+
+@Composable
+fun LoginRoute(
+ onNavigateToSignUp: () -> Unit,
+ onNavigateToForgotPassword: () -> Unit,
+ onLoginSuccess: () -> Unit,
+ authRepository: AuthRepository
+) {
+ val viewModel = remember(authRepository) {
+ LoginViewModel(authRepository)
+ }
+ val state by viewModel.uiState.collectAsState()
+
+ // When login succeeds, trigger navigation once
+ LaunchedEffect(state.loginSuccess) {
+ if (state.loginSuccess) {
+ onLoginSuccess()
+ }
+ }
+
+ LoginScreen(
+ state = state,
+ onEvent = viewModel::onEvent,
+ onNavigateToSignUp = onNavigateToSignUp,
+ onNavigateToForgotPassword = onNavigateToForgotPassword
+ )
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/login/LoginScreen.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/login/LoginScreen.kt
new file mode 100644
index 0000000..8a5adb2
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/login/LoginScreen.kt
@@ -0,0 +1,106 @@
+package com.example.demo.feature.auth.login
+
+import androidx.compose.foundation.layout.*
+import androidx.compose.material3.*
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import com.example.demo.feature.auth.components.AppLogo
+import com.example.demo.feature.auth.components.AuthScaffold
+import com.example.demo.feature.auth.components.AuthTextField
+import com.example.demo.ui.theme.DrexelBlue
+import com.example.demo.ui.theme.DrexelGold
+
+@Composable
+fun LoginScreen(
+ state: LoginUiState,
+ onEvent: (LoginEvent) -> Unit,
+ onNavigateToSignUp: () -> Unit,
+ onNavigateToForgotPassword: () -> Unit
+) {
+ AuthScaffold {
+ AppLogo()
+
+ Spacer(Modifier.height(32.dp))
+
+ AuthTextField(
+ label = "Email",
+ value = state.email,
+ onValueChange = { onEvent(LoginEvent.EmailChanged(it)) },
+ placeholder = "your@email.com",
+ isPassword = false,
+ modifier = Modifier.fillMaxWidth()
+ )
+
+ Spacer(Modifier.height(12.dp))
+
+ AuthTextField(
+ label = "Password",
+ value = state.password,
+ onValueChange = { onEvent(LoginEvent.PasswordChanged(it)) },
+ placeholder = "••••••••",
+ isPassword = true,
+ modifier = Modifier.fillMaxWidth()
+ )
+
+ Spacer(Modifier.height(8.dp))
+
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.End
+ ) {
+ TextButton(onClick = onNavigateToForgotPassword) {
+ Text(
+ text = "Forgot password?",
+ style = MaterialTheme.typography.labelMedium,
+ color = DrexelGold
+ )
+ }
+ }
+
+ Spacer(Modifier.height(16.dp))
+
+ Button(
+ onClick = { onEvent(LoginEvent.Submit) },
+ enabled = !state.isLoading,
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(52.dp),
+ colors = ButtonDefaults.buttonColors(
+ containerColor = DrexelGold,
+ contentColor = DrexelBlue
+ )
+ ) {
+ Text(if (state.isLoading) "Loading..." else "Log In")
+ }
+
+ state.errorMessage?.let { error ->
+ Spacer(Modifier.height(8.dp))
+ Text(
+ text = error,
+ color = Color.Red,
+ style = MaterialTheme.typography.bodySmall
+ )
+ }
+
+ Spacer(Modifier.height(24.dp))
+
+ Text(
+ text = "Don't have an account?",
+ style = MaterialTheme.typography.bodyMedium,
+ color = Color.White
+ )
+
+ Spacer(Modifier.height(4.dp))
+
+ TextButton(onClick = onNavigateToSignUp) {
+ Text(
+ text = "Sign up now",
+ style = MaterialTheme.typography.bodyMedium,
+ color = DrexelGold
+ )
+ }
+ }
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/login/LoginUiState.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/login/LoginUiState.kt
new file mode 100644
index 0000000..6b8ee52
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/login/LoginUiState.kt
@@ -0,0 +1,9 @@
+package com.example.demo.feature.auth.login
+
+data class LoginUiState(
+ val email: String = "",
+ val password: String = "",
+ val isLoading: Boolean = false,
+ val errorMessage: String? = null,
+ val loginSuccess: Boolean = false,
+)
\ No newline at end of file
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/login/LoginViewModel.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/login/LoginViewModel.kt
new file mode 100644
index 0000000..7ec7094
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/login/LoginViewModel.kt
@@ -0,0 +1,93 @@
+package com.example.demo.feature.auth.login
+
+import com.example.demo.feature.auth.data.AuthRepository
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.launch
+
+class LoginViewModel(
+ private val repository: AuthRepository
+) {
+ private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
+
+ private val _uiState = MutableStateFlow(LoginUiState())
+ val uiState: StateFlow = _uiState
+
+ fun onEvent(event: LoginEvent) {
+ when (event) {
+ is LoginEvent.EmailChanged -> {
+ _uiState.value = _uiState.value.copy(
+ email = event.value,
+ errorMessage = null,
+ loginSuccess = false
+ )
+ }
+
+ is LoginEvent.PasswordChanged -> {
+ _uiState.value = _uiState.value.copy(
+ password = event.value,
+ errorMessage = null,
+ loginSuccess = false
+ )
+ }
+
+ LoginEvent.Submit -> submit()
+ }
+ }
+
+ private fun submit() {
+ val state = _uiState.value
+
+ if (!isValidEmail(state.email)) {
+ setError("Please enter a valid email")
+ return
+ }
+ if (state.password.isBlank()) {
+ setError("Password is required")
+ return
+ }
+
+ // start loading
+ _uiState.value = state.copy(
+ isLoading = true,
+ errorMessage = null,
+ loginSuccess = false
+ )
+
+ scope.launch {
+ val result = repository.login(state.email.trim(), state.password)
+
+ _uiState.value = if (result.isSuccess) {
+ _uiState.value.copy(
+ isLoading = false,
+ loginSuccess = true,
+ errorMessage = null
+ )
+ } else {
+ _uiState.value.copy(
+ isLoading = false,
+ loginSuccess = false,
+ errorMessage = result.exceptionOrNull()?.message ?: "Login failed"
+ )
+ }
+ }
+ }
+
+ private fun setError(message: String) {
+ _uiState.value = _uiState.value.copy(
+ errorMessage = message,
+ isLoading = false,
+ loginSuccess = false
+ )
+ }
+
+ // Same simple cross-platform email validation you used before
+ private fun isValidEmail(email: String): Boolean {
+ val at = email.indexOf('@')
+ val dot = email.lastIndexOf('.')
+ return at > 0 && dot > at + 1 && dot < email.length - 1
+ }
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/signup/SignUpEvent.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/signup/SignUpEvent.kt
new file mode 100644
index 0000000..bf61c34
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/signup/SignUpEvent.kt
@@ -0,0 +1,9 @@
+package com.example.demo.feature.auth.signup
+
+sealed interface SignUpEvent{
+ data class NameChanged(val value: String) : SignUpEvent
+ data class EmailChanged(val value: String) : SignUpEvent
+ data class PasswordChanged(val value: String) : SignUpEvent
+ data class ConfirmPasswordChanged(val value: String) : SignUpEvent
+ data object Submit : SignUpEvent
+}
\ No newline at end of file
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/signup/SignUpRoute.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/signup/SignUpRoute.kt
new file mode 100644
index 0000000..edcf3d2
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/signup/SignUpRoute.kt
@@ -0,0 +1,20 @@
+package com.example.demo.feature.auth.signup
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+
+@Composable
+fun SignUpRoute(
+ onNavigateToLogin: () -> Unit,
+ viewModel: SignUpViewModel = remember { SignUpViewModel() }
+) {
+ val state by viewModel.uiState.collectAsState()
+
+ SignUpScreen(
+ state = state,
+ onEvent = viewModel::onEvent,
+ onNavigateToLogin = onNavigateToLogin
+ )
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/signup/SignUpScreen.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/signup/SignUpScreen.kt
new file mode 100644
index 0000000..16dc345
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/signup/SignUpScreen.kt
@@ -0,0 +1,129 @@
+package com.example.demo.feature.auth.signup
+
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.*
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import com.example.demo.feature.auth.components.AppLogo
+import com.example.demo.feature.auth.components.AuthScaffold
+import com.example.demo.feature.auth.components.AuthTextField
+import com.example.demo.ui.theme.DrexelBlue
+import com.example.demo.ui.theme.DrexelGold
+
+@Composable
+fun SignUpScreen(
+ state: SignUpUiState,
+ onEvent: (SignUpEvent) -> Unit,
+ onNavigateToLogin: () -> Unit
+) {
+ AuthScaffold {
+ AppLogo()
+
+ Spacer(Modifier.height(16.dp))
+
+ Text(
+ text = "Create Account",
+ style = MaterialTheme.typography.titleMedium,
+ color = Color.White
+ )
+
+ Spacer(Modifier.height(4.dp))
+
+ Text(
+ text = "Join the ride-sharing community",
+ style = MaterialTheme.typography.bodyMedium,
+ color = Color.White.copy(alpha = 0.8f)
+ )
+
+ Spacer(Modifier.height(32.dp))
+
+ AuthTextField(
+ label = "Name",
+ value = state.fullName,
+ onValueChange = { onEvent(SignUpEvent.NameChanged(it)) },
+ placeholder = "Your name",
+ isPassword = false,
+ modifier = Modifier.fillMaxWidth()
+ )
+
+ Spacer(Modifier.height(12.dp))
+
+ AuthTextField(
+ label = "Email",
+ value = state.email,
+ onValueChange = { onEvent(SignUpEvent.EmailChanged(it)) },
+ placeholder = "your@email.com",
+ isPassword = false,
+ modifier = Modifier.fillMaxWidth()
+ )
+
+ Spacer(Modifier.height(12.dp))
+
+ AuthTextField(
+ label = "Password",
+ value = state.password,
+ onValueChange = { onEvent(SignUpEvent.PasswordChanged(it)) },
+ placeholder = "••••••••",
+ isPassword = true,
+ modifier = Modifier.fillMaxWidth()
+ )
+
+ Spacer(Modifier.height(12.dp))
+
+ AuthTextField(
+ label = "Confirm Password",
+ value = state.confirmPassword,
+ onValueChange = { onEvent(SignUpEvent.ConfirmPasswordChanged(it)) },
+ placeholder = "••••••••",
+ isPassword = true,
+ modifier = Modifier.fillMaxWidth()
+ )
+
+ Spacer(Modifier.height(16.dp))
+
+ Button(
+ onClick = { onEvent(SignUpEvent.Submit) },
+ enabled = !state.isLoading,
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(52.dp),
+ shape = RoundedCornerShape(10.dp),
+ colors = ButtonDefaults.buttonColors(
+ containerColor = DrexelGold,
+ contentColor = DrexelBlue
+ )
+ ) {
+ Text(if (state.isLoading) "Signing up..." else "Sign Up")
+ }
+
+ state.errorMessage?.let { error ->
+ Spacer(Modifier.height(8.dp))
+ Text(
+ text = error,
+ color = Color.Red,
+ style = MaterialTheme.typography.bodySmall
+ )
+ }
+
+ Spacer(Modifier.height(24.dp))
+
+ Text(
+ text = "Already have an account?",
+ style = MaterialTheme.typography.bodyMedium,
+ color = Color.White
+ )
+
+ Spacer(Modifier.height(4.dp))
+
+ TextButton(onClick = onNavigateToLogin) {
+ Text(
+ text = "Log in",
+ style = MaterialTheme.typography.bodyMedium,
+ color = DrexelGold
+ )
+ }
+ }
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/signup/SignUpUiState.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/signup/SignUpUiState.kt
new file mode 100644
index 0000000..7035fda
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/signup/SignUpUiState.kt
@@ -0,0 +1,11 @@
+package com.example.demo.feature.auth.signup
+
+data class SignUpUiState(
+ val fullName: String = "",
+ val email: String = "",
+ val password: String = "",
+ val confirmPassword: String = "",
+ val isLoading: Boolean = false,
+ val errorMessage: String? = null,
+ val success: Boolean = false
+)
\ No newline at end of file
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/signup/SignUpViewModel.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/signup/SignUpViewModel.kt
new file mode 100644
index 0000000..6ec694c
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/auth/signup/SignUpViewModel.kt
@@ -0,0 +1,106 @@
+package com.example.demo.feature.auth.signup
+
+import FakeAuthRepository
+import com.example.demo.feature.auth.data.AuthRepository
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+
+class SignUpViewModel(
+ private val repository: AuthRepository = FakeAuthRepository()
+) {
+
+ private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
+
+ private val _uiState = MutableStateFlow(SignUpUiState())
+ val uiState: StateFlow = _uiState
+
+ fun onEvent(event: SignUpEvent) {
+ when (event) {
+ is SignUpEvent.NameChanged ->
+ _uiState.value = _uiState.value.copy(fullName = event.value, errorMessage = null)
+
+ is SignUpEvent.EmailChanged ->
+ _uiState.value = _uiState.value.copy(email = event.value, errorMessage = null)
+
+ is SignUpEvent.PasswordChanged ->
+ _uiState.value = _uiState.value.copy(password = event.value, errorMessage = null)
+
+ is SignUpEvent.ConfirmPasswordChanged ->
+ _uiState.value = _uiState.value.copy(confirmPassword = event.value, errorMessage = null)
+
+ SignUpEvent.Submit -> submit()
+ }
+ }
+
+ private fun submit() {
+ val state = _uiState.value
+ val name = state.fullName.trim()
+ val email = state.email.trim()
+ val pass = state.password
+ val confirm = state.confirmPassword
+
+ // 1) Require all fields
+ if (name.isBlank() || email.isBlank() || pass.isBlank() || confirm.isBlank()) {
+ setError("Please fill in all fields.")
+ return
+ }
+
+ // 2) Email validation
+ if (!isValidEmail(email)) {
+ setError("Please enter a valid email.")
+ return
+ }
+
+ // 3) Password match
+ if (pass != confirm) {
+ setError("Passwords do not match.")
+ return
+ }
+
+ // 4) Password strength
+ if (pass.length < 6) {
+ setError("Password must be at least 6 characters.")
+ return
+ }
+
+ // Clear old errors + show loading
+ _uiState.value = state.copy(
+ isLoading = true,
+ errorMessage = null
+ )
+
+ // 5) Call repository
+ scope.launch {
+ val result = repository.signUp(name, email, pass)
+
+ _uiState.value = if (result.isSuccess) {
+ _uiState.value.copy(
+ isLoading = false,
+ errorMessage = null,
+ success = true // you can use this to auto-navigate later
+ )
+ } else {
+ _uiState.value.copy(
+ isLoading = false,
+ errorMessage = result.exceptionOrNull()?.message
+ ?: "Sign up failed. Try again."
+ )
+ }
+ }
+ }
+
+ private fun setError(msg: String) {
+ _uiState.value = _uiState.value.copy(
+ errorMessage = msg,
+ isLoading = false
+ )
+ }
+
+ // Simple cross-platform email validation
+ private fun isValidEmail(email: String): Boolean {
+ val at = email.indexOf('@')
+ val dot = email.lastIndexOf('.')
+ return at > 0 && dot > at + 1 && dot < email.length - 1
+ }
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/db/RideRepository.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/db/RideRepository.kt
new file mode 100644
index 0000000..d61f199
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/db/RideRepository.kt
@@ -0,0 +1,35 @@
+package com.example.demo.feature.db
+
+data class RideOffer(
+ val offerId: Long,
+ val driverId: Long,
+ val vehicleId: Long,
+ val fromName: String,
+ val toName: String,
+ val departAt: String,
+ val seatsAvailable: Int,
+ val priceBase: Double
+)
+
+interface RideRepository {
+ suspend fun getOpenRideOffers(): List
+ suspend fun createRideRequest(
+ riderId: Long,
+ pickupLocationId: Long,
+ dropoffLocationId: Long,
+ earliestPickup: String,
+ latestPickup: String?,
+ seatsNeeded: Int
+ )
+
+ suspend fun createRideOffer(
+ driverId: Long,
+ vehicleId: Long,
+ originalLocationId: Long,
+ destLocationId: Long,
+ departAt: String,
+ seatsAvailable: Int,
+ priceBase: Double,
+ pricePerMile: Double
+ )
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/main/HomeScreen.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/main/HomeScreen.kt
new file mode 100644
index 0000000..d7b996d
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/main/HomeScreen.kt
@@ -0,0 +1,238 @@
+package com.example.demo.feature.main
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.*
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.DirectionsCar
+import androidx.compose.material.icons.filled.People
+import androidx.compose.material.icons.filled.Settings
+import androidx.compose.ui.graphics.vector.ImageVector
+import com.example.demo.ui.theme.DrexelBlue
+import com.example.demo.ui.theme.DrexelGold
+import com.example.demo.ui.theme.FieldBackground
+import com.example.demo.ui.theme.HintGrey
+
+
+@Composable
+fun HomeScreen(
+ modifier: Modifier = Modifier,
+ onFindRideClick: () -> Unit = {},
+ onOfferRideClick: () -> Unit = {}
+) {
+ Column(
+ modifier = modifier
+ .fillMaxSize()
+ .background(FieldBackground)
+ ) {
+ // ---------- HEADER ----------
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(DrexelBlue)
+ ) {
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 20.dp, vertical = 24.dp)
+ ) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Column {
+ Text(
+ text = "Welcome back!",
+ style = MaterialTheme.typography.headlineSmall,
+ color = Color.White,
+ fontWeight = FontWeight.Bold
+ )
+ Spacer(modifier = Modifier.height(4.dp))
+ Text(
+ text = "Where would you like to go?",
+ style = MaterialTheme.typography.bodyMedium,
+ color = Color.White.copy(alpha = 0.8f)
+ )
+ }
+
+ IconButton(onClick = { /* settings later */ }) {
+ Icon(
+ imageVector = Icons.Filled.Settings,
+ contentDescription = "Settings",
+ tint = Color.White
+ )
+ }
+ }
+ }
+ }
+
+ Spacer(modifier = Modifier.height(16.dp))
+
+ // ---------- MAIN CARDS ----------
+ Column(
+ modifier = Modifier
+ .padding(horizontal = 16.dp)
+ .weight(1f, fill = true),
+ verticalArrangement = Arrangement.spacedBy(16.dp)
+ ) {
+ // Find a Ride card
+ HomeActionCard(
+ title = "Find a Ride",
+ subtitle = "Search for available rides to your destination",
+ iconBgColor = Color(0xFFE3F2FD),
+ iconTint = DrexelBlue,
+ icon = Icons.Filled.People,
+ buttonText = "Get started →",
+ onClick = onFindRideClick
+ )
+
+ // Offer a Ride card
+ HomeActionCard(
+ title = "Offer a Ride",
+ subtitle = "Share your trip and earn money",
+ iconBgColor = Color(0xFFFFF4CC),
+ iconTint = DrexelGold,
+ icon = Icons.Filled.DirectionsCar,
+ buttonText = "Get started →",
+ onClick = onOfferRideClick
+ )
+
+ Spacer(modifier = Modifier.height(8.dp))
+
+ // Stats row
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.spacedBy(12.dp)
+ ) {
+ StatCard(
+ label = "Rides",
+ value = "12",
+ modifier = Modifier.weight(1f)
+ )
+ StatCard(
+ label = "Rating",
+ value = "4.8★",
+ modifier = Modifier.weight(1f)
+ )
+ StatCard(
+ label = "Saved",
+ value = "$142",
+ modifier = Modifier.weight(1f)
+ )
+ }
+ }
+ }
+}
+
+@Composable
+private fun HomeActionCard(
+ title: String,
+ subtitle: String,
+ iconBgColor: Color,
+ iconTint: Color,
+ icon: ImageVector,
+ buttonText: String,
+ onClick: () -> Unit
+) {
+ Card(
+ modifier = Modifier.fillMaxWidth(),
+ shape = RoundedCornerShape(24.dp),
+ colors = CardDefaults.cardColors(containerColor = Color.White),
+ elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
+ ) {
+ Column(
+ modifier = Modifier
+ .padding(horizontal = 20.dp, vertical = 16.dp)
+ ) {
+ Text(
+ text = title,
+ style = MaterialTheme.typography.titleMedium,
+ fontWeight = FontWeight.SemiBold,
+ color = DrexelBlue
+ )
+
+ Spacer(modifier = Modifier.height(4.dp))
+
+ Text(
+ text = subtitle,
+ style = MaterialTheme.typography.bodyMedium,
+ color = HintGrey
+ )
+
+ Spacer(modifier = Modifier.height(16.dp))
+
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Box(
+ modifier = Modifier
+ .size(44.dp)
+ .clip(CircleShape)
+ .background(iconBgColor),
+ contentAlignment = Alignment.Center
+ ) {
+ Icon(
+ imageVector = icon,
+ contentDescription = null,
+ tint = iconTint
+ )
+ }
+
+ TextButton(onClick = onClick) {
+ Text(
+ text = buttonText,
+ color = DrexelBlue,
+ fontWeight = FontWeight.SemiBold
+ )
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun StatCard(
+ label: String,
+ value: String,
+ modifier: Modifier = Modifier,
+) {
+ Card(
+ modifier = modifier,
+ shape = RoundedCornerShape(18.dp),
+ colors = CardDefaults.cardColors(containerColor = Color.White),
+ elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
+ ) {
+ Column(
+ modifier = Modifier
+ .padding(vertical = 12.dp)
+ .fillMaxWidth(),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Text(
+ text = value,
+ fontSize = 18.sp,
+ fontWeight = FontWeight.Bold,
+ color = DrexelBlue
+ )
+ Spacer(modifier = Modifier.height(2.dp))
+ Text(
+ text = label,
+ style = MaterialTheme.typography.bodySmall,
+ color = HintGrey
+ )
+ }
+ }
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/main/MainRoute.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/main/MainRoute.kt
new file mode 100644
index 0000000..1b85323
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/main/MainRoute.kt
@@ -0,0 +1,145 @@
+package com.example.demo.feature.main
+
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.*
+import androidx.compose.runtime.*
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import com.example.demo.feature.profile.ProfileRoute
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.*
+import com.example.demo.feature.db.RideRepository
+import com.example.demo.feature.messages.MessagesRoute
+import com.example.demo.feature.messages.data.MessagesRepository
+import com.example.demo.feature.profile.data.ProfileRepository
+import com.example.demo.feature.rides.AvailableOfferScreen
+import com.example.demo.feature.rides.AvailableRidesScreen
+import com.example.demo.feature.rides.MyRidesScreen
+import com.example.demo.ui.theme.DrexelBlue
+import com.example.demo.ui.theme.DrexelGold
+
+enum class MainTab { Home, Rides, Messages, Profile }
+private enum class HomePage { Dashboard, AvailableRides, OfferRide }
+
+@Composable
+fun MainRoute(
+ rideRepository: RideRepository,
+ profileRepository: ProfileRepository,
+ messagesRepository: MessagesRepository,
+) {
+ var currentTab by remember { mutableStateOf(MainTab.Home) }
+ var homePage by remember { mutableStateOf(HomePage.Dashboard) }
+
+ Scaffold(
+ bottomBar = {
+ MainBottomNav(
+ currentTab = currentTab,
+ onTabSelected = { tab ->
+ currentTab = tab
+ if (tab == MainTab.Home) {
+ homePage = HomePage.Dashboard // reset when returning to Home tab
+ }
+ }
+ )
+ },
+ containerColor = Color(0xFFF5F5F7)
+ ) { padding ->
+ when (currentTab) {
+ MainTab.Home -> {
+ when (homePage) {
+ HomePage.Dashboard -> {
+ HomeScreen(
+ modifier = Modifier.padding(padding),
+ onFindRideClick = { homePage = HomePage.AvailableRides },
+ onOfferRideClick = { homePage = HomePage.OfferRide },
+ )
+ }
+ HomePage.AvailableRides -> {
+ AvailableRidesScreen(
+ modifier = Modifier.padding(padding),
+ onBack = { homePage = HomePage.Dashboard },
+ rideRepository = rideRepository
+ )
+ }
+ HomePage.OfferRide -> {
+ AvailableOfferScreen(
+ modifier = Modifier.padding(padding),
+ onBack = { homePage = HomePage.Dashboard },
+ onPublish = {
+ homePage = HomePage.Dashboard
+ }
+ )
+ }
+ }
+ }
+ MainTab.Rides -> {
+ MyRidesScreen(
+ modifier = Modifier.padding(padding)
+ )
+ }
+ MainTab.Messages -> {
+ MessagesRoute(
+ modifier = Modifier.padding(padding),
+ repository = messagesRepository,
+ )
+ }
+ MainTab.Profile -> {
+ ProfileRoute(
+ modifier = Modifier.padding(padding),
+ repository = profileRepository
+ )
+ }
+ }
+ }
+}
+
+
+@Composable
+private fun MainBottomNav(
+ currentTab: MainTab,
+ onTabSelected: (MainTab) -> Unit
+) {
+ NavigationBar(
+ containerColor = Color.White,
+ tonalElevation = 4.dp
+ ) {
+ val itemColors = NavigationBarItemDefaults.colors(
+ selectedIconColor = DrexelBlue,
+ selectedTextColor = DrexelBlue,
+ unselectedIconColor = Color.Gray,
+ unselectedTextColor = Color.Gray,
+ indicatorColor = DrexelGold.copy(alpha = 0.18f) // subtle gold pill behind selected
+ )
+
+ NavigationBarItem(
+ selected = currentTab == MainTab.Home,
+ onClick = { onTabSelected(MainTab.Home) },
+ icon = { Icon(Icons.Default.Home, contentDescription = "Home") },
+ label = { Text("Home") },
+ colors = itemColors
+ )
+ NavigationBarItem(
+ selected = currentTab == MainTab.Rides,
+ onClick = { onTabSelected(MainTab.Rides) },
+ icon = { Icon(Icons.Default.CalendarToday, contentDescription = "My Rides") },
+ label = { Text("My Rides") },
+ colors = itemColors
+ )
+ NavigationBarItem(
+ selected = currentTab == MainTab.Messages,
+ onClick = { onTabSelected(MainTab.Messages) },
+ icon = { Icon(Icons.Default.ChatBubbleOutline, contentDescription = "Messages") },
+ label = { Text("Messages") },
+ colors = itemColors
+ )
+ NavigationBarItem(
+ selected = currentTab == MainTab.Profile,
+ onClick = { onTabSelected(MainTab.Profile) },
+ icon = { Icon(Icons.Default.Person, contentDescription = "Profile") },
+ label = { Text("Profile") },
+ colors = itemColors
+ )
+ }
+}
+
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/main/MyRideScreen.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/main/MyRideScreen.kt
new file mode 100644
index 0000000..8e7733f
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/main/MyRideScreen.kt
@@ -0,0 +1,352 @@
+package com.example.demo.feature.rides
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.*
+import androidx.compose.runtime.*
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.CalendarToday
+import androidx.compose.material.icons.filled.LocationOn
+import com.example.demo.ui.theme.DrexelBlue
+import com.example.demo.ui.theme.FieldBackground
+import com.example.demo.ui.theme.HintGrey
+
+private enum class MyRidesTab { Upcoming, Completed }
+
+data class RideHistoryItem(
+ val id: Int,
+ val status: String,
+ val role: String,
+ val driverName: String,
+ val pickup: String,
+ val dropoff: String,
+ val date: String,
+ val time: String,
+ val price: String
+)
+
+@Composable
+fun MyRidesScreen(
+ modifier: Modifier = Modifier
+) {
+ // fake data for now
+ val upcomingRides = listOf(
+ RideHistoryItem(
+ id = 1,
+ status = "Confirmed",
+ role = "Passenger",
+ driverName = "Abdul B.",
+ pickup = "30th Street Station",
+ dropoff = "Cira Green",
+ date = "Nov 11, 2025",
+ time = "5:30 PM",
+ price = "$11.71"
+ )
+ )
+
+ val completedRides = listOf(
+ RideHistoryItem(
+ id = 2,
+ status = "Passenger", // only pill we show
+ role = "", // leave empty so no second pill
+ driverName = "Sarah M.",
+ pickup = "University Crossings",
+ dropoff = "Downtown Philadelphia",
+ date = "Nov 5, 2025",
+ time = "8:00 AM",
+ price = "$15.5"
+ ),
+ RideHistoryItem(
+ id = 3,
+ status = "Driver",
+ role = "",
+ driverName = "You", // or whoever
+ pickup = "West Philadelphia",
+ dropoff = "King of Prussia",
+ date = "Nov 1, 2025",
+ time = "6:30 PM",
+ price = "$28"
+ ),
+ RideHistoryItem(
+ id = 4,
+ status = "Passenger",
+ role = "",
+ driverName = "Michael T.",
+ pickup = "Temple University",
+ dropoff = "Chestnut Hill",
+ date = "Oct 28, 2025",
+ time = "3:15 PM",
+ price = "$12.75"
+ )
+ )
+
+ var currentTab by remember { mutableStateOf(MyRidesTab.Upcoming) }
+
+ Column(
+ modifier = modifier
+ .fillMaxSize()
+ .background(FieldBackground)
+ ) {
+ // ------- HEADER --------
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(DrexelBlue)
+ ) {
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 20.dp, vertical = 24.dp)
+ ) {
+ Text(
+ text = "My Rides",
+ style = MaterialTheme.typography.headlineSmall,
+ color = Color.White,
+ fontWeight = FontWeight.Bold
+ )
+ Spacer(modifier = Modifier.height(4.dp))
+ Text(
+ text = "Your ride history & upcoming trips",
+ style = MaterialTheme.typography.bodyMedium,
+ color = Color.White.copy(alpha = 0.8f)
+ )
+
+ Spacer(modifier = Modifier.height(16.dp))
+
+ // segmented control
+ Surface(
+ shape = RoundedCornerShape(24.dp),
+ color = Color.White,
+ tonalElevation = 4.dp,
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(40.dp)
+ ) {
+ Row(
+ modifier = Modifier.fillMaxSize(),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ SegmentedTab(
+ text = "Upcoming",
+ selected = currentTab == MyRidesTab.Upcoming,
+ modifier = Modifier.weight(1f)
+ ) { currentTab = MyRidesTab.Upcoming }
+
+ SegmentedTab(
+ text = "Completed",
+ selected = currentTab == MyRidesTab.Completed,
+ modifier = Modifier.weight(1f)
+ ) { currentTab = MyRidesTab.Completed }
+ }
+ }
+ }
+ }
+
+ Spacer(modifier = Modifier.height(16.dp))
+
+ // ------- LIST CONTENT -------
+ val ridesToShow =
+ if (currentTab == MyRidesTab.Upcoming) upcomingRides else completedRides
+
+ LazyColumn(
+ contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp),
+ verticalArrangement = Arrangement.spacedBy(12.dp)
+ ) {
+ items(ridesToShow, key = { it.id }) { ride ->
+ RideHistoryCard(ride)
+ }
+
+ if (ridesToShow.isEmpty()) {
+ item {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(top = 24.dp),
+ contentAlignment = Alignment.Center
+ ) {
+ Text(
+ text = "No rides in this section yet.",
+ color = HintGrey
+ )
+ }
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun SegmentedTab(
+ text: String,
+ selected: Boolean,
+ modifier: Modifier = Modifier,
+ onClick: () -> Unit
+) {
+ val bg = if (selected) Color.White else Color.Transparent
+ val textColor = if (selected) DrexelBlue else HintGrey
+
+ Box(
+ modifier = modifier
+ .fillMaxHeight()
+ ) {
+ TextButton(
+ onClick = onClick,
+ modifier = Modifier
+ .fillMaxSize(),
+ contentPadding = PaddingValues(0.dp)
+ ) {
+ Surface(
+ shape = RoundedCornerShape(24.dp),
+ color = if (selected) Color(0xFFEFF3FF) else Color.Transparent
+ ) {
+ Box(
+ modifier = Modifier
+ .padding(horizontal = 12.dp, vertical = 6.dp),
+ contentAlignment = Alignment.Center
+ ) {
+ Text(
+ text = text,
+ fontSize = 14.sp,
+ fontWeight = if (selected) FontWeight.SemiBold else FontWeight.Normal,
+ color = textColor
+ )
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun RideHistoryCard(ride: RideHistoryItem) {
+ Card(
+ shape = RoundedCornerShape(24.dp),
+ colors = CardDefaults.cardColors(containerColor = Color.White),
+ elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Column(
+ modifier = Modifier
+ .padding(horizontal = 20.dp, vertical = 16.dp)
+ ) {
+ // status row
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
+ // always show the first pill
+ PillChip(
+ text = ride.status,
+ bg = Color(0xFFE3F8EA),
+ textColor = Color(0xFF2C7A43)
+ )
+
+ // only show second pill when role is non-blank
+ if (ride.role.isNotBlank()) {
+ PillChip(
+ text = ride.role,
+ bg = Color(0xFFEAF0FF),
+ textColor = DrexelBlue
+ )
+ }
+ }
+ Text(
+ text = ride.price,
+ fontWeight = FontWeight.Bold,
+ color = DrexelBlue
+ )
+ }
+
+ Spacer(modifier = Modifier.height(8.dp))
+
+ Text(
+ text = "Driver: ${ride.driverName}",
+ style = MaterialTheme.typography.bodyMedium,
+ color = HintGrey
+ )
+
+ Spacer(modifier = Modifier.height(16.dp))
+
+ // locations
+ Row(verticalAlignment = Alignment.CenterVertically) {
+ Icon(
+ imageVector = Icons.Filled.LocationOn,
+ contentDescription = null,
+ tint = HintGrey,
+ modifier = Modifier.size(18.dp)
+ )
+ Spacer(modifier = Modifier.width(8.dp))
+ Text(
+ text = ride.pickup,
+ style = MaterialTheme.typography.bodyMedium,
+ color = HintGrey
+ )
+ }
+
+ Spacer(modifier = Modifier.height(8.dp))
+
+ Row(verticalAlignment = Alignment.CenterVertically) {
+ Icon(
+ imageVector = Icons.Filled.LocationOn,
+ contentDescription = null,
+ tint = HintGrey,
+ modifier = Modifier.size(18.dp)
+ )
+ Spacer(modifier = Modifier.width(8.dp))
+ Text(
+ text = ride.dropoff,
+ style = MaterialTheme.typography.bodyMedium,
+ color = HintGrey
+ )
+ }
+
+ Spacer(modifier = Modifier.height(16.dp))
+
+ // date + time
+ Row(verticalAlignment = Alignment.CenterVertically) {
+ Icon(
+ imageVector = Icons.Filled.CalendarToday,
+ contentDescription = null,
+ tint = HintGrey,
+ modifier = Modifier.size(18.dp)
+ )
+ Spacer(modifier = Modifier.width(8.dp))
+ Text(
+ text = "${ride.date} ${ride.time}",
+ style = MaterialTheme.typography.bodyMedium,
+ color = HintGrey
+ )
+ }
+ }
+ }
+}
+
+@Composable
+private fun PillChip(
+ text: String,
+ bg: Color,
+ textColor: Color
+) {
+ Surface(
+ color = bg,
+ shape = RoundedCornerShape(50)
+ ) {
+ Text(
+ text = text,
+ fontSize = 12.sp,
+ fontWeight = FontWeight.SemiBold,
+ color = textColor,
+ modifier = Modifier.padding(horizontal = 10.dp, vertical = 4.dp)
+ )
+ }
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/MessagesRoute.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/MessagesRoute.kt
new file mode 100644
index 0000000..3d1d1e4
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/MessagesRoute.kt
@@ -0,0 +1,48 @@
+package com.example.demo.feature.messages
+
+import androidx.compose.runtime.*
+import androidx.compose.ui.Modifier
+import com.example.demo.feature.messages.chat.ChatDetailRoute
+import com.example.demo.feature.messages.data.MessagesRepository
+import com.example.demo.feature.messages.list.MessageThreadUi
+import com.example.demo.feature.messages.list.MessagesListRoute
+
+private enum class MessagesPage { List, Conversation }
+
+@Composable
+fun MessagesRoute(
+ modifier: Modifier = Modifier,
+ repository: MessagesRepository
+) {
+ var currentPage by remember { mutableStateOf(MessagesPage.List) }
+ var activeThread by remember { mutableStateOf(null) }
+
+ when (currentPage) {
+ MessagesPage.List -> {
+ MessagesListRoute(
+ modifier = modifier,
+ repository = repository,
+ onOpenConversation = { thread ->
+ activeThread = thread
+ currentPage = MessagesPage.Conversation
+ }
+ )
+ }
+ MessagesPage.Conversation -> {
+ val thread = activeThread
+ if (thread == null) {
+ currentPage = MessagesPage.List
+ } else {
+ ChatDetailRoute(
+ threadId = thread.id,
+ contactName = thread.senderName,
+ initials = thread.initials,
+ repository = repository,
+ onBack = { currentPage = MessagesPage.List },
+ modifier = modifier
+ )
+ }
+ }
+ }
+}
+
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/chat/ChatDetailEvent.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/chat/ChatDetailEvent.kt
new file mode 100644
index 0000000..9f8fd69
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/chat/ChatDetailEvent.kt
@@ -0,0 +1,6 @@
+package com.example.demo.feature.messages.chat
+
+sealed interface ChatDetailEvent {
+ data class MessageTextChanged(val value: String) : ChatDetailEvent
+ data object SendClicked : ChatDetailEvent
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/chat/ChatDetailRoute.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/chat/ChatDetailRoute.kt
new file mode 100644
index 0000000..aa6ed9f
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/chat/ChatDetailRoute.kt
@@ -0,0 +1,27 @@
+package com.example.demo.feature.messages.chat
+
+import androidx.compose.runtime.*
+import androidx.compose.ui.Modifier
+import com.example.demo.feature.messages.data.MessagesRepository
+
+@Composable
+fun ChatDetailRoute(
+ threadId: Int,
+ contactName: String,
+ initials: String,
+ repository: MessagesRepository,
+ onBack: () -> Unit,
+ modifier: Modifier = Modifier
+) {
+ val viewModel = remember(threadId, repository) {
+ ChatDetailViewModel(threadId, contactName, initials, repository)
+ }
+ val state by viewModel.uiState.collectAsState()
+
+ ChatDetailScreen(
+ state = state,
+ onEvent = viewModel::onEvent,
+ onBack = onBack,
+ modifier = modifier
+ )
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/chat/ChatDetailScreen.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/chat/ChatDetailScreen.kt
new file mode 100644
index 0000000..bf8f9e8
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/chat/ChatDetailScreen.kt
@@ -0,0 +1,168 @@
+package com.example.demo.feature.messages.chat
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.ArrowBack
+import androidx.compose.material.icons.automirrored.filled.Send
+import androidx.compose.material.icons.filled.Send
+import androidx.compose.material3.*
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.example.demo.ui.theme.DrexelBlue
+import com.example.demo.ui.theme.DrexelGold
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun ChatDetailScreen(
+ state: ChatDetailUiState,
+ onEvent: (ChatDetailEvent) -> Unit,
+ onBack: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ Scaffold(
+ modifier = modifier,
+ topBar = {
+ TopAppBar(
+ title = {
+ Row(verticalAlignment = Alignment.CenterVertically) {
+ Box(
+ modifier = Modifier
+ .size(32.dp)
+ .clip(CircleShape)
+ .background(DrexelBlue),
+ contentAlignment = Alignment.Center
+ ) {
+ Text(
+ text = state.initials,
+ color = Color.White,
+ fontSize = 14.sp,
+ fontWeight = FontWeight.Bold
+ )
+ }
+ Spacer(Modifier.width(8.dp))
+ Text(state.contactName)
+ }
+ },
+ navigationIcon = {
+ IconButton(onClick = onBack) {
+ Icon(
+ imageVector = Icons.AutoMirrored.Filled.ArrowBack,
+ contentDescription = "Back",
+ tint = Color.White
+ )
+ }
+ },
+ colors = TopAppBarDefaults.topAppBarColors(
+ containerColor = DrexelBlue,
+ titleContentColor = Color.White,
+ navigationIconContentColor = Color.White
+ )
+ )
+ }
+ ) { padding ->
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(padding)
+ .background(Color(0xFFF5F5F7))
+ ) {
+ // Messages
+ LazyColumn(
+ modifier = Modifier
+ .weight(1f)
+ .fillMaxWidth()
+ .padding(horizontal = 12.dp, vertical = 8.dp)
+ ) {
+ items(state.messages) { msg ->
+ ChatBubble(message = msg)
+ Spacer(Modifier.height(6.dp))
+ }
+ }
+
+ // Input bar
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(Color.White)
+ .padding(8.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ OutlinedTextField(
+ value = state.newMessageText,
+ onValueChange = { onEvent(ChatDetailEvent.MessageTextChanged(it)) },
+ modifier = Modifier.weight(1f),
+ placeholder = { Text("Type a message...") },
+ maxLines = 3,
+ colors = OutlinedTextFieldDefaults.colors(
+ focusedBorderColor = DrexelBlue,
+ unfocusedBorderColor = Color(0xFFE5E5EA),
+ cursorColor = DrexelBlue
+ ),
+ shape = RoundedCornerShape(20.dp)
+ )
+
+ Spacer(Modifier.width(8.dp))
+
+ IconButton(
+ onClick = { onEvent(ChatDetailEvent.SendClicked) }
+ ) {
+ Icon(
+ imageVector = Icons.AutoMirrored.Filled.Send,
+ contentDescription = "Send",
+ tint = DrexelGold
+ )
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun ChatBubble(message: ChatMessageUi) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = if (message.isMe) Arrangement.End else Arrangement.Start
+ ) {
+ Column(
+ horizontalAlignment = if (message.isMe) Alignment.End else Alignment.Start
+ ) {
+ Box(
+ modifier = Modifier
+ .widthIn(max = 260.dp)
+ .clip(
+ RoundedCornerShape(
+ topStart = 18.dp,
+ topEnd = 18.dp,
+ bottomStart = if (message.isMe) 18.dp else 4.dp,
+ bottomEnd = if (message.isMe) 4.dp else 18.dp
+ )
+ )
+ .background(if (message.isMe) DrexelBlue else Color.White)
+ .padding(horizontal = 12.dp, vertical = 8.dp)
+ ) {
+ Text(
+ text = message.text,
+ color = if (message.isMe) Color.White else Color(0xFF1C1C1E),
+ fontSize = 14.sp
+ )
+ }
+ Spacer(Modifier.height(2.dp))
+ Text(
+ text = message.time,
+ fontSize = 11.sp,
+ color = Color(0xFF8E8E93)
+ )
+ }
+ }
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/chat/ChatDetailUiState.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/chat/ChatDetailUiState.kt
new file mode 100644
index 0000000..e4d4673
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/chat/ChatDetailUiState.kt
@@ -0,0 +1,17 @@
+package com.example.demo.feature.messages.chat
+
+data class ChatMessageUi(
+ val id: Int,
+ val isMe: Boolean,
+ val text: String,
+ val time: String
+)
+
+data class ChatDetailUiState(
+ val contactName: String = "",
+ val initials: String = "",
+ val isLoading: Boolean = false,
+ val messages: List = emptyList(),
+ val newMessageText: String = "",
+ val errorMessage: String? = null
+)
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/chat/ChatDetailViewModel.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/chat/ChatDetailViewModel.kt
new file mode 100644
index 0000000..86eebad
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/chat/ChatDetailViewModel.kt
@@ -0,0 +1,87 @@
+package com.example.demo.feature.messages.chat
+
+import com.example.demo.feature.messages.data.MessagesRepository
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.launch
+
+class ChatDetailViewModel(
+ private val threadId: Int,
+ contactName: String,
+ initials: String,
+ private val repository: MessagesRepository
+) {
+ private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
+
+ private val _uiState = MutableStateFlow(
+ ChatDetailUiState(
+ contactName = contactName,
+ initials = initials,
+ isLoading = true
+ )
+ )
+ val uiState: StateFlow = _uiState
+
+ init {
+ loadMessages()
+ }
+
+ private fun loadMessages() {
+ scope.launch {
+ try {
+ val msgs = repository.getMessagesForThread(threadId)
+ _uiState.update {
+ it.copy(
+ isLoading = false,
+ messages = msgs,
+ errorMessage = null
+ )
+ }
+ } catch (e: Exception) {
+ _uiState.update {
+ it.copy(
+ isLoading = false,
+ errorMessage = e.message ?: "Failed to load messages"
+ )
+ }
+ }
+ }
+ }
+
+ fun onEvent(event: ChatDetailEvent) {
+ when (event) {
+ is ChatDetailEvent.MessageTextChanged ->
+ _uiState.update { it.copy(newMessageText = event.value) }
+
+ ChatDetailEvent.SendClicked -> sendMessage()
+ }
+ }
+
+ private fun sendMessage() {
+ val text = _uiState.value.newMessageText.trim()
+ if (text.isBlank()) return
+
+ scope.launch {
+ try {
+ val msg = repository.sendMessage(threadId, text)
+ _uiState.update {
+ it.copy(
+ messages = it.messages + msg,
+ newMessageText = "",
+ errorMessage = null
+ )
+ }
+ } catch (e: Exception) {
+ _uiState.update {
+ it.copy(
+ errorMessage = e.message ?: "Failed to send message"
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/data/FakeMessageRepository.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/data/FakeMessageRepository.kt
new file mode 100644
index 0000000..61f89c1
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/data/FakeMessageRepository.kt
@@ -0,0 +1,81 @@
+package com.example.demo.feature.messages.data
+
+import com.example.demo.feature.messages.chat.ChatMessageUi
+import com.example.demo.feature.messages.list.MessageThreadUi
+import kotlinx.coroutines.delay
+
+class FakeMessagesRepository : MessagesRepository {
+
+ // pretend current user is id=1
+ private val fakeThreads = mutableListOf(
+ MessageThreadUi(
+ id = 1,
+ senderName = "Alex Johnson",
+ initials = "AJ",
+ lastMessage = "Are we still on for 5:30 PM?",
+ timeAgo = "2m ago",
+ unreadCount = 1
+ ),
+ MessageThreadUi(
+ id = 2,
+ senderName = "Campus Carpool",
+ initials = "CC",
+ lastMessage = "Thanks again for organizing the rides!",
+ timeAgo = "3h ago",
+ unreadCount = 0
+ ),
+ MessageThreadUi(
+ id = 3,
+ senderName = "Taylor Smith",
+ initials = "TS",
+ lastMessage = "I’ll grab the front seat next time 😄",
+ timeAgo = "Yesterday",
+ unreadCount = 0
+ )
+ )
+
+ // in-memory messages per thread
+ private val fakeMessages = mutableMapOf(
+ 1 to mutableListOf(
+ ChatMessageUi(1, isMe = false, "Hey, thanks for the ride!", "3h ago"),
+ ChatMessageUi(2, isMe = true, "Of course! Happy to help.", "3h ago"),
+ ChatMessageUi(3, isMe = false, "Are we still on for 5:30 PM?", "2m ago"),
+ ),
+ 2 to mutableListOf(
+ ChatMessageUi(4, isMe = false, "Carpool this Friday?", "1d ago"),
+ ChatMessageUi(5, isMe = true, "Yes, let’s do it!", "1d ago"),
+ )
+ )
+
+ override suspend fun getThreadsForUser(userId: Int): List {
+ delay(200) // fake network/db delay
+ return fakeThreads
+ }
+
+ override suspend fun getMessagesForThread(threadId: Int): List {
+ delay(150)
+ return fakeMessages[threadId]?.toList() ?: emptyList()
+ }
+
+ override suspend fun sendMessage(threadId: Int, text: String): ChatMessageUi {
+ delay(100)
+ val list = fakeMessages.getOrPut(threadId) { mutableListOf() }
+ val newId = (fakeMessages.values.flatten().maxOfOrNull { it.id } ?: 0) + 1
+ val msg = ChatMessageUi(
+ id = newId,
+ isMe = true,
+ text = text,
+ time = "Now"
+ )
+ list.add(msg)
+
+ // update last message in thread
+ val idx = fakeThreads.indexOfFirst { it.id == threadId }
+ if (idx >= 0) {
+ val thread = fakeThreads[idx]
+ fakeThreads[idx] = thread.copy(lastMessage = text, timeAgo = "Now")
+ }
+
+ return msg
+ }
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/data/MessagesRepository.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/data/MessagesRepository.kt
new file mode 100644
index 0000000..2b2eecf
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/data/MessagesRepository.kt
@@ -0,0 +1,13 @@
+package com.example.demo.feature.messages.data
+
+import com.example.demo.feature.messages.chat.ChatMessageUi
+import com.example.demo.feature.messages.list.MessageThreadUi
+
+interface MessagesRepository {
+
+ suspend fun getThreadsForUser(userId: Int): List
+
+ suspend fun getMessagesForThread(threadId: Int): List
+
+ suspend fun sendMessage(threadId: Int, text: String): ChatMessageUi
+}
\ No newline at end of file
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/list/MessagesEvent.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/list/MessagesEvent.kt
new file mode 100644
index 0000000..97293fc
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/list/MessagesEvent.kt
@@ -0,0 +1,5 @@
+package com.example.demo.feature.messages.list
+
+sealed interface MessagesEvent {
+ data class SearchQueryChanged(val value: String) : MessagesEvent
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/list/MessagesListRoute.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/list/MessagesListRoute.kt
new file mode 100644
index 0000000..c446f70
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/list/MessagesListRoute.kt
@@ -0,0 +1,30 @@
+package com.example.demo.feature.messages.list
+
+import androidx.compose.runtime.*
+import androidx.compose.ui.Modifier
+import com.example.demo.feature.messages.data.MessagesRepository
+
+@Composable
+fun MessagesListRoute(
+ modifier: Modifier = Modifier,
+ onOpenConversation: (MessageThreadUi) -> Unit,
+ repository: MessagesRepository
+) {
+ // For now, we use userId = 1; AndroidMessagesRepository internally
+ // will override this with CurrentUserStore.userId when available.
+ val viewModel = remember(repository) {
+ MessagesViewModel(
+ repository = repository,
+ userId = 1
+ )
+ }
+
+ val state by viewModel.uiState.collectAsState()
+
+ MessagesScreen(
+ state = state,
+ onEvent = viewModel::onEvent,
+ onOpenConversation = onOpenConversation,
+ modifier = modifier
+ )
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/list/MessagesScreen.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/list/MessagesScreen.kt
new file mode 100644
index 0000000..69f3c9d
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/list/MessagesScreen.kt
@@ -0,0 +1,173 @@
+package com.example.demo.feature.messages.list
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Search
+import androidx.compose.material3.*
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.example.demo.ui.theme.DrexelBlue
+import com.example.demo.ui.theme.DrexelGold
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun MessagesScreen(
+ state: MessagesUiState,
+ onEvent: (MessagesEvent) -> Unit,
+ onOpenConversation: (MessageThreadUi) -> Unit,
+ modifier: Modifier = Modifier
+) {
+ Scaffold(
+ modifier = modifier,
+ topBar = {
+ TopAppBar(
+ title = { Text("Messages") },
+ colors = TopAppBarDefaults.topAppBarColors(
+ containerColor = DrexelBlue,
+ titleContentColor = Color.White
+ )
+ )
+ }
+ ) { padding ->
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(padding)
+ .background(Color(0xFFF5F5F7))
+ .padding(horizontal = 16.dp, vertical = 8.dp)
+ ) {
+ // Search bar
+ OutlinedTextField(
+ value = state.searchQuery,
+ onValueChange = { onEvent(MessagesEvent.SearchQueryChanged(it)) },
+ modifier = Modifier.fillMaxWidth(),
+ placeholder = { Text("Search messages") },
+ leadingIcon = {
+ Icon(Icons.Default.Search, contentDescription = null)
+ },
+ singleLine = true,
+ shape = RoundedCornerShape(20.dp)
+ )
+
+ Spacer(Modifier.height(12.dp))
+
+ if (state.isLoading) {
+ Box(
+ modifier = Modifier.fillMaxSize(),
+ contentAlignment = Alignment.Center
+ ) {
+ CircularProgressIndicator()
+ }
+ } else {
+ val filtered = state.threads.filter {
+ val q = state.searchQuery.trim().lowercase()
+ q.isEmpty() ||
+ it.senderName.lowercase().contains(q) ||
+ it.lastMessage.lowercase().contains(q)
+ }
+
+ LazyColumn(
+ modifier = Modifier.fillMaxSize(),
+ verticalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+ items(filtered) { thread ->
+ MessageThreadRow(thread, onClick = { onOpenConversation(thread) })
+ }
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun MessageThreadRow(
+ thread: MessageThreadUi,
+ onClick: () -> Unit
+) {
+ Card(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clickable(onClick = onClick),
+ shape = RoundedCornerShape(12.dp),
+ colors = CardDefaults.cardColors(containerColor = Color.White),
+ elevation = CardDefaults.cardElevation(defaultElevation = 1.dp)
+ ) {
+ Row(
+ modifier = Modifier.padding(12.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Box(
+ modifier = Modifier
+ .size(40.dp)
+ .clip(CircleShape)
+ .background(DrexelGold),
+ contentAlignment = Alignment.Center
+ ) {
+ Text(
+ text = thread.initials,
+ color = Color.Blue,
+ fontWeight = FontWeight.Bold,
+ fontSize = 14.sp
+ )
+ }
+
+ Spacer(Modifier.width(12.dp))
+
+ Column(modifier = Modifier.weight(1f)) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ text = thread.senderName,
+ fontWeight = FontWeight.SemiBold,
+ fontSize = 15.sp
+ )
+ Spacer(Modifier.width(8.dp))
+ Text(
+ text = thread.timeAgo,
+ fontSize = 12.sp,
+ color = Color.Gray
+ )
+ }
+
+ Spacer(Modifier.height(4.dp))
+
+ Text(
+ text = thread.lastMessage,
+ fontSize = 13.sp,
+ color = Color(0xFF555555),
+ maxLines = 1
+ )
+ }
+
+ if (thread.unreadCount > 0) {
+ Box(
+ modifier = Modifier
+ .size(20.dp)
+ .clip(CircleShape)
+ .background(Color(0xFFFF3B30)),
+ contentAlignment = Alignment.Center
+ ) {
+ Text(
+ text = thread.unreadCount.toString(),
+ color = Color.White,
+ fontSize = 11.sp,
+ fontWeight = FontWeight.Bold
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/list/MessagesUiState.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/list/MessagesUiState.kt
new file mode 100644
index 0000000..1b3682e
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/list/MessagesUiState.kt
@@ -0,0 +1,17 @@
+package com.example.demo.feature.messages.list
+
+data class MessageThreadUi(
+ val id: Int,
+ val senderName: String,
+ val initials: String,
+ val lastMessage: String,
+ val timeAgo: String,
+ val unreadCount: Int = 0
+)
+
+data class MessagesUiState(
+ val isLoading: Boolean = false,
+ val searchQuery: String = "",
+ val threads: List = emptyList(),
+ val errorMessage: String? = null
+)
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/list/MessagesViewModel.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/list/MessagesViewModel.kt
new file mode 100644
index 0000000..b1bed78
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/messages/list/MessagesViewModel.kt
@@ -0,0 +1,48 @@
+package com.example.demo.feature.messages.list
+
+import com.example.demo.feature.messages.data.MessagesRepository
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.launch
+
+class MessagesViewModel(
+ private val repository: MessagesRepository,
+ private val userId: Int
+) {
+ private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
+
+ private val _uiState = MutableStateFlow(MessagesUiState())
+ val uiState: StateFlow = _uiState
+
+ init {
+ loadThreads()
+ }
+
+ private fun loadThreads() {
+ scope.launch {
+ try {
+ val threads = repository.getThreadsForUser(userId)
+ _uiState.value = _uiState.value.copy(
+ isLoading = false,
+ threads = threads,
+ errorMessage = null
+ )
+ } catch (e: Exception) {
+ _uiState.value = _uiState.value.copy(
+ isLoading = false,
+ errorMessage = e.message ?: "Failed to load messages"
+ )
+ }
+ }
+ }
+
+ fun onEvent(event: MessagesEvent) {
+ when (event) {
+ is MessagesEvent.SearchQueryChanged ->
+ _uiState.value = _uiState.value.copy(searchQuery = event.value)
+ }
+ }
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/profile/ProfileEvent.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/profile/ProfileEvent.kt
new file mode 100644
index 0000000..8c22af0
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/profile/ProfileEvent.kt
@@ -0,0 +1,56 @@
+package com.example.demo.feature.profile
+
+enum class SettingsOption {
+ AccountSetting,
+ Preferences,
+ PrivacySetting,
+}
+sealed interface ProfileEvent {
+
+ // Vehicles
+ data class VehicleClicked(val vehicleId: Int) : ProfileEvent
+ data object VehicleDialogDismissed : ProfileEvent
+ data class VehicleEditMakeChanged(val value: String) : ProfileEvent
+ data class VehicleEditModelChanged(val value: String) : ProfileEvent
+ data class VehicleEditColorChanged(val value: String) : ProfileEvent
+ data class VehicleEditPlateChanged(val value: String) : ProfileEvent
+ data class VehicleEditSeatsChanged(val value: String) : ProfileEvent
+ data class VehicleEditYearChanged(val value: String) : ProfileEvent
+ data class VehicleEditFunFactChanged(val value: String) : ProfileEvent
+ data object AddVehicleClicked : ProfileEvent
+ data object DeleteVehicleClicked : ProfileEvent
+ data object SaveVehicleChanges : ProfileEvent
+
+
+ data class PreferencesChanged(
+ val notificationsEnabled: Boolean,
+ val emailUpdatesEnabled: Boolean,
+ val darkModeEnabled: Boolean,
+ ) : ProfileEvent
+
+ data object SavePreferences : ProfileEvent
+
+ // Settings
+ data class SettingsClicked(val option: SettingsOption) : ProfileEvent
+ data object SettingsDialogDismissed : ProfileEvent
+
+ data class AccountSettingsChanged(
+ val fullName: String? = null,
+ val email: String? = null,
+ val phone: String? = null,
+ val password: String? = null,
+ ) : ProfileEvent
+
+ data object SaveAccountSettings : ProfileEvent
+ data object DeleteAccount : ProfileEvent
+
+ data class PrivacySafetyChanged(
+ val showProfilePublicly: Boolean,
+ val allowMessagesFromNonContacts: Boolean,
+ val shareTripHistoryWithFriends: Boolean,
+ val twoFactorEnabled: Boolean
+ ) : ProfileEvent
+
+ data object SavePrivacySafety : ProfileEvent
+
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/profile/ProfileScreen.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/profile/ProfileScreen.kt
new file mode 100644
index 0000000..cbc6fc5
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/profile/ProfileScreen.kt
@@ -0,0 +1,324 @@
+package com.example.demo.feature.profile
+
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.*
+import androidx.compose.material3.*
+import androidx.compose.runtime.*
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.example.demo.feature.profile.components.SettingsCard
+import com.example.demo.feature.profile.components.VehicleEditDialog
+import com.example.demo.feature.profile.components.VehiclesCard
+import com.example.demo.feature.profile.data.InMemoryProfileRepository
+import com.example.demo.feature.profile.data.ProfileRepository
+import com.example.demo.feature.profile.pages.AccountSettingsScreen
+import com.example.demo.feature.profile.pages.PreferencesScreen
+import com.example.demo.feature.profile.pages.PrivacySafetyScreen
+import com.example.demo.ui.theme.DrexelBlue
+import com.example.demo.ui.theme.DrexelGold
+
+enum class ProfilePage {
+ Overview,
+ AccountSettings,
+ Preferences,
+ PrivacySafety
+}
+
+@Composable
+fun ProfileRoute(
+ modifier: Modifier = Modifier,
+ repository: ProfileRepository
+) {
+ val viewModel = remember(repository) { ProfileViewModel(repository) }
+ var currentPage by remember { mutableStateOf(ProfilePage.Overview) }
+ val state by viewModel.uiState.collectAsState()
+
+ when (currentPage) {
+ ProfilePage.Overview -> ProfileScreen(
+ state = state,
+ onEvent = viewModel::onEvent,
+ onAccountSettingsClick = { currentPage = ProfilePage.AccountSettings },
+ onPreferencesClick = { currentPage = ProfilePage.Preferences },
+ onPrivacySafetyClick = { currentPage = ProfilePage.PrivacySafety },
+ modifier = modifier
+ )
+
+ ProfilePage.AccountSettings -> AccountSettingsScreen(
+ state = state.account,
+ onChange = { updated ->
+ viewModel.onEvent(
+ ProfileEvent.AccountSettingsChanged(
+ fullName = updated.fullName,
+ email = updated.email,
+ phone = updated.phone,
+ password = updated.password
+ )
+ )
+ },
+ onSave = { viewModel.onEvent(ProfileEvent.SaveAccountSettings) },
+ onDelete = { viewModel.onEvent(ProfileEvent.DeleteAccount) },
+ onBack = { currentPage = ProfilePage.Overview }
+ )
+
+ ProfilePage.Preferences -> PreferencesScreen(
+ prefs = state.preferences,
+ onChange = { newPrefs ->
+ viewModel.onEvent(
+ ProfileEvent.PreferencesChanged(
+ notificationsEnabled = newPrefs.notificationsEnabled,
+ emailUpdatesEnabled = newPrefs.emailUpdatesEnabled,
+ darkModeEnabled = newPrefs.darkModeEnabled
+ )
+ )
+ },
+ onSave = { viewModel.onEvent(ProfileEvent.SavePreferences) },
+ onBack = { currentPage = ProfilePage.Overview },
+ modifier = modifier
+ )
+
+ ProfilePage.PrivacySafety -> PrivacySafetyScreen(
+ state = state.privacy,
+ onChange = { updated ->
+ viewModel.onEvent(
+ ProfileEvent.PrivacySafetyChanged(
+ showProfilePublicly = updated.showProfilePublicly,
+ allowMessagesFromNonContacts = updated.allowMessagesFromNonContacts,
+ shareTripHistoryWithFriends = updated.shareTripHistoryWithFriends,
+ twoFactorEnabled = updated.twoFactorEnabled
+ )
+ )
+ },
+ onSave = { viewModel.onEvent(ProfileEvent.SavePrivacySafety) },
+ onBack = { currentPage = ProfilePage.Overview },
+ modifier = modifier
+ )
+ }
+}
+
+
+@Composable
+fun ProfileScreen(
+ state: ProfileUiState,
+ onEvent: (ProfileEvent) -> Unit,
+ onAccountSettingsClick: () -> Unit,
+ onPreferencesClick: () -> Unit,
+ onPrivacySafetyClick: () -> Unit,
+ modifier: Modifier = Modifier
+) {
+ Column(
+ modifier = modifier.fillMaxSize()
+ ) {
+ ProfileHeader(state)
+
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(horizontal = 16.dp, vertical = 12.dp),
+ verticalArrangement = Arrangement.spacedBy(12.dp)
+ ) {
+ StatsCard(state)
+ VerifiedCard()
+ VehiclesCard(state, onEvent)
+ SettingsCard(
+ onAccountSettingsClick = onAccountSettingsClick,
+ onPreferencesClick = onPreferencesClick,
+ onPrivacySafetyClick = onPrivacySafetyClick
+ )
+ }
+ }
+
+ if (state.isVehicleDialogOpen) {
+ VehicleEditDialog(state, onEvent)
+ }
+}
+
+
+@Composable
+private fun ProfileHeader(state: ProfileUiState) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(DrexelBlue)
+ ) {
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp, vertical = 24.dp)
+ ) {
+ Row(verticalAlignment = Alignment.CenterVertically) {
+ // Avatar circle with initials
+ Box(
+ modifier = Modifier
+ .size(64.dp)
+ .clip(CircleShape)
+ .background(DrexelGold),
+ contentAlignment = Alignment.Center
+ ) {
+ Text(
+ text = state.name.split(" ")
+ .take(2)
+ .joinToString("") { it.first().uppercase() },
+ fontSize = 22.sp,
+ fontWeight = FontWeight.Bold,
+ color = DrexelBlue
+ )
+ }
+
+ Spacer(Modifier.width(16.dp))
+
+ Column {
+ Text(
+ text = state.name,
+ style = MaterialTheme.typography.titleMedium,
+ color = Color.White
+ )
+ Text(
+ text = state.email,
+ style = MaterialTheme.typography.bodyMedium,
+ color = Color.White.copy(alpha = 0.9f)
+ )
+
+ Spacer(Modifier.height(4.dp))
+
+ Row(verticalAlignment = Alignment.CenterVertically) {
+ Icon(
+ imageVector = Icons.Default.Star,
+ contentDescription = null,
+ tint = DrexelGold,
+ modifier = Modifier.size(16.dp)
+ )
+ Text(
+ text = "${state.rating}",
+ style = MaterialTheme.typography.bodyMedium,
+ color = Color.White,
+ modifier = Modifier.padding(start = 4.dp, end = 8.dp)
+ )
+ Text(
+ text = "${state.totalRides} rides",
+ style = MaterialTheme.typography.bodySmall,
+ color = Color.White.copy(alpha = 0.9f)
+ )
+ }
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun StatsCard(state: ProfileUiState) {
+ Card(
+ modifier = Modifier.fillMaxWidth(),
+ shape = RoundedCornerShape(16.dp),
+ colors = CardDefaults.cardColors(containerColor = Color.White),
+ elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
+ ) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(vertical = 16.dp),
+ horizontalArrangement = Arrangement.SpaceEvenly
+ ) {
+ StatItem(
+ title = "\$${state.savedAmount}",
+ subtitle = "Saved"
+ )
+ StatItem(
+ title = "${state.passengerRides}",
+ subtitle = "As Passenger"
+ )
+ StatItem(
+ title = "${state.driverRides}",
+ subtitle = "As Driver"
+ )
+ }
+ }
+}
+
+@Composable
+private fun StatItem(title: String, subtitle: String) {
+ Column(horizontalAlignment = Alignment.CenterHorizontally) {
+ Text(
+ text = title,
+ fontWeight = FontWeight.Bold,
+ fontSize = 18.sp
+ )
+ Spacer(Modifier.height(4.dp))
+ Text(
+ text = subtitle,
+ style = MaterialTheme.typography.bodySmall,
+ color = Color.Gray
+ )
+ }
+}
+
+@Composable
+private fun VerifiedCard() {
+ Card(
+ modifier = Modifier.fillMaxWidth(),
+ shape = RoundedCornerShape(16.dp),
+ colors = CardDefaults.cardColors(containerColor = Color.White),
+ elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
+ ) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(16.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Icon(
+ imageVector = Icons.Default.Verified,
+ contentDescription = null,
+ tint = Color(0xFF42B883),
+ modifier = Modifier.size(24.dp)
+ )
+ Spacer(Modifier.width(12.dp))
+ Column {
+ Text("Verified Account", fontWeight = FontWeight.SemiBold)
+ Text(
+ "ID and phone verified",
+ style = MaterialTheme.typography.bodySmall,
+ color = Color.Gray
+ )
+ }
+ }
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/profile/ProfileUiState.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/profile/ProfileUiState.kt
new file mode 100644
index 0000000..756ae4a
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/profile/ProfileUiState.kt
@@ -0,0 +1,81 @@
+package com.example.demo.feature.profile
+
+data class VehicleUi(
+ val id: Int,
+ val ownerUserId: Int,
+ val make: String,
+ val model: String,
+ val color: String,
+ val plate: String,
+ val seatsTotal: Int,
+ val year: Int,
+ val funFact: String
+)
+
+// Used only while editing in the dialog
+data class VehicleEditState(
+ val id: Int? = null,
+ val ownerUserId: Int? = null,
+ val make: String = "",
+ val model: String = "",
+ val color: String = "",
+ val plate: String = "",
+ val seatsTotal: String = "", // keep as String for text field
+ val year: String = "",
+ val funFact: String = ""
+)
+
+data class ProfileUiState(
+ val name: String = "John Doe",
+ val email: String = "john.doe@email.com",
+ val rating: Double = 4.8,
+ val totalRides: Int = 12,
+ val savedAmount: Int = 142,
+ val passengerRides: Int = 8,
+ val driverRides: Int = 4,
+ val isVerified: Boolean = true,
+ val vehicles: List = listOf(
+ VehicleUi(
+ id = 1,
+ ownerUserId = 42, // placeholder
+ make = "Tesla",
+ model = "Model Y",
+ color = "Blue",
+ plate = "ABC-1234",
+ seatsTotal = 5,
+ year = 2022,
+ funFact = "First EV on campus"
+ )
+ ),
+ val isSettingsDialogOpen: Boolean = false,
+ val selectedSettingsOption: SettingsOption? = null,
+
+ // dialog / editing state
+ val isVehicleDialogOpen: Boolean = false,
+ val vehicleEdit: VehicleEditState = VehicleEditState(),
+ val preferences: PreferencesState = PreferencesState(),
+ val account: AccountSettingsState = AccountSettingsState(),
+ val privacy: PrivacySafetyState = PrivacySafetyState(),
+)
+
+
+data class PreferencesState(
+ val notificationsEnabled: Boolean = true,
+ val emailUpdatesEnabled: Boolean = false,
+ val darkModeEnabled: Boolean = false,
+)
+
+data class AccountSettingsState(
+ val fullName: String = "John Doe",
+ val email: String = "john.doe@email.com",
+ val phone: String = "0123456789",
+ val password: String = "*******",
+)
+
+data class PrivacySafetyState(
+ val showProfilePublicly: Boolean = true,
+ val allowMessagesFromNonContacts: Boolean = true,
+ val shareTripHistoryWithFriends: Boolean = false,
+ val twoFactorEnabled: Boolean = false
+)
+
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/profile/ProfileViewModel.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/profile/ProfileViewModel.kt
new file mode 100644
index 0000000..6d063f1
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/profile/ProfileViewModel.kt
@@ -0,0 +1,259 @@
+package com.example.demo.feature.profile
+
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+
+import com.example.demo.feature.profile.data.InMemoryProfileRepository
+import com.example.demo.feature.profile.data.ProfileRepository
+
+class ProfileViewModel(
+ private val repository: ProfileRepository
+) {
+
+ private val _uiState = MutableStateFlow(repository.loadInitialProfile())
+ val uiState: StateFlow = _uiState
+
+ fun onEvent(event: ProfileEvent) {
+ when (event) {
+
+ is ProfileEvent.VehicleClicked -> {
+ val vehicle = _uiState.value.vehicles.firstOrNull { it.id == event.vehicleId }
+ ?: return
+
+ _uiState.value = _uiState.value.copy(
+ isVehicleDialogOpen = true,
+ vehicleEdit = VehicleEditState(
+ id = vehicle.id,
+ ownerUserId = vehicle.ownerUserId,
+ make = vehicle.make,
+ model = vehicle.model,
+ color = vehicle.color,
+ plate = vehicle.plate,
+ seatsTotal = vehicle.seatsTotal.toString(),
+ year = vehicle.year.toString(),
+ funFact = vehicle.funFact
+ )
+ )
+ }
+
+ ProfileEvent.VehicleDialogDismissed -> {
+ _uiState.value = _uiState.value.copy(
+ isVehicleDialogOpen = false,
+ vehicleEdit = VehicleEditState()
+ )
+ }
+
+ is ProfileEvent.VehicleEditMakeChanged ->
+ updateEdit { it.copy(make = event.value) }
+
+ is ProfileEvent.VehicleEditModelChanged ->
+ updateEdit { it.copy(model = event.value) }
+
+ is ProfileEvent.VehicleEditColorChanged ->
+ updateEdit { it.copy(color = event.value) }
+
+ is ProfileEvent.VehicleEditPlateChanged ->
+ updateEdit { it.copy(plate = event.value) }
+
+ is ProfileEvent.VehicleEditSeatsChanged ->
+ updateEdit { it.copy(seatsTotal = event.value) }
+
+ is ProfileEvent.VehicleEditYearChanged ->
+ updateEdit { it.copy(year = event.value) }
+
+ is ProfileEvent.VehicleEditFunFactChanged ->
+ updateEdit { it.copy(funFact = event.value) }
+
+ ProfileEvent.SaveVehicleChanges -> {
+ saveVehicle()
+ persist() // save to repo
+ }
+
+ is ProfileEvent.AddVehicleClicked -> {
+ _uiState.value = _uiState.value.copy(
+ isVehicleDialogOpen = true,
+ vehicleEdit = VehicleEditState(
+ id = null,
+ ownerUserId = 1, // TODO: replace with real user id later
+ make = "",
+ model = "",
+ color = "",
+ plate = "",
+ seatsTotal = "",
+ year = "",
+ funFact = "",
+ )
+ )
+ }
+
+ is ProfileEvent.DeleteVehicleClicked -> {
+ val edit = _uiState.value.vehicleEdit
+ val id = edit.id ?: return
+
+ _uiState.value = _uiState.value.copy(
+ vehicles = _uiState.value.vehicles.filterNot { it.id == id },
+ isVehicleDialogOpen = false,
+ vehicleEdit = VehicleEditState()
+ )
+ persist() // save to repo
+ }
+
+ is ProfileEvent.SettingsClicked -> {
+ _uiState.value = _uiState.value.copy(
+ isSettingsDialogOpen = true,
+ selectedSettingsOption = event.option
+ )
+ }
+
+ ProfileEvent.SettingsDialogDismissed -> {
+ _uiState.value = _uiState.value.copy(
+ isSettingsDialogOpen = false,
+ selectedSettingsOption = null
+ )
+ }
+
+ // ---------- Preferences ----------
+
+ is ProfileEvent.PreferencesChanged -> {
+ _uiState.value = _uiState.value.copy(
+ preferences = _uiState.value.preferences.copy(
+ notificationsEnabled = event.notificationsEnabled,
+ emailUpdatesEnabled = event.emailUpdatesEnabled,
+ darkModeEnabled = event.darkModeEnabled
+ )
+ )
+ }
+
+ ProfileEvent.SavePreferences -> {
+ println("Preferences saved: ${_uiState.value.preferences}")
+ persist() // save to repo
+ }
+
+ // ---------- Account Settings ----------
+
+ is ProfileEvent.AccountSettingsChanged -> {
+ val current = _uiState.value.account
+ _uiState.value = _uiState.value.copy(
+ account = current.copy(
+ fullName = event.fullName ?: current.fullName,
+ email = event.email ?: current.email,
+ phone = event.phone ?: current.phone,
+ password = event.password ?: current.password
+ )
+ )
+ }
+
+ ProfileEvent.SaveAccountSettings -> {
+ println("ACCOUNT UPDATED: ${_uiState.value.account}")
+ persist() // save to repo
+ }
+
+ ProfileEvent.DeleteAccount -> {
+ println("ACCOUNT DELETED (TODO: real delete + logout)")
+ // You might later clear profile / navigate away etc.
+ persist() // still log/save current state
+ }
+
+ // ---------- Privacy & Safety ----------
+
+ is ProfileEvent.PrivacySafetyChanged -> {
+ _uiState.value = _uiState.value.copy(
+ privacy = _uiState.value.privacy.copy(
+ showProfilePublicly = event.showProfilePublicly,
+ allowMessagesFromNonContacts = event.allowMessagesFromNonContacts,
+ shareTripHistoryWithFriends = event.shareTripHistoryWithFriends,
+ twoFactorEnabled = event.twoFactorEnabled
+ )
+ )
+ }
+
+ ProfileEvent.SavePrivacySafety -> {
+ println("Privacy & Safety saved: ${_uiState.value.privacy}")
+ persist() // save to repo
+ }
+ }
+ }
+
+ private fun updateEdit(transform: (VehicleEditState) -> VehicleEditState) {
+ _uiState.value = _uiState.value.copy(
+ vehicleEdit = transform(_uiState.value.vehicleEdit)
+ )
+ }
+
+ fun saveVehicle() {
+ val state = _uiState.value
+ val edit = state.vehicleEdit
+
+ // --- Enforce DB constraints safely ---
+
+ // Seats: parse, clamp 1..8, default to 4 if blank/bad
+ val seats = edit.seatsTotal
+ .toIntOrNull()
+ ?.coerceIn(1, 8)
+ ?: 4
+
+ // Year: parse, clamp 1900..2100, default to 2024 if blank/bad
+ val safeYear = edit.year
+ .toIntOrNull()
+ ?.coerceIn(1900, 2100)
+ ?: 2024
+
+ // Required strings: make sure they are not blank
+ val safeMake = edit.make.ifBlank { "Unknown" }
+ val safeModel = edit.model.ifBlank { "Car" }
+ val safeColor = edit.color.ifBlank { "Unknown" }
+ val basePlate = edit.plate.ifBlank { "TEMP" }
+
+ val updatedVehicles = if (edit.id == null) {
+ // New vehicle → CREATE
+ // Use negative IDs so we never collide with existing positive DB IDs
+ val currentMinId = state.vehicles.minOfOrNull { it.id } ?: 0
+ val newId = if (currentMinId > 0) -1 else currentMinId - 1
+
+ // Make sure plate is unique-ish if user left it blank
+ val safePlate = if (edit.plate.isBlank()) {
+ "$basePlate-$newId" // <-- use whatever base string you defined above
+ } else {
+ edit.plate
+ }
+
+ state.vehicles + VehicleUi(
+ id = newId,
+ ownerUserId = edit.ownerUserId ?: 1, // current user id placeholder
+ make = safeMake,
+ model = safeModel,
+ color = safeColor,
+ plate = safePlate,
+ seatsTotal = seats,
+ year = safeYear,
+ funFact = edit.funFact
+ )
+ } else {
+ // Existing vehicle → UPDATE
+ state.vehicles.map { v ->
+ if (v.id == edit.id) {
+ v.copy(
+ make = safeMake,
+ model = safeModel,
+ color = safeColor,
+ plate = if (edit.plate.isBlank()) v.plate else edit.plate,
+ seatsTotal = seats,
+ year = safeYear,
+ funFact = edit.funFact
+ )
+ } else v
+ }
+ }
+
+ _uiState.value = state.copy(
+ vehicles = updatedVehicles,
+ isVehicleDialogOpen = false,
+ vehicleEdit = VehicleEditState()
+ )
+ }
+
+ // Single place to write to our "fake DB"
+ private fun persist() {
+ repository.saveProfile(_uiState.value)
+ }
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/profile/components/SettingsCard.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/profile/components/SettingsCard.kt
new file mode 100644
index 0000000..de56c08
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/profile/components/SettingsCard.kt
@@ -0,0 +1,87 @@
+package com.example.demo.feature.profile.components
+
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.ChevronRight
+import androidx.compose.material.icons.filled.Settings
+import androidx.compose.material.icons.filled.Shield
+import androidx.compose.material.icons.filled.Tune
+import androidx.compose.material3.Card
+import androidx.compose.material3.CardDefaults
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.graphics.vector.ImageVector
+
+@Composable
+fun SettingsCard(
+ onAccountSettingsClick: () -> Unit,
+ onPreferencesClick: () -> Unit,
+ onPrivacySafetyClick: () -> Unit,
+ modifier: Modifier = Modifier
+) {
+ Card(
+ modifier = modifier.fillMaxWidth(),
+ shape = RoundedCornerShape(16.dp),
+ colors = CardDefaults.cardColors(containerColor = Color.White),
+ elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
+ ) {
+ Column(modifier = Modifier.padding(16.dp)) {
+ Text("Settings", fontWeight = FontWeight.SemiBold)
+ Spacer(Modifier.height(12.dp))
+
+ SettingsRow(
+ icon = Icons.Default.Settings,
+ label = "Account Settings",
+ onClick = onAccountSettingsClick
+ )
+ SettingsRow(
+ icon = Icons.Default.Tune,
+ label = "Preferences",
+ onClick = onPreferencesClick
+ )
+ SettingsRow(
+ icon = Icons.Default.Shield,
+ label = "Privacy & Safety",
+ onClick = onPrivacySafetyClick
+ )
+ }
+ }
+}
+
+@Composable
+private fun SettingsRow(
+ icon: ImageVector,
+ label: String,
+ onClick: () -> Unit
+) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(vertical = 8.dp)
+ .clickable { onClick() },
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Icon(
+ imageVector = icon,
+ contentDescription = null,
+ tint = Color.Gray,
+ modifier = Modifier.size(20.dp)
+ )
+ Spacer(Modifier.width(12.dp))
+ Text(label, modifier = Modifier.weight(1f), style = MaterialTheme.typography.bodyMedium)
+ Icon(
+ imageVector = Icons.Default.ChevronRight,
+ contentDescription = null,
+ tint = Color.Gray
+ )
+ }
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/profile/components/VehicleEditDialog.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/profile/components/VehicleEditDialog.kt
new file mode 100644
index 0000000..6894239
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/profile/components/VehicleEditDialog.kt
@@ -0,0 +1,156 @@
+package com.example.demo.feature.profile.components
+
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.*
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.Dialog
+import com.example.demo.feature.profile.ProfileEvent
+import com.example.demo.feature.profile.ProfileUiState
+import com.example.demo.ui.theme.DrexelBlue
+import com.example.demo.ui.theme.DrexelGold
+
+@Composable
+fun VehicleEditDialog(
+ state: ProfileUiState,
+ onEvent: (ProfileEvent) -> Unit
+) {
+ val edit = state.vehicleEdit
+ val isEditing = edit.id != null
+
+ Dialog(onDismissRequest = { onEvent(ProfileEvent.VehicleDialogDismissed) }) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 24.dp)
+ .clip(RoundedCornerShape(20.dp))
+ .background(Color.White)
+ ) {
+ Column(
+ modifier = Modifier.fillMaxWidth()
+ ) {
+
+ // Header
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(DrexelBlue)
+ .padding(16.dp),
+ contentAlignment = Alignment.CenterStart
+ ) {
+ Text(
+ text = if (isEditing) "Edit Vehicle" else "Add Vehicle",
+ color = Color.White,
+ style = MaterialTheme.typography.titleMedium
+ )
+ }
+
+ // Form fields
+ Column(
+ modifier = Modifier.padding(16.dp),
+ verticalArrangement = Arrangement.spacedBy(10.dp)
+ ) {
+ OutlinedTextField(
+ value = edit.make,
+ onValueChange = { onEvent(ProfileEvent.VehicleEditMakeChanged(it)) },
+ label = { Text("Make") },
+ shape = RoundedCornerShape(10.dp),
+ modifier = Modifier.fillMaxWidth()
+ )
+ OutlinedTextField(
+ value = edit.model,
+ onValueChange = { onEvent(ProfileEvent.VehicleEditModelChanged(it)) },
+ label = { Text("Model") },
+ shape = RoundedCornerShape(10.dp),
+ modifier = Modifier.fillMaxWidth()
+ )
+ OutlinedTextField(
+ value = edit.color,
+ onValueChange = { onEvent(ProfileEvent.VehicleEditColorChanged(it)) },
+ label = { Text("Color") },
+ shape = RoundedCornerShape(10.dp),
+ modifier = Modifier.fillMaxWidth()
+ )
+ OutlinedTextField(
+ value = edit.plate,
+ onValueChange = { onEvent(ProfileEvent.VehicleEditPlateChanged(it)) },
+ label = { Text("Plate") },
+ shape = RoundedCornerShape(10.dp),
+ modifier = Modifier.fillMaxWidth()
+ )
+ OutlinedTextField(
+ value = edit.seatsTotal,
+ onValueChange = { onEvent(ProfileEvent.VehicleEditSeatsChanged(it)) },
+ label = { Text("Seats (total)") },
+ shape = RoundedCornerShape(10.dp),
+ modifier = Modifier.fillMaxWidth()
+ )
+ OutlinedTextField(
+ value = edit.year,
+ onValueChange = { onEvent(ProfileEvent.VehicleEditYearChanged(it)) },
+ label = { Text("Year") },
+ shape = RoundedCornerShape(10.dp),
+ modifier = Modifier.fillMaxWidth()
+ )
+ OutlinedTextField(
+ value = edit.funFact,
+ onValueChange = { onEvent(ProfileEvent.VehicleEditFunFactChanged(it)) },
+ label = { Text("Fun fact about this car") },
+ shape = RoundedCornerShape(10.dp),
+ modifier = Modifier.fillMaxWidth()
+ )
+ }
+
+ // Buttons (Delete + Cancel + Save)
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(15.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+
+ // Only show Delete when editing an existing car
+ if (isEditing) {
+ OutlinedButton(
+ onClick = { onEvent(ProfileEvent.DeleteVehicleClicked) },
+ border = BorderStroke(1.dp, Color.Red),
+ colors = ButtonDefaults.outlinedButtonColors(
+ contentColor = Color.Red
+ )
+ ) {
+ Text("Delete")
+ }
+ }
+
+ Spacer(Modifier.weight(1f))
+
+ TextButton(
+ onClick = { onEvent(ProfileEvent.VehicleDialogDismissed) }
+ ) {
+ Text("Cancel", color = DrexelBlue)
+ }
+
+ Spacer(Modifier.width(8.dp))
+
+ Button(
+ onClick = { onEvent(ProfileEvent.SaveVehicleChanges) },
+ colors = ButtonDefaults.buttonColors(
+ containerColor = DrexelGold,
+ contentColor = DrexelBlue
+ ),
+ shape = RoundedCornerShape(10.dp)
+ ) {
+ Text("Save")
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/profile/components/VehiclesCard.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/profile/components/VehiclesCard.kt
new file mode 100644
index 0000000..b093849
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/profile/components/VehiclesCard.kt
@@ -0,0 +1,105 @@
+package com.example.demo.feature.profile.components
+
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.ChevronRight
+import androidx.compose.material.icons.filled.DirectionsCar
+import androidx.compose.material3.*
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import com.example.demo.feature.profile.ProfileEvent
+import com.example.demo.feature.profile.ProfileUiState
+import com.example.demo.ui.theme.DrexelBlue
+import com.example.demo.ui.theme.DrexelGold
+
+@Composable
+fun VehiclesCard(
+ state: ProfileUiState,
+ onEvent: (ProfileEvent) -> Unit
+) {
+ Card(
+ modifier = Modifier.fillMaxWidth(),
+ shape = RoundedCornerShape(16.dp),
+ colors = CardDefaults.cardColors(containerColor = Color.White),
+ elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
+ ) {
+ Column(modifier = Modifier.padding(16.dp)) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text("My Vehicles", fontWeight = FontWeight.SemiBold)
+
+ TextButton(
+ onClick = { onEvent(ProfileEvent.AddVehicleClicked) },
+ contentPadding = PaddingValues(0.dp)
+ ) {
+ Text(
+ text = "+ Add",
+ color = DrexelGold,
+ style = MaterialTheme.typography.bodyMedium
+ )
+ }
+ }
+
+ Spacer(Modifier.height(12.dp))
+
+ state.vehicles.forEach { vehicle ->
+ Card(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(vertical = 4.dp)
+ .clickable {
+ onEvent(ProfileEvent.VehicleClicked(vehicle.id))
+ },
+ shape = RoundedCornerShape(12.dp),
+ colors = CardDefaults.cardColors(containerColor = Color(0xFFF3F6FF)),
+ elevation = CardDefaults.cardElevation(defaultElevation = 0.dp)
+ ) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(12.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Icon(
+ imageVector = Icons.Default.DirectionsCar,
+ contentDescription = null,
+ tint = DrexelBlue,
+ modifier = Modifier.size(24.dp)
+ )
+ Spacer(Modifier.width(12.dp))
+ Column(modifier = Modifier.weight(1f)) {
+ Text(
+ "${vehicle.make} ${vehicle.model}",
+ fontWeight = FontWeight.SemiBold
+ )
+ Text(
+ "${vehicle.color} · ${vehicle.plate}",
+ style = MaterialTheme.typography.bodySmall,
+ color = Color.Gray
+ )
+ Text(
+ "${vehicle.seatsTotal} seats · ${vehicle.year}",
+ style = MaterialTheme.typography.bodySmall,
+ color = Color.Gray
+ )
+ }
+ Icon(
+ imageVector = Icons.Default.ChevronRight,
+ contentDescription = null,
+ tint = Color.Gray
+ )
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/profile/data/ProfileRepository.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/profile/data/ProfileRepository.kt
new file mode 100644
index 0000000..d9e160b
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/profile/data/ProfileRepository.kt
@@ -0,0 +1,29 @@
+package com.example.demo.feature.profile.data
+
+import com.example.demo.feature.profile.ProfileUiState
+
+/**
+ * This is our "data source" API.
+ * Later you can have a real DB implementation that also implements this.
+ */
+interface ProfileRepository {
+ fun loadInitialProfile(): ProfileUiState
+ fun saveProfile(state: ProfileUiState)
+}
+
+/**
+ * Simple in-memory implementation
+ * Acts like a fake database for now.
+ */
+class InMemoryProfileRepository : ProfileRepository {
+
+ // This is our "stored" profile (fake DB row)
+ private var current: ProfileUiState = ProfileUiState()
+
+ override fun loadInitialProfile(): ProfileUiState = current
+
+ override fun saveProfile(state: ProfileUiState) {
+ current = state
+ println(" [InMemoryProfileRepository] ProfileRepository.loadInitialProfile() current: $current")
+ }
+}
\ No newline at end of file
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/profile/pages/AccountSettingsScreen.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/profile/pages/AccountSettingsScreen.kt
new file mode 100644
index 0000000..8824116
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/profile/pages/AccountSettingsScreen.kt
@@ -0,0 +1,130 @@
+package com.example.demo.feature.profile.pages
+
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.ArrowBack
+import androidx.compose.material3.*
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import com.example.demo.feature.profile.AccountSettingsState
+import com.example.demo.ui.theme.DrexelBlue
+import com.example.demo.ui.theme.DrexelGold
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun AccountSettingsScreen(
+ state: AccountSettingsState,
+ onChange: (AccountSettingsState) -> Unit,
+ onSave: () -> Unit,
+ onDelete: () -> Unit,
+ onBack: () -> Unit,
+ modifier: Modifier = Modifier
+) {
+ Scaffold(
+ topBar = {
+ TopAppBar(
+ title = { Text("Account Settings") },
+ navigationIcon = {
+ IconButton(onClick = onBack) {
+ Icon(
+ imageVector = Icons.AutoMirrored.Filled.ArrowBack,
+ contentDescription = "Back",
+ tint = Color.White
+ )
+ }
+ },
+ colors = TopAppBarDefaults.topAppBarColors(
+ containerColor = DrexelBlue,
+ titleContentColor = Color.White,
+ navigationIconContentColor = Color.White
+ )
+ )
+ }
+ ) { padding ->
+ Column(
+ modifier = modifier
+ .padding(padding)
+ .fillMaxSize()
+ .background(Color(0xFFF5F5F7))
+ .verticalScroll(rememberScrollState())
+ .padding(16.dp),
+ verticalArrangement = Arrangement.spacedBy(16.dp)
+ ) {
+
+ val textFieldColors = OutlinedTextFieldDefaults.colors(
+ focusedBorderColor = DrexelBlue,
+ unfocusedBorderColor = Color.LightGray,
+ focusedLabelColor = DrexelBlue,
+ cursorColor = DrexelBlue
+ )
+
+ Text("Profile", style = MaterialTheme.typography.titleMedium, color = DrexelBlue)
+
+ OutlinedTextField(
+ value = state.fullName,
+ onValueChange = { onChange(state.copy(fullName = it)) },
+ label = { Text("Full Name") },
+ modifier = Modifier.fillMaxWidth(),
+ colors = textFieldColors
+ )
+
+ OutlinedTextField(
+ value = state.email,
+ onValueChange = { onChange(state.copy(email = it)) },
+ label = { Text("Email") },
+ modifier = Modifier.fillMaxWidth(),
+ colors = textFieldColors
+ )
+
+ OutlinedTextField(
+ value = state.phone,
+ onValueChange = { onChange(state.copy(phone = it)) },
+ label = { Text("Phone") },
+ modifier = Modifier.fillMaxWidth(),
+ colors = textFieldColors
+ )
+
+ Spacer(Modifier.height(8.dp))
+
+ Text("Security", style = MaterialTheme.typography.titleMedium, color = DrexelBlue)
+
+ OutlinedTextField(
+ value = state.password,
+ onValueChange = { onChange(state.copy(password = it)) },
+ label = { Text("Password") },
+ modifier = Modifier.fillMaxWidth(),
+ colors = textFieldColors
+ )
+
+ Spacer(Modifier.height(16.dp))
+
+ Button(
+ onClick = onSave,
+ modifier = Modifier.fillMaxWidth(),
+ colors = ButtonDefaults.buttonColors(
+ containerColor = DrexelGold,
+ contentColor = DrexelBlue
+ )
+ ) { Text("Update Account") }
+
+ Spacer(Modifier.height(24.dp))
+
+ Text("Danger Zone", style = MaterialTheme.typography.titleMedium, color = Color.Red)
+
+ OutlinedButton(
+ onClick = onDelete,
+ modifier = Modifier.fillMaxWidth(),
+ border = BorderStroke(1.dp, Color.Red),
+ colors = ButtonDefaults.outlinedButtonColors(contentColor = Color.Red)
+ ) {
+ Text("Delete Account")
+ }
+ }
+ }
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/profile/pages/PreferencesScreen.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/profile/pages/PreferencesScreen.kt
new file mode 100644
index 0000000..58392fe
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/profile/pages/PreferencesScreen.kt
@@ -0,0 +1,152 @@
+package com.example.demo.feature.profile.pages
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.ArrowBack
+import androidx.compose.material.icons.filled.ArrowBack
+import androidx.compose.material3.*
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import com.example.demo.feature.profile.PreferencesState
+import com.example.demo.ui.theme.DrexelBlue
+import com.example.demo.ui.theme.DrexelGold
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun PreferencesScreen(
+ prefs: PreferencesState,
+ onChange: (PreferencesState) -> Unit,
+ onSave: () -> Unit,
+ onBack: () -> Unit,
+ modifier: Modifier = Modifier
+) {
+ Scaffold(
+ topBar = {
+ TopAppBar(
+ title = { Text("Preferences") },
+ navigationIcon = {
+ IconButton(onClick = onBack) {
+ Icon(
+ imageVector = Icons.AutoMirrored.Filled.ArrowBack,
+ contentDescription = "Back",
+ tint = Color.White
+ )
+ }
+ },
+ colors = TopAppBarDefaults.topAppBarColors(
+ containerColor = DrexelBlue,
+ titleContentColor = Color.White,
+ navigationIconContentColor = Color.White
+ )
+ )
+ }
+ ) { padding ->
+ Column(
+ modifier = modifier
+ .padding(padding)
+ .fillMaxSize()
+ .background(Color(0xFFF5F5F7))
+ .verticalScroll(rememberScrollState())
+ .padding(16.dp),
+ verticalArrangement = Arrangement.spacedBy(16.dp)
+ ) {
+
+ Text(
+ "Notifications",
+ style = MaterialTheme.typography.titleMedium,
+ color = DrexelBlue
+ )
+
+ PreferenceSwitchRow(
+ title = "Push notifications",
+ subtitle = "Get alerts about upcoming rides and messages",
+ checked = prefs.notificationsEnabled,
+ onCheckedChange = {
+ onChange(
+ prefs.copy(notificationsEnabled = it)
+ )
+ }
+ )
+
+ PreferenceSwitchRow(
+ title = "Email updates",
+ subtitle = "Receive trip summaries and announcements",
+ checked = prefs.emailUpdatesEnabled,
+ onCheckedChange = {
+ onChange(
+ prefs.copy(emailUpdatesEnabled = it)
+ )
+ }
+ )
+
+ Spacer(Modifier.height(16.dp))
+
+ Text(
+ "Appearance",
+ style = MaterialTheme.typography.titleMedium,
+ color = DrexelBlue
+ )
+
+ PreferenceSwitchRow(
+ title = "Dark mode",
+ subtitle = "Use a darker color scheme at night",
+ checked = prefs.darkModeEnabled,
+ onCheckedChange = {
+ onChange(
+ prefs.copy(darkModeEnabled = it)
+ )
+ }
+ )
+
+ Spacer(Modifier.height(24.dp))
+
+ Button(
+ onClick = onSave,
+ modifier = Modifier.fillMaxWidth(),
+ colors = ButtonDefaults.buttonColors(
+ containerColor = DrexelGold,
+ contentColor = DrexelBlue
+ )
+ ) {
+ Text("Save Preferences")
+ }
+ }
+ }
+}
+
+@Composable
+fun PreferenceSwitchRow(
+ title: String,
+ subtitle: String,
+ checked: Boolean,
+ onCheckedChange: (Boolean) -> Unit
+) {
+ val switchColors = SwitchDefaults.colors(
+ checkedThumbColor = Color.White,
+ checkedTrackColor = DrexelBlue,
+ uncheckedThumbColor = Color.White,
+ uncheckedTrackColor = Color.LightGray,
+ checkedBorderColor = DrexelBlue,
+ uncheckedBorderColor = Color.LightGray
+ )
+
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween
+ ) {
+ Column(modifier = Modifier.weight(1f)) {
+ Text(title, style = MaterialTheme.typography.bodyLarge)
+ Text(subtitle, style = MaterialTheme.typography.bodySmall, color = Color.Gray)
+ }
+ Switch(
+ checked = checked,
+ onCheckedChange = onCheckedChange,
+ colors = switchColors
+ )
+ }
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/profile/pages/PrivacySafetyScreen.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/profile/pages/PrivacySafetyScreen.kt
new file mode 100644
index 0000000..0ac5ff2
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/profile/pages/PrivacySafetyScreen.kt
@@ -0,0 +1,162 @@
+package com.example.demo.feature.profile.pages
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.ArrowBack
+import androidx.compose.material3.*
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import com.example.demo.feature.profile.PrivacySafetyState
+import com.example.demo.ui.theme.DrexelBlue
+import com.example.demo.ui.theme.DrexelGold
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun PrivacySafetyScreen(
+ state: PrivacySafetyState,
+ onChange: (PrivacySafetyState) -> Unit,
+ onSave: () -> Unit,
+ onBack: () -> Unit,
+ modifier: Modifier = Modifier
+) {
+ Scaffold(
+ topBar = {
+ TopAppBar(
+ title = { Text("Privacy & Safety") },
+ navigationIcon = {
+ IconButton(onClick = onBack) {
+ Icon(
+ imageVector = Icons.AutoMirrored.Filled.ArrowBack,
+ contentDescription = "Back",
+ tint = Color.White
+ )
+ }
+ },
+ colors = TopAppBarDefaults.topAppBarColors(
+ containerColor = DrexelBlue,
+ titleContentColor = Color.White,
+ navigationIconContentColor = Color.White
+ )
+ )
+ }
+ ) { padding ->
+ Column(
+ modifier = modifier
+ .padding(padding)
+ .fillMaxSize()
+ .background(Color(0xFFF5F5F7))
+ .verticalScroll(rememberScrollState())
+ .padding(16.dp),
+ verticalArrangement = Arrangement.spacedBy(16.dp)
+ ) {
+
+ Text(
+ "Profile Visibility",
+ style = MaterialTheme.typography.titleMedium,
+ color = DrexelBlue
+ )
+
+ PrivacySwitchRow(
+ title = "Show profile publicly",
+ subtitle = "Allow other students to view your basic profile",
+ checked = state.showProfilePublicly,
+ onCheckedChange = {
+ onChange(state.copy(showProfilePublicly = it))
+ }
+ )
+
+ PrivacySwitchRow(
+ title = "Allow messages from non-contacts",
+ subtitle = "Let people who haven't ridden with you send requests",
+ checked = state.allowMessagesFromNonContacts,
+ onCheckedChange = {
+ onChange(state.copy(allowMessagesFromNonContacts = it))
+ }
+ )
+
+ Spacer(Modifier.height(12.dp))
+
+ Text(
+ "Sharing",
+ style = MaterialTheme.typography.titleMedium,
+ color = DrexelBlue
+ )
+
+ PrivacySwitchRow(
+ title = "Share trip history with friends",
+ subtitle = "Friends can see your past rides and eco impact",
+ checked = state.shareTripHistoryWithFriends,
+ onCheckedChange = {
+ onChange(state.copy(shareTripHistoryWithFriends = it))
+ }
+ )
+
+ Spacer(Modifier.height(12.dp))
+
+ Text(
+ "Security",
+ style = MaterialTheme.typography.titleMedium,
+ color = DrexelBlue
+ )
+
+ PrivacySwitchRow(
+ title = "Two-factor authentication",
+ subtitle = "Add an extra step when logging into your account",
+ checked = state.twoFactorEnabled,
+ onCheckedChange = {
+ onChange(state.copy(twoFactorEnabled = it))
+ }
+ )
+
+ Spacer(Modifier.height(24.dp))
+
+ Button(
+ onClick = onSave,
+ modifier = Modifier.fillMaxWidth(),
+ colors = ButtonDefaults.buttonColors(
+ containerColor = DrexelGold,
+ contentColor = DrexelBlue
+ )
+ ) {
+ Text("Save Privacy Settings")
+ }
+ }
+ }
+}
+
+@Composable
+private fun PrivacySwitchRow(
+ title: String,
+ subtitle: String,
+ checked: Boolean,
+ onCheckedChange: (Boolean) -> Unit
+) {
+ val switchColors = SwitchDefaults.colors(
+ checkedThumbColor = Color.White,
+ checkedTrackColor = DrexelBlue,
+ uncheckedThumbColor = Color.White,
+ uncheckedTrackColor = Color.LightGray,
+ checkedBorderColor = DrexelBlue,
+ uncheckedBorderColor = Color.LightGray
+ )
+
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween
+ ) {
+ Column(modifier = Modifier.weight(1f)) {
+ Text(title, style = MaterialTheme.typography.bodyLarge)
+ Text(subtitle, style = MaterialTheme.typography.bodySmall, color = Color.Gray)
+ }
+ Switch(
+ checked = checked,
+ onCheckedChange = onCheckedChange,
+ colors = switchColors
+ )
+ }
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/rides/.gitkeep b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/rides/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/rides/AvailableOffersScreen.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/rides/AvailableOffersScreen.kt
new file mode 100644
index 0000000..58d8143
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/rides/AvailableOffersScreen.kt
@@ -0,0 +1,352 @@
+package com.example.demo.feature.rides
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.*
+import androidx.compose.material3.*
+import androidx.compose.runtime.*
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.example.demo.ui.theme.DrexelBlue
+import com.example.demo.ui.theme.DrexelGold
+import com.example.demo.ui.theme.FieldBackground
+import com.example.demo.ui.theme.HintGrey
+
+@Composable
+fun AvailableOfferScreen(
+ modifier: Modifier = Modifier,
+ onBack: () -> Unit = {},
+ onPublish: () -> Unit = {}
+) {
+ var origin by remember { mutableStateOf("University Crossings") }
+ var destination by remember { mutableStateOf("Korman Center") }
+ var departure by remember { mutableStateOf("") }
+
+ var selectedVehicle by remember { mutableStateOf("Tesla Model Y (Blue)") }
+ val vehicleOptions = listOf(
+ "Tesla Model Y (Blue)",
+ "Honda Civic (Black)",
+ "Toyota Camry (Silver)"
+ )
+
+ var selectedSeats by remember { mutableStateOf("2 Seats") }
+ val seatOptions = listOf("1 Seat", "2 Seats", "3 Seats", "4 Seats")
+
+ var basePrice by remember { mutableStateOf("5.00") }
+ var perMilePrice by remember { mutableStateOf("0.50") }
+
+ var scrollState = rememberScrollState()
+
+ Column(
+ modifier = modifier
+ .fillMaxSize()
+ .background(FieldBackground)
+ .verticalScroll(scrollState)
+ ) {
+ // ---------- HEADER ----------
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(DrexelBlue)
+ ) {
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 20.dp, vertical = 24.dp)
+ ) {
+ IconButton(onClick = onBack) {
+ Icon(
+ imageVector = Icons.Default.ArrowBack,
+ contentDescription = "Back",
+ tint = Color.White
+ )
+ }
+
+ Spacer(modifier = Modifier.height(8.dp))
+
+ Text(
+ text = "Offer a Ride",
+ style = MaterialTheme.typography.headlineSmall,
+ color = Color.White,
+ fontWeight = FontWeight.Bold
+ )
+
+ Spacer(modifier = Modifier.height(4.dp))
+
+ Text(
+ text = "Share your journey details",
+ style = MaterialTheme.typography.bodyMedium,
+ color = Color.White.copy(alpha = 0.8f)
+ )
+ }
+ }
+
+ Spacer(modifier = Modifier.height(16.dp))
+
+ // ---------- MAIN CARD ----------
+ Card(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp),
+ shape = RoundedCornerShape(24.dp),
+ colors = CardDefaults.cardColors(containerColor = Color.White),
+ elevation = CardDefaults.cardElevation(defaultElevation = 6.dp)
+ ) {
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 20.dp, vertical = 24.dp),
+ verticalArrangement = Arrangement.spacedBy(16.dp)
+ ) {
+ // Origin
+ LabeledIconField(
+ label = "Origin Location",
+ value = origin,
+ onValueChange = { origin = it },
+ icon = Icons.Default.Place,
+ placeholder = "University Crossings"
+ )
+
+ // Destination
+ LabeledIconField(
+ label = "Destination",
+ value = destination,
+ onValueChange = { destination = it },
+ icon = Icons.Default.Place,
+ placeholder = "Korman Center"
+ )
+
+ // Departure Time
+ LabeledIconField(
+ label = "Departure Time",
+ value = departure,
+ onValueChange = { departure = it },
+ icon = Icons.Default.AccessTime,
+ placeholder = "mm/dd/yyyy --:-- --"
+ )
+
+ // Vehicle dropdown
+ LabeledDropdownField(
+ label = "Choose Vehicle",
+ value = selectedVehicle,
+ onValueChange = { selectedVehicle = it },
+ options = vehicleOptions,
+ leadingIcon = Icons.Default.DirectionsCar
+ )
+
+ // Seats dropdown
+ LabeledDropdownField(
+ label = "Available Seats",
+ value = selectedSeats,
+ onValueChange = { selectedSeats = it },
+ options = seatOptions,
+ leadingIcon = Icons.Default.People
+ )
+
+ // Prices row
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(12.dp),
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ LabeledPriceField(
+ label = "Base Price",
+ value = basePrice,
+ onValueChange = { basePrice = it },
+ modifier = Modifier.weight(1f)
+ )
+
+ LabeledPriceField(
+ label = "Per Mile",
+ value = perMilePrice,
+ onValueChange = { perMilePrice = it },
+ modifier = Modifier.weight(1f)
+ )
+ }
+
+ Spacer(modifier = Modifier.height(8.dp))
+
+ // Publish button
+ Button(
+ onClick = onPublish,
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(52.dp),
+ colors = ButtonDefaults.buttonColors(
+ containerColor = DrexelGold,
+ contentColor = DrexelBlue
+ ),
+ shape = RoundedCornerShape(8.dp)
+ ) {
+ Text(
+ text = "Publish Offer",
+ fontWeight = FontWeight.SemiBold,
+ fontSize = 16.sp
+ )
+ }
+ }
+ }
+
+ Spacer(modifier = Modifier.height(16.dp))
+ }
+}
+
+/* ---------- Reusable components used on the screen ---------- */
+
+@Composable
+private fun FieldLabel(text: String) {
+ Text(
+ text = text,
+ style = MaterialTheme.typography.titleSmall,
+ fontWeight = FontWeight.SemiBold,
+ color = DrexelBlue,
+ modifier = Modifier.padding(bottom = 4.dp)
+ )
+}
+
+@Composable
+private fun LabeledIconField(
+ label: String,
+ value: String,
+ onValueChange: (String) -> Unit,
+ icon: androidx.compose.ui.graphics.vector.ImageVector,
+ placeholder: String
+) {
+ Column {
+ FieldLabel(label)
+
+ OutlinedTextField(
+ value = value,
+ onValueChange = onValueChange,
+ modifier = Modifier.fillMaxWidth(),
+ leadingIcon = {
+ Icon(
+ imageVector = icon,
+ contentDescription = null,
+ tint = HintGrey
+ )
+ },
+ placeholder = { Text(text = placeholder, color = HintGrey) },
+ shape = RoundedCornerShape(10.dp),
+ colors = OutlinedTextFieldDefaults.colors(
+ focusedContainerColor = Color(0xFFF5F6F8),
+ unfocusedContainerColor = Color(0xFFF5F6F8),
+ disabledContainerColor = Color(0xFFF5F6F8),
+ focusedBorderColor = Color.Transparent,
+ unfocusedBorderColor = Color.Transparent,
+ focusedTextColor = DrexelBlue,
+ unfocusedTextColor = DrexelBlue
+ ),
+ singleLine = true
+ )
+ }
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+private fun LabeledDropdownField(
+ label: String,
+ value: String,
+ onValueChange: (String) -> Unit,
+ options: List,
+ leadingIcon: androidx.compose.ui.graphics.vector.ImageVector
+) {
+ var expanded by remember { mutableStateOf(false) }
+
+ Column {
+ FieldLabel(label)
+
+ ExposedDropdownMenuBox(
+ expanded = expanded,
+ onExpandedChange = { expanded = !expanded }
+ ) {
+ OutlinedTextField(
+ value = value,
+ onValueChange = {},
+ readOnly = true,
+ modifier = Modifier
+ .menuAnchor()
+ .fillMaxWidth(),
+ leadingIcon = {
+ Icon(
+ imageVector = leadingIcon,
+ contentDescription = null,
+ tint = HintGrey
+ )
+ },
+ trailingIcon = {
+ ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded)
+ },
+ shape = RoundedCornerShape(10.dp),
+ colors = OutlinedTextFieldDefaults.colors(
+ focusedContainerColor = Color(0xFFF5F6F8),
+ unfocusedContainerColor = Color(0xFFF5F6F8),
+ disabledContainerColor = Color(0xFFF5F6F8),
+ focusedBorderColor = Color.Transparent,
+ unfocusedBorderColor = Color.Transparent,
+ focusedTextColor = DrexelBlue,
+ unfocusedTextColor = DrexelBlue
+ ),
+ singleLine = true
+ )
+
+ ExposedDropdownMenu(
+ expanded = expanded,
+ onDismissRequest = { expanded = false }
+ ) {
+ options.forEach { option ->
+ DropdownMenuItem(
+ text = { Text(option) },
+ onClick = {
+ onValueChange(option)
+ expanded = false
+ }
+ )
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun LabeledPriceField(
+ label: String,
+ value: String,
+ onValueChange: (String) -> Unit,
+ modifier: Modifier = Modifier
+) {
+ Column(modifier = modifier) {
+ FieldLabel(label)
+
+ OutlinedTextField(
+ value = value,
+ onValueChange = onValueChange,
+ modifier = Modifier.fillMaxWidth(),
+ leadingIcon = {
+ Text(
+ text = "$",
+ color = HintGrey,
+ fontWeight = FontWeight.SemiBold
+ )
+ },
+ shape = RoundedCornerShape(10.dp),
+ colors = OutlinedTextFieldDefaults.colors(
+ focusedContainerColor = Color(0xFFF5F6F8),
+ unfocusedContainerColor = Color(0xFFF5F6F8),
+ disabledContainerColor = Color(0xFFF5F6F8),
+ focusedBorderColor = Color.Transparent,
+ unfocusedBorderColor = Color.Transparent,
+ focusedTextColor = DrexelBlue,
+ unfocusedTextColor = DrexelBlue
+ ),
+ singleLine = true
+ )
+ }
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/rides/AvailableRidesScreen.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/rides/AvailableRidesScreen.kt
new file mode 100644
index 0000000..b43a64c
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/rides/AvailableRidesScreen.kt
@@ -0,0 +1,224 @@
+package com.example.demo.feature.rides
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.*
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.example.demo.ui.theme.DrexelBlue
+import com.example.demo.ui.theme.DrexelGold
+import com.example.demo.ui.theme.HintGrey
+import com.example.demo.ui.theme.FieldBackground
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.*
+import androidx.compose.material3.Icon
+
+import androidx.compose.runtime.*
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.setValue
+import kotlinx.coroutines.launch
+import com.example.demo.feature.db.RideOffer
+import com.example.demo.feature.db.RideRepository
+
+@Composable
+fun AvailableRidesScreen(
+ modifier: Modifier = Modifier,
+ onBack: () -> Unit = {},
+ rideRepository: RideRepository
+) {
+ val coroutineScope = rememberCoroutineScope()
+
+ var isLoading by remember { mutableStateOf(true) }
+ var errorMessage by remember { mutableStateOf(null) }
+ var rideOffers by remember { mutableStateOf>(emptyList()) }
+
+ // load from DB on first composition
+ LaunchedEffect(Unit) {
+ try {
+ isLoading = true
+ errorMessage = null
+ rideOffers = rideRepository.getOpenRideOffers()
+ } catch (e: Exception) {
+ errorMessage = e.message ?: "Failed to load rides"
+ } finally {
+ isLoading = false
+ }
+ }
+
+ // simple “best / other” split (first 3 vs rest)
+ val bestMatches: List =
+ if (rideOffers.size > 3) rideOffers.take(3) else rideOffers
+ val otherMatches: List =
+ if (rideOffers.size > 3) rideOffers.drop(3) else emptyList()
+
+ Column(
+ modifier = modifier
+ .fillMaxSize()
+ .background(FieldBackground)
+ ) {
+ // your existing header / back button code stays the same
+
+ if (isLoading) {
+ Box(
+ modifier = Modifier
+ .fillMaxSize(),
+ contentAlignment = Alignment.Center
+ ) {
+ CircularProgressIndicator(color = DrexelBlue)
+ }
+ return@Column
+ }
+
+ if (errorMessage != null) {
+ Box(
+ modifier = Modifier
+ .fillMaxSize(),
+ contentAlignment = Alignment.Center
+ ) {
+ Text(
+ text = errorMessage!!,
+ color = Color.Red
+ )
+ }
+ return@Column
+ }
+
+ LazyColumn(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(horizontal = 16.dp, vertical = 12.dp),
+ verticalArrangement = Arrangement.spacedBy(12.dp)
+ ) {
+ if (bestMatches.isNotEmpty()) {
+ item {
+ SectionBadge(
+ text = "Best Matches",
+ color = DrexelGold,
+ textColor = DrexelBlue
+ )
+ Spacer(modifier = Modifier.height(8.dp))
+ }
+
+ items(bestMatches) { offer ->
+ RideCard(offer = offer)
+ }
+ }
+
+ if (otherMatches.isNotEmpty()) {
+ item {
+ Spacer(modifier = Modifier.height(16.dp))
+ SectionBadge(
+ text = "Other Matches",
+ color = Color.White,
+ textColor = HintGrey,
+ isBordered = true
+ )
+ Spacer(modifier = Modifier.height(8.dp))
+ }
+
+ items(otherMatches) { offer ->
+ RideCard(offer = offer)
+ }
+ }
+ }
+ }
+}
+
+
+
+@Composable
+fun RideCard(offer: RideOffer) {
+ Card(
+ shape = RoundedCornerShape(16.dp),
+ colors = CardDefaults.cardColors(containerColor = Color.White),
+ elevation = CardDefaults.cardElevation(defaultElevation = 2.dp),
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Column(
+ modifier = Modifier.padding(16.dp)
+ ) {
+ // You don't currently join driver name / rating / car model in RideOffer,
+ // so we’ll show what we *do* have and use placeholders where needed.
+
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween
+ ) {
+ Column {
+ Text(
+ text = "Driver #${offer.driverId}",
+ style = MaterialTheme.typography.titleMedium,
+ fontWeight = FontWeight.Bold,
+ color = DrexelBlue
+ )
+ Text(
+ text = "Vehicle #${offer.vehicleId}",
+ style = MaterialTheme.typography.bodySmall,
+ color = HintGrey
+ )
+ }
+
+ Column(horizontalAlignment = Alignment.End) {
+ Text(
+ text = "$${"%.2f".format(offer.priceBase)}",
+ style = MaterialTheme.typography.titleMedium,
+ fontWeight = FontWeight.Bold,
+ color = DrexelBlue
+ )
+ Text(
+ text = "${offer.seatsAvailable} seats",
+ style = MaterialTheme.typography.bodySmall,
+ color = HintGrey
+ )
+ }
+ }
+
+ Spacer(modifier = Modifier.height(12.dp))
+
+ Text(
+ text = "${offer.fromName} → ${offer.toName}",
+ style = MaterialTheme.typography.bodyMedium,
+ color = DrexelBlue
+ )
+
+ Spacer(modifier = Modifier.height(8.dp))
+
+ Text(
+ text = offer.departAt,
+ style = MaterialTheme.typography.bodyMedium,
+ color = DrexelBlue
+ )
+ }
+ }
+}
+
+@Composable
+fun SectionBadge(
+ text: String,
+ color: Color,
+ textColor: Color,
+ isBordered: Boolean = false
+) {
+ Surface(
+ color = color,
+ shape = RoundedCornerShape(50),
+ border = if (isBordered) null else null,
+ modifier = Modifier.wrapContentSize()
+ ) {
+ Text(
+ text = text,
+ modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
+ style = MaterialTheme.typography.labelMedium,
+ fontWeight = FontWeight.Bold,
+ color = textColor
+ )
+ }
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/rides/FieldInputs.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/rides/FieldInputs.kt
new file mode 100644
index 0000000..baf2f79
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/rides/FieldInputs.kt
@@ -0,0 +1,80 @@
+package com.example.demo.feature.rides
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.OutlinedTextFieldDefaults
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.painter.Painter
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import com.example.demo.ui.theme.DrexelBlue
+import com.example.demo.ui.theme.HintGrey
+
+@Composable
+fun RideInput(
+ label: String,
+ icon: ImageVector,
+ placeholder: String,
+ modifier: Modifier = Modifier
+) {
+ var textState by remember { mutableStateOf("") }
+ val fieldColor = Color(0xFFF3F4F6)
+
+ Column(modifier = modifier) {
+ Text(
+ text = label,
+ style = MaterialTheme.typography.titleMedium,
+ color = DrexelBlue,
+ fontWeight = FontWeight.SemiBold,
+ modifier = Modifier.padding(bottom = 4.dp)
+ )
+
+ OutlinedTextField(
+ value = textState,
+ onValueChange = { textState = it },
+ modifier = Modifier.fillMaxWidth(),
+
+ placeholder = {
+ Text(
+ text = placeholder,
+ color = HintGrey
+ )
+ },
+
+ leadingIcon = {
+ Icon(
+ imageVector = icon,
+ contentDescription = null,
+ tint = HintGrey,
+ modifier = Modifier.size(28.dp)
+ )
+ },
+
+ shape = RoundedCornerShape(8.dp),
+ colors = OutlinedTextFieldDefaults.colors(
+ focusedContainerColor = fieldColor,
+ unfocusedContainerColor = fieldColor,
+ disabledContainerColor = fieldColor,
+ focusedBorderColor = Color.Transparent,
+ unfocusedBorderColor = Color.Transparent,
+ focusedTextColor = DrexelBlue,
+ unfocusedTextColor = DrexelBlue
+ ),
+ singleLine = true,
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/rides/FindRideScreen.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/rides/FindRideScreen.kt
new file mode 100644
index 0000000..83313d1
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/rides/FindRideScreen.kt
@@ -0,0 +1,144 @@
+package com.example.demo.ui.theme
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.*
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.example.demo.feature.rides.RideInput
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.*
+import androidx.compose.material3.Icon
+
+@Composable
+fun FindRideScreen() {
+ val bgColor = Color(0xFFF3F4F6)
+ val scrollState = rememberScrollState()
+
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(bgColor)
+ ) {
+ // 1. Header Section
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(200.dp)
+ .background(DrexelBlue)
+ .padding(24.dp)
+ ) {
+ IconButton(onClick = { /* TODO: handle back nav */ }) {
+ Icon(
+ imageVector = Icons.Filled.ArrowBack,
+ contentDescription = "Back",
+ tint = Color.White
+ )
+ }
+
+ Spacer(modifier = Modifier.height(8.dp))
+
+ Text(
+ text = "Find a Ride",
+ style = MaterialTheme.typography.titleLarge,
+ color = Color.White
+ )
+
+ Spacer(modifier = Modifier.height(8.dp))
+
+ Text(
+ text = "Enter your trip details",
+ style = MaterialTheme.typography.titleLarge,
+ color = HintGrey
+ )
+ }
+
+ // 2. The Floating Card
+ Card(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(top = 175.dp, start = 16.dp, end = 16.dp, bottom = 16.dp)
+ .verticalScroll(scrollState), // Make form scrollable on small screens
+ shape = RoundedCornerShape(16.dp),
+ colors = CardDefaults.cardColors(containerColor = Color.White),
+ elevation = CardDefaults.cardElevation(defaultElevation = 8.dp)
+ ) {
+ Column(
+ modifier = Modifier.padding(20.dp),
+ verticalArrangement = Arrangement.spacedBy(16.dp)
+ ) {
+ // Locations
+ RideInput(
+ label = "Pickup Location",
+ icon = Icons.Filled.LocationOn,
+ placeholder = "30th Street Station"
+ )
+ RideInput(
+ label = "Drop-off Location",
+ icon = Icons.Filled.LocationOn,
+ placeholder = "Cira Green"
+ )
+
+ // Date & Time
+ Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
+ RideInput(
+ label = "Date",
+ icon = Icons.Filled.CalendarMonth,
+ placeholder = "mm/dd/yyyy",
+ modifier = Modifier.weight(1f)
+ )
+ RideInput(
+ label = "Time",
+ icon = Icons.Filled.AccessTime,
+ placeholder = "--:-- --",
+ modifier = Modifier.weight(1f)
+ )
+ }
+
+ // Seats & Price
+ Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
+ RideInput(
+ label = "Seats Needed",
+ icon = Icons.Filled.Group,
+ placeholder = "2",
+ modifier = Modifier.weight(1f)
+ )
+ RideInput(
+ label = "Max Price",
+ icon = Icons.Filled.AttachMoney,
+ placeholder = "20",
+ modifier = Modifier.weight(1f)
+ )
+ }
+
+ Spacer(modifier = Modifier.height(4.dp))
+
+ // Search Button
+ Button(
+ onClick = { /* TODO: trigger search */ },
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(50.dp),
+ colors = ButtonDefaults.buttonColors(
+ containerColor = DrexelGold,
+ contentColor = DrexelBlue
+ ),
+ shape = RoundedCornerShape(8.dp)
+ ) {
+ Text(
+ text = "Search for Rides",
+ fontWeight = FontWeight.Bold,
+ fontSize = 16.sp
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/rides/OfferRideScreen.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/rides/OfferRideScreen.kt
new file mode 100644
index 0000000..815342f
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/rides/OfferRideScreen.kt
@@ -0,0 +1,147 @@
+package com.example.demo.ui.theme
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.*
+import androidx.compose.material3.*
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.example.demo.feature.rides.RideInput
+
+@Composable
+fun OfferRideScreen(
+ modifier: Modifier = Modifier,
+ onBack: () -> Unit = {},
+ onPublish: () -> Unit = {}
+) {
+ val bgColor = Color(0xFFF3F4F6)
+
+ Column(
+ modifier = modifier
+ .fillMaxSize()
+ .background(bgColor)
+ ) {
+ // ---------- HEADER (smaller) ----------
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(DrexelBlue)
+ .padding(horizontal = 20.dp, vertical = 16.dp)
+ ) {
+ IconButton(onClick = onBack) {
+ Icon(
+ imageVector = Icons.Filled.ArrowBack,
+ contentDescription = "Back",
+ tint = Color.White
+ )
+ }
+
+ Text(
+ text = "Offer a Ride",
+ style = MaterialTheme.typography.titleMedium, // smaller
+ color = Color.White
+ )
+
+ Spacer(modifier = Modifier.height(4.dp))
+
+ Text(
+ text = "Share your journey details",
+ style = MaterialTheme.typography.bodySmall,
+ color = Color.White.copy(alpha = 0.8f)
+ )
+ }
+
+ Spacer(modifier = Modifier.height(12.dp))
+
+ // ---------- CARD ----------
+ Card(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp),
+ shape = RoundedCornerShape(16.dp),
+ colors = CardDefaults.cardColors(containerColor = Color.White),
+ elevation = CardDefaults.cardElevation(defaultElevation = 6.dp)
+ ) {
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp, vertical = 12.dp),
+ verticalArrangement = Arrangement.spacedBy(8.dp) // tighter spacing
+ ) {
+ // Origin & Destination
+ RideInput(
+ label = "Origin",
+ icon = Icons.Filled.LocationOn,
+ placeholder = "University Crossings"
+ )
+ RideInput(
+ label = "Destination",
+ icon = Icons.Filled.LocationOn,
+ placeholder = "Korman Center"
+ )
+
+ // Time + Seats (2 per row to save height)
+ Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
+ RideInput(
+ label = "Time",
+ icon = Icons.Filled.AccessTime,
+ placeholder = "mm/dd hh:mm",
+ modifier = Modifier.weight(1f)
+ )
+ RideInput(
+ label = "Seats",
+ icon = Icons.Filled.Group,
+ placeholder = "2",
+ modifier = Modifier.weight(1f)
+ )
+ }
+
+ // Vehicle + Price (2 per row)
+ Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
+ RideInput(
+ label = "Vehicle",
+ icon = Icons.Filled.DirectionsCar,
+ placeholder = "Model Y",
+ modifier = Modifier.weight(1f)
+ )
+ RideInput(
+ label = "Price",
+ icon = Icons.Filled.AttachMoney,
+ placeholder = "5.00",
+ modifier = Modifier.weight(1f)
+ )
+ }
+
+ Spacer(modifier = Modifier.height(4.dp))
+
+ // ---------- BUTTON (always visible) ----------
+ Button(
+ onClick = onPublish,
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(44.dp), // a bit shorter
+ colors = ButtonDefaults.buttonColors(
+ containerColor = DrexelGold,
+ contentColor = DrexelBlue
+ ),
+ shape = RoundedCornerShape(8.dp)
+ ) {
+ Text(
+ text = "Publish Offer",
+ fontWeight = FontWeight.SemiBold,
+ fontSize = 14.sp // smaller
+ )
+ }
+ }
+ }
+
+ // tiny spacer so it doesn’t touch bottom nav
+ Spacer(modifier = Modifier.height(8.dp))
+ }
+}
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/rides/RideOption.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/rides/RideOption.kt
new file mode 100644
index 0000000..f50636d
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/feature/rides/RideOption.kt
@@ -0,0 +1,14 @@
+package com.example.demo.feature.rides
+
+// Simple data class to hold ride info
+data class RideOption(
+ val driverName: String,
+ val rating: Double,
+ val price: Double,
+ val seats: Int,
+ val carModel: String,
+ val pickup: String,
+ val dropoff: String,
+ val time: String,
+ val isBestMatch: Boolean = false
+)
\ No newline at end of file
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/ui/theme/Color.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/ui/theme/Color.kt
new file mode 100644
index 0000000..80727f1
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/ui/theme/Color.kt
@@ -0,0 +1,9 @@
+package com.example.demo.ui.theme
+
+import androidx.compose.ui.graphics.Color
+
+// Drexel University Colors
+val DrexelBlue = Color(0xFF07294D) // #07294D
+val DrexelGold = Color(0xFFFFC600) // #FFC600
+val FieldBackground = Color(0xFF073B70)
+val HintGrey = Color(0xFF9AA4B2)
\ No newline at end of file
diff --git a/app/composeApp/src/commonMain/kotlin/com/example/demo/ui/theme/Theme.kt b/app/composeApp/src/commonMain/kotlin/com/example/demo/ui/theme/Theme.kt
new file mode 100644
index 0000000..dca8c74
--- /dev/null
+++ b/app/composeApp/src/commonMain/kotlin/com/example/demo/ui/theme/Theme.kt
@@ -0,0 +1,23 @@
+package com.example.demo.ui.theme
+
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.lightColorScheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.graphics.Color
+
+@Composable
+fun FindMyRideTheme(content: @Composable () -> Unit) {
+ MaterialTheme(
+ colorScheme = lightColorScheme(),
+ typography = MaterialTheme.typography,
+ content = content,
+ )
+}
+
+@Composable
+fun lightColorScheme() = androidx.compose.material3.lightColorScheme(
+ primary = DrexelGold,
+ onPrimary = DrexelBlue,
+ background = DrexelBlue,
+ onBackground = Color.White,
+)
\ No newline at end of file
diff --git a/app/composeApp/src/commonTest/kotlin/com/example/demo/ComposeAppCommonTest.kt b/app/composeApp/src/commonTest/kotlin/com/example/demo/ComposeAppCommonTest.kt
new file mode 100644
index 0000000..6cd3a0a
--- /dev/null
+++ b/app/composeApp/src/commonTest/kotlin/com/example/demo/ComposeAppCommonTest.kt
@@ -0,0 +1,12 @@
+package com.example.app
+
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class ComposeAppCommonTest {
+
+ @Test
+ fun example() {
+ assertEquals(3, 1 + 2)
+ }
+}
\ No newline at end of file
diff --git a/app/composeApp/src/iosMain/kotlin/com/example/demo/MainViewController.kt b/app/composeApp/src/iosMain/kotlin/com/example/demo/MainViewController.kt
new file mode 100644
index 0000000..e65b194
--- /dev/null
+++ b/app/composeApp/src/iosMain/kotlin/com/example/demo/MainViewController.kt
@@ -0,0 +1,5 @@
+package com.example.app
+
+import androidx.compose.ui.window.ComposeUIViewController
+
+fun MainViewController() = ComposeUIViewController { App() }
\ No newline at end of file
diff --git a/app/composeApp/src/iosMain/kotlin/com/example/demo/Platform.ios.kt b/app/composeApp/src/iosMain/kotlin/com/example/demo/Platform.ios.kt
new file mode 100644
index 0000000..ab94116
--- /dev/null
+++ b/app/composeApp/src/iosMain/kotlin/com/example/demo/Platform.ios.kt
@@ -0,0 +1,9 @@
+package com.example.app
+
+import platform.UIKit.UIDevice
+
+class IOSPlatform : Platform {
+ override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion
+}
+
+actual fun getPlatform(): Platform = IOSPlatform()
\ No newline at end of file
diff --git a/app/composeApp/src/jsMain/kotlin/com/example/demo/Platform.js.kt b/app/composeApp/src/jsMain/kotlin/com/example/demo/Platform.js.kt
new file mode 100644
index 0000000..72a25f9
--- /dev/null
+++ b/app/composeApp/src/jsMain/kotlin/com/example/demo/Platform.js.kt
@@ -0,0 +1,7 @@
+package com.example.app
+
+class JsPlatform : Platform {
+ override val name: String = "Web with Kotlin/JS"
+}
+
+actual fun getPlatform(): Platform = JsPlatform()
\ No newline at end of file
diff --git a/app/composeApp/src/jvmMain/kotlin/com/example/demo/Platform.jvm.kt b/app/composeApp/src/jvmMain/kotlin/com/example/demo/Platform.jvm.kt
new file mode 100644
index 0000000..52ce616
--- /dev/null
+++ b/app/composeApp/src/jvmMain/kotlin/com/example/demo/Platform.jvm.kt
@@ -0,0 +1,7 @@
+package com.example.app
+
+class JVMPlatform : Platform {
+ override val name: String = "Java ${System.getProperty("java.version")}"
+}
+
+actual fun getPlatform(): Platform = JVMPlatform()
\ No newline at end of file
diff --git a/app/composeApp/src/jvmMain/kotlin/com/example/demo/main.kt b/app/composeApp/src/jvmMain/kotlin/com/example/demo/main.kt
new file mode 100644
index 0000000..1c17f1f
--- /dev/null
+++ b/app/composeApp/src/jvmMain/kotlin/com/example/demo/main.kt
@@ -0,0 +1,13 @@
+package com.example.app
+
+import androidx.compose.ui.window.Window
+import androidx.compose.ui.window.application
+
+fun main() = application {
+ Window(
+ onCloseRequest = ::exitApplication,
+ title = "app",
+ ) {
+ App()
+ }
+}
\ No newline at end of file
diff --git a/app/composeApp/src/wasmJsMain/kotlin/com/example/demo/Platform.wasmJs.kt b/app/composeApp/src/wasmJsMain/kotlin/com/example/demo/Platform.wasmJs.kt
new file mode 100644
index 0000000..e95802e
--- /dev/null
+++ b/app/composeApp/src/wasmJsMain/kotlin/com/example/demo/Platform.wasmJs.kt
@@ -0,0 +1,7 @@
+package com.example.app
+
+class WasmPlatform : Platform {
+ override val name: String = "Web with Kotlin/Wasm"
+}
+
+actual fun getPlatform(): Platform = WasmPlatform()
\ No newline at end of file
diff --git a/app/composeApp/src/webMain/kotlin/com/example/demo/main.kt b/app/composeApp/src/webMain/kotlin/com/example/demo/main.kt
new file mode 100644
index 0000000..756528d
--- /dev/null
+++ b/app/composeApp/src/webMain/kotlin/com/example/demo/main.kt
@@ -0,0 +1,11 @@
+package com.example.app
+
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.window.ComposeViewport
+
+@OptIn(ExperimentalComposeUiApi::class)
+fun main() {
+ ComposeViewport {
+ App()
+ }
+}
\ No newline at end of file
diff --git a/app/composeApp/src/webMain/resources/index.html b/app/composeApp/src/webMain/resources/index.html
new file mode 100644
index 0000000..1b4370f
--- /dev/null
+++ b/app/composeApp/src/webMain/resources/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ app
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/composeApp/src/webMain/resources/styles.css b/app/composeApp/src/webMain/resources/styles.css
new file mode 100644
index 0000000..0549b10
--- /dev/null
+++ b/app/composeApp/src/webMain/resources/styles.css
@@ -0,0 +1,7 @@
+html, body {
+ width: 100%;
+ height: 100%;
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+}
\ No newline at end of file
diff --git a/app/composeApp/webpack.config.d/watch.js b/app/composeApp/webpack.config.d/watch.js
new file mode 100644
index 0000000..04713d2
--- /dev/null
+++ b/app/composeApp/webpack.config.d/watch.js
@@ -0,0 +1,21 @@
+/*
+ * Temporary workaround for [KT-80582](https://youtrack.jetbrains.com/issue/KT-80582)
+ *
+ * This file should be safe to be removed once the ticket is closed and the project is updated to Kotlin version which solves that issue.
+ */
+config.watchOptions = config.watchOptions || {
+ ignored: ["**/*.kt", "**/node_modules"]
+}
+
+if (config.devServer) {
+ config.devServer.static = config.devServer.static.map(file => {
+ if (typeof file === "string") {
+ return {
+ directory: file,
+ watch: false,
+ }
+ } else {
+ return file
+ }
+ })
+}
\ No newline at end of file
diff --git a/app/gradle.properties b/app/gradle.properties
new file mode 100644
index 0000000..ff91b57
--- /dev/null
+++ b/app/gradle.properties
@@ -0,0 +1,12 @@
+#Kotlin
+kotlin.code.style=official
+kotlin.daemon.jvmargs=-Xmx3072M
+#Gradle
+org.gradle.jvmargs=-Xmx4096M -Dfile.encoding=UTF-8
+org.gradle.configuration-cache=true
+org.gradle.caching=true
+#Android
+android.nonTransitiveRClass=true
+android.useAndroidX=true
+# Force Gradle to use JDK 21 (not JBR or a JRE)
+org.gradle.java.home=C\:\\Program Files\\Java\\jdk-21
diff --git a/app/gradle/libs.versions.toml b/app/gradle/libs.versions.toml
new file mode 100644
index 0000000..cac3f9e
--- /dev/null
+++ b/app/gradle/libs.versions.toml
@@ -0,0 +1,37 @@
+[versions]
+agp = "8.11.2"
+android-compileSdk = "36"
+android-minSdk = "24"
+android-targetSdk = "36"
+androidx-activity = "1.11.0"
+androidx-appcompat = "1.7.1"
+androidx-core = "1.17.0"
+androidx-espresso = "3.7.0"
+androidx-lifecycle = "2.9.5"
+androidx-testExt = "1.3.0"
+composeHotReload = "1.0.0-rc02"
+composeMultiplatform = "1.9.1"
+junit = "4.13.2"
+kotlin = "2.2.20"
+kotlinx-coroutines = "1.10.2"
+
+[libraries]
+kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
+kotlin-testJunit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }
+junit = { module = "junit:junit", version.ref = "junit" }
+androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidx-core" }
+androidx-testExt-junit = { module = "androidx.test.ext:junit", version.ref = "androidx-testExt" }
+androidx-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "androidx-espresso" }
+androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" }
+androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity" }
+androidx-lifecycle-viewmodelCompose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "androidx-lifecycle" }
+androidx-lifecycle-runtimeCompose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidx-lifecycle" }
+kotlinx-coroutinesSwing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "kotlinx-coroutines" }
+
+[plugins]
+androidApplication = { id = "com.android.application", version.ref = "agp" }
+androidLibrary = { id = "com.android.library", version.ref = "agp" }
+composeHotReload = { id = "org.jetbrains.compose.hot-reload", version.ref = "composeHotReload" }
+composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "composeMultiplatform" }
+composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
+kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
\ No newline at end of file
diff --git a/app/gradle/wrapper/gradle-wrapper.jar b/app/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..1b33c55
Binary files /dev/null and b/app/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/app/gradle/wrapper/gradle-wrapper.properties b/app/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..d4081da
--- /dev/null
+++ b/app/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,7 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/app/gradlew b/app/gradlew
new file mode 100644
index 0000000..23d15a9
--- /dev/null
+++ b/app/gradlew
@@ -0,0 +1,251 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH="\\\"\\\""
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD=$JAVA_HOME/jre/sh/java
+ else
+ JAVACMD=$JAVA_HOME/bin/java
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ if ! command -v java >/dev/null 2>&1
+ then
+ die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command:
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+# and any embedded shellness will be escaped.
+# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+# treated as '${Hostname}' itself on the command line.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/app/gradlew.bat b/app/gradlew.bat
new file mode 100644
index 0000000..db3a6ac
--- /dev/null
+++ b/app/gradlew.bat
@@ -0,0 +1,94 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+@rem SPDX-License-Identifier: Apache-2.0
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 0 goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/app/iosApp/Configuration/Config.xcconfig b/app/iosApp/Configuration/Config.xcconfig
new file mode 100644
index 0000000..8fdfea9
--- /dev/null
+++ b/app/iosApp/Configuration/Config.xcconfig
@@ -0,0 +1,7 @@
+TEAM_ID=
+
+PRODUCT_NAME=app
+PRODUCT_BUNDLE_IDENTIFIER=com.example.app.app$(TEAM_ID)
+
+CURRENT_PROJECT_VERSION=1
+MARKETING_VERSION=1.0
\ No newline at end of file
diff --git a/app/iosApp/iosApp.xcodeproj/project.pbxproj b/app/iosApp/iosApp.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..465beec
--- /dev/null
+++ b/app/iosApp/iosApp.xcodeproj/project.pbxproj
@@ -0,0 +1,373 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 77;
+ objects = {
+
+/* Begin PBXFileReference section */
+ 7A380F3429DBE0DB29C95EE4 /* app.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = app.app; sourceTree = BUILT_PRODUCTS_DIR; };
+/* End PBXFileReference section */
+
+/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
+ 1092A42DE9B4AD61F5B2C54C /* Exceptions for "iosApp" folder in "iosApp" target */ = {
+ isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
+ membershipExceptions = (
+ Info.plist,
+ );
+ target = CD1B80002B634D87B36F80DB /* iosApp */;
+ };
+/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
+
+/* Begin PBXFileSystemSynchronizedRootGroup section */
+ 10A6EC1D812604177B0009FB /* iosApp */ = {
+ isa = PBXFileSystemSynchronizedRootGroup;
+ exceptions = (
+ 1092A42DE9B4AD61F5B2C54C /* Exceptions for "iosApp" folder in "iosApp" target */,
+ );
+ path = iosApp;
+ sourceTree = "";
+ };
+ DB82D36C9EA4FA1259DEEAF7 /* Configuration */ = {
+ isa = PBXFileSystemSynchronizedRootGroup;
+ path = Configuration;
+ sourceTree = "";
+ };
+/* End PBXFileSystemSynchronizedRootGroup section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ D165D7170AC0D4B9D475265B /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 4AC475FF37A34EEADD7B2792 = {
+ isa = PBXGroup;
+ children = (
+ DB82D36C9EA4FA1259DEEAF7 /* Configuration */,
+ 10A6EC1D812604177B0009FB /* iosApp */,
+ D4720B5C452919CC0AD4747C /* Products */,
+ );
+ sourceTree = "";
+ };
+ D4720B5C452919CC0AD4747C /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 7A380F3429DBE0DB29C95EE4 /* app.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ CD1B80002B634D87B36F80DB /* iosApp */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 5AAE3A3C287BA96EBB9E8785 /* Build configuration list for PBXNativeTarget "iosApp" */;
+ buildPhases = (
+ 6B8B1E6D425DF20B442A1F17 /* Compile Kotlin Framework */,
+ 0A6ABA9517D1D1FEC2A70CFC /* Sources */,
+ D165D7170AC0D4B9D475265B /* Frameworks */,
+ 50197EB1FF0366B234823AC3 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ fileSystemSynchronizedGroups = (
+ 10A6EC1D812604177B0009FB /* iosApp */,
+ );
+ name = iosApp;
+ packageProductDependencies = (
+ );
+ productName = iosApp;
+ productReference = 7A380F3429DBE0DB29C95EE4 /* app.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ F586D3F54435FE2812B6E7DD /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ BuildIndependentTargetsInParallel = 1;
+ LastSwiftUpdateCheck = 1620;
+ LastUpgradeCheck = 1620;
+ TargetAttributes = {
+ CD1B80002B634D87B36F80DB = {
+ CreatedOnToolsVersion = 16.2;
+ };
+ };
+ };
+ buildConfigurationList = F05A1F281F222D43AFA07DFB /* Build configuration list for PBXProject "iosApp" */;
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 4AC475FF37A34EEADD7B2792;
+ minimizedProjectReferenceProxies = 1;
+ preferredProjectObjectVersion = 77;
+ productRefGroup = D4720B5C452919CC0AD4747C /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ CD1B80002B634D87B36F80DB /* iosApp */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 50197EB1FF0366B234823AC3 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 6B8B1E6D425DF20B442A1F17 /* Compile Kotlin Framework */ = {
+ isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ );
+ name = "Compile Kotlin Framework";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "if [ \"YES\" = \"$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED\" ]; then\n echo \"Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \\\"YES\\\"\"\n exit 0\nfi\ncd \"$SRCROOT/..\"\n./gradlew :composeApp:embedAndSignAppleFrameworkForXcode\n";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 0A6ABA9517D1D1FEC2A70CFC /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+ B107BF0BF662703690766292 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReferenceAnchor = DB82D36C9EA4FA1259DEEAF7 /* Configuration */;
+ baseConfigurationReferenceRelativePath = Config.xcconfig;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 18.2;
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ 406B74BC13444CE80EBB1DA4 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReferenceAnchor = DB82D36C9EA4FA1259DEEAF7 /* Configuration */;
+ baseConfigurationReferenceRelativePath = Config.xcconfig;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 18.2;
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ F3177536EF5792537521DA90 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = arm64;
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_IDENTITY = "Apple Development";
+ CODE_SIGN_STYLE = Automatic;
+ DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
+ DEVELOPMENT_TEAM = "${TEAM_ID}";
+ ENABLE_PREVIEWS = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = iosApp/Info.plist;
+ INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
+ INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
+ INFOPLIST_KEY_UILaunchScreen_Generation = YES;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 20CC850455FA1663AF73B642 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = arm64;
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_IDENTITY = "Apple Development";
+ CODE_SIGN_STYLE = Automatic;
+ DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
+ DEVELOPMENT_TEAM = "${TEAM_ID}";
+ ENABLE_PREVIEWS = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = iosApp/Info.plist;
+ INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
+ INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
+ INFOPLIST_KEY_UILaunchScreen_Generation = YES;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ F05A1F281F222D43AFA07DFB /* Build configuration list for PBXProject "iosApp" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ B107BF0BF662703690766292 /* Debug */,
+ 406B74BC13444CE80EBB1DA4 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 5AAE3A3C287BA96EBB9E8785 /* Build configuration list for PBXNativeTarget "iosApp" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F3177536EF5792537521DA90 /* Debug */,
+ 20CC850455FA1663AF73B642 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = F586D3F54435FE2812B6E7DD /* Project object */;
+}
\ No newline at end of file
diff --git a/app/iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/app/iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..919434a
--- /dev/null
+++ b/app/iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/app/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json b/app/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json
new file mode 100644
index 0000000..0afb3cf
--- /dev/null
+++ b/app/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json
@@ -0,0 +1,11 @@
+{
+ "colors": [
+ {
+ "idiom": "universal"
+ }
+ ],
+ "info": {
+ "author": "xcode",
+ "version": 1
+ }
+}
diff --git a/app/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/app/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..fbe7ce1
--- /dev/null
+++ b/app/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,36 @@
+{
+ "images": [
+ {
+ "filename": "app-icon-1024.png",
+ "idiom": "universal",
+ "platform": "ios",
+ "size": "1024x1024"
+ },
+ {
+ "appearances": [
+ {
+ "appearance": "luminosity",
+ "value": "dark"
+ }
+ ],
+ "idiom": "universal",
+ "platform": "ios",
+ "size": "1024x1024"
+ },
+ {
+ "appearances": [
+ {
+ "appearance": "luminosity",
+ "value": "tinted"
+ }
+ ],
+ "idiom": "universal",
+ "platform": "ios",
+ "size": "1024x1024"
+ }
+ ],
+ "info": {
+ "author": "xcode",
+ "version": 1
+ }
+}
diff --git a/app/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png b/app/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png
new file mode 100644
index 0000000..53fc536
Binary files /dev/null and b/app/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png differ
diff --git a/app/iosApp/iosApp/Assets.xcassets/Contents.json b/app/iosApp/iosApp/Assets.xcassets/Contents.json
new file mode 100644
index 0000000..74d6a72
--- /dev/null
+++ b/app/iosApp/iosApp/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info": {
+ "author": "xcode",
+ "version": 1
+ }
+}
diff --git a/app/iosApp/iosApp/ContentView.swift b/app/iosApp/iosApp/ContentView.swift
new file mode 100644
index 0000000..c765ff2
--- /dev/null
+++ b/app/iosApp/iosApp/ContentView.swift
@@ -0,0 +1,21 @@
+import UIKit
+import SwiftUI
+import ComposeApp
+
+struct ComposeView: UIViewControllerRepresentable {
+ func makeUIViewController(context: Context) -> UIViewController {
+ MainViewControllerKt.MainViewController()
+ }
+
+ func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
+}
+
+struct ContentView: View {
+ var body: some View {
+ ComposeView()
+ .ignoresSafeArea()
+ }
+}
+
+
+
diff --git a/app/iosApp/iosApp/Info.plist b/app/iosApp/iosApp/Info.plist
new file mode 100644
index 0000000..11845e1
--- /dev/null
+++ b/app/iosApp/iosApp/Info.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ CADisableMinimumFrameDurationOnPhone
+
+
+
diff --git a/app/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json b/app/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json
new file mode 100644
index 0000000..74d6a72
--- /dev/null
+++ b/app/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info": {
+ "author": "xcode",
+ "version": 1
+ }
+}
diff --git a/app/iosApp/iosApp/iOSApp.swift b/app/iosApp/iosApp/iOSApp.swift
new file mode 100644
index 0000000..d83dca6
--- /dev/null
+++ b/app/iosApp/iosApp/iOSApp.swift
@@ -0,0 +1,10 @@
+import SwiftUI
+
+@main
+struct iOSApp: App {
+ var body: some Scene {
+ WindowGroup {
+ ContentView()
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/kotlin-js-store/wasm/yarn.lock b/app/kotlin-js-store/wasm/yarn.lock
new file mode 100644
index 0000000..5f4567d
--- /dev/null
+++ b/app/kotlin-js-store/wasm/yarn.lock
@@ -0,0 +1,8 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@js-joda/core@3.2.0":
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/@js-joda/core/-/core-3.2.0.tgz#3e61e21b7b2b8a6be746df1335cf91d70db2a273"
+ integrity sha512-PMqgJ0sw5B7FKb2d5bWYIoxjri+QlW/Pys7+Rw82jSH0QN3rB05jZ/VrrsUdh1w4+i2kw9JOejXGq/KhDOX7Kg==
diff --git a/app/settings.gradle.kts b/app/settings.gradle.kts
new file mode 100644
index 0000000..132559c
--- /dev/null
+++ b/app/settings.gradle.kts
@@ -0,0 +1,36 @@
+rootProject.name = "app"
+enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
+
+pluginManagement {
+ repositories {
+ google {
+ mavenContent {
+ includeGroupAndSubgroups("androidx")
+ includeGroupAndSubgroups("com.android")
+ includeGroupAndSubgroups("com.google")
+ }
+ }
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+
+dependencyResolutionManagement {
+ repositories {
+ google {
+ mavenContent {
+ includeGroupAndSubgroups("androidx")
+ includeGroupAndSubgroups("com.android")
+ includeGroupAndSubgroups("com.google")
+ }
+ }
+ mavenCentral()
+ }
+}
+
+plugins {
+ id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
+}
+
+include(":composeApp")
+project(":composeApp").projectDir = file("composeApp")
diff --git a/database/export_all.sql b/database/export_all.sql
index f994ec2..ca4b49e 100644
--- a/database/export_all.sql
+++ b/database/export_all.sql
@@ -22,4 +22,10 @@ SELECT * FROM "RIDE_MATCH" ORDER BY match_id;
.output exports/RATING.csv
SELECT * FROM "RATING" ORDER BY rating_id;
-.output stdout
+.output exports/MESSAGE_THREAD.csv
+SELECT * FROM "MESSAGE_THREAD" ORDER BY thread_id;
+
+.output exports/MESSAGE.csv
+SELECT * FROM "MESSAGE" ORDER BY message_id;
+
+.output stdout
\ No newline at end of file
diff --git a/database/findmyride.db b/database/findmyride.db
new file mode 100644
index 0000000..afaffe5
Binary files /dev/null and b/database/findmyride.db differ
diff --git a/database/generate_database.py b/database/generate_database.py
new file mode 100644
index 0000000..8424809
--- /dev/null
+++ b/database/generate_database.py
@@ -0,0 +1,542 @@
+#!/usr/bin/env python3
+"""
+generate_database.py
+1. Generates database/schema.sql and database/populate.sql.
+2. Creates 'findmyride.db' from scratch in the database folder.
+3. Executes the schema and population scripts against the database.
+4. Copies the finished database to ./app/composeApp/src/androidMain/assets/findmyride.db so that it can be used by the application
+"""
+
+import os
+import random
+import datetime
+import sqlite3
+import shutil
+from pathlib import Path
+
+random.seed(42)
+
+# --- CONFIGURATION ---
+
+# SQL Output Paths
+OUT_DIR = Path("database")
+OUT_DIR.mkdir(parents=True, exist_ok=True)
+SCHEMA_PATH = OUT_DIR / "schema.sql"
+POPULATE_PATH = OUT_DIR / "populate.sql"
+
+# DB Path
+DB_FILENAME = OUT_DIR / "findmyride.db"
+CURRENT_DB_PATH = Path(DB_FILENAME)
+
+# Target Asset Path
+ASSET_DIR = Path("./app/composeApp/src/androidMain/assets")
+ASSET_DB_PATH = ASSET_DIR / "findmyride.db"
+
+# --- SCHEMA DEFINITION ---
+SCHEMA_SQL = """
+PRAGMA foreign_keys = ON;
+
+CREATE TABLE "USER" (
+ user_id INTEGER PRIMARY KEY,
+ email TEXT NOT NULL UNIQUE,
+ username TEXT NOT NULL UNIQUE,
+ password_hash TEXT NOT NULL,
+ phone_number TEXT,
+ role TEXT NOT NULL CHECK (role IN ('rider','driver','both')),
+ rating_avg REAL DEFAULT 2.5 CHECK (rating_avg BETWEEN 0 AND 5),
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
+);
+
+CREATE TABLE "VEHICLE" (
+ vehicle_id INTEGER PRIMARY KEY,
+ owner_user_id INTEGER NOT NULL,
+ make TEXT NOT NULL,
+ model TEXT NOT NULL,
+ color TEXT,
+ plate TEXT NOT NULL UNIQUE,
+ seats_total INTEGER NOT NULL CHECK (seats_total BETWEEN 1 AND 8),
+ year INTEGER CHECK (year BETWEEN 1900 AND 2100),
+ fun_fact TEXT,
+ FOREIGN KEY (owner_user_id) REFERENCES "USER"(user_id)
+);
+
+CREATE TABLE "LOCATION" (
+ location_id INTEGER PRIMARY KEY,
+ name TEXT NOT NULL,
+ address TEXT NOT NULL
+);
+
+CREATE TABLE "RIDE_OFFER" (
+ offer_id INTEGER PRIMARY KEY,
+ driver_id INTEGER NOT NULL,
+ vehicle_id INTEGER NOT NULL,
+ original_location_id INTEGER NOT NULL,
+ dest_location_id INTEGER NOT NULL,
+ depart_at TEXT NOT NULL,
+ seats_available INTEGER NOT NULL CHECK (seats_available >= 0),
+ price_base NUMERIC(10,2) NOT NULL CHECK (price_base >= 0),
+ price_per_mile NUMERIC(10,2) NOT NULL CHECK (price_per_mile >= 0),
+ status TEXT NOT NULL CHECK (status IN ('open','closed','cancelled','full')),
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
+ FOREIGN KEY (driver_id) REFERENCES "USER"(user_id),
+ FOREIGN KEY (vehicle_id) REFERENCES "VEHICLE"(vehicle_id),
+ FOREIGN KEY (original_location_id) REFERENCES "LOCATION"(location_id),
+ FOREIGN KEY (dest_location_id) REFERENCES "LOCATION"(location_id)
+);
+
+CREATE TABLE "RIDE_REQUEST" (
+ request_id INTEGER PRIMARY KEY,
+ rider_id INTEGER NOT NULL,
+ pickup_location_id INTEGER NOT NULL,
+ dropoff_location_id INTEGER NOT NULL,
+ earliest_pickup TEXT NOT NULL,
+ latest_pickup TEXT,
+ seats_needed INTEGER NOT NULL CHECK (seats_needed >= 1),
+ status TEXT NOT NULL CHECK (status IN ('open','matched','cancelled','expired')),
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
+ FOREIGN KEY (rider_id) REFERENCES "USER"(user_id),
+ FOREIGN KEY (pickup_location_id) REFERENCES "LOCATION"(location_id),
+ FOREIGN KEY (dropoff_location_id) REFERENCES "LOCATION"(location_id),
+ CHECK (latest_pickup IS NULL OR latest_pickup >= earliest_pickup)
+);
+
+CREATE TABLE "RIDE_MATCH" (
+ match_id INTEGER PRIMARY KEY,
+ request_id INTEGER NOT NULL,
+ offer_id INTEGER NOT NULL,
+ seats_booked INTEGER NOT NULL CHECK (seats_booked >= 1),
+ price_total NUMERIC(10,2) NOT NULL CHECK (price_total >= 0),
+ state TEXT NOT NULL CHECK (state IN ('pending','confirmed','completed','cancelled','no_show')),
+ matched_at TEXT NOT NULL DEFAULT (datetime('now')),
+ FOREIGN KEY (request_id) REFERENCES "RIDE_REQUEST"(request_id),
+ FOREIGN KEY (offer_id) REFERENCES "RIDE_OFFER"(offer_id),
+ UNIQUE (request_id, offer_id)
+);
+
+CREATE TABLE "RATING" (
+ rating_id INTEGER PRIMARY KEY,
+ match_id INTEGER NOT NULL,
+ from_user_id INTEGER NOT NULL,
+ to_user_id INTEGER NOT NULL,
+ stars INTEGER NOT NULL CHECK (stars BETWEEN 1 AND 5),
+ comment TEXT,
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
+ FOREIGN KEY (match_id) REFERENCES "RIDE_MATCH"(match_id),
+ FOREIGN KEY (from_user_id) REFERENCES "USER"(user_id),
+ FOREIGN KEY (to_user_id) REFERENCES "USER"(user_id),
+ CHECK (from_user_id <> to_user_id),
+ UNIQUE (match_id, from_user_id, to_user_id)
+);
+
+CREATE TABLE "MESSAGE_THREAD" (
+ thread_id INTEGER PRIMARY KEY,
+ user1_id INTEGER NOT NULL,
+ user2_id INTEGER NOT NULL,
+ ride_match_id INTEGER,
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
+ FOREIGN KEY (user1_id) REFERENCES "USER"(user_id),
+ FOREIGN KEY (user2_id) REFERENCES "USER"(user_id),
+ FOREIGN KEY (ride_match_id) REFERENCES "RIDE_MATCH"(match_id),
+ CHECK (user1_id <> user2_id)
+);
+
+CREATE TABLE "MESSAGE" (
+ message_id INTEGER PRIMARY KEY,
+ thread_id INTEGER NOT NULL,
+ sender_id INTEGER NOT NULL,
+ body TEXT NOT NULL,
+ sent_at TEXT NOT NULL DEFAULT (datetime('now')),
+ FOREIGN KEY (thread_id) REFERENCES "MESSAGE_THREAD"(thread_id),
+ FOREIGN KEY (sender_id) REFERENCES "USER"(user_id)
+);
+"""
+
+# --- 1. HARDCODED DATA GENERATION ---
+
+# Hardcoded Preexisting Users
+users_existing = [
+ (1, "abdul_bookwala", "abdul.bookwala1@example.edu", "8569861234", "driver", 4.72, "2025-10-02T00:00:00"),
+ (2, "quincy_lu", "quincy.lu2@example.edu", "8569862334", "driver", 4.67, "2025-10-09T00:00:00"),
+ (3, "ame_shabuse", "ame.shabuse3@example.edu", "8569861334", "rider", 4.55, "2025-10-02T00:00:00"),
+ (4, "ame_lu", "ame.lu4@example.edu", "8569831234", "both", 4.54, "2025-10-10T00:00:00"),
+ (5, "kennan_shajid", "kennan.shajid5@example.edu", "8569861234", "both", 4.70, "2025-10-01T00:00:00"),
+ (1001, "driver.drexel", "driver.drexel@drexel.edu", "8560001001", "driver", 4.80, "2025-09-01 09:05:00"),
+ (1002, "rider.drexel", "rider.drexel@drexel.edu", "8560001002", "rider", 3.50, "2025-09-02 10:00:00"),
+ (1003, "both.guy", "both.guy@drexel.edu", "8560001003", "both", 4.20, "2025-09-03 11:00:00"),
+ (1004, "chill.rider", "chill.rider@drexel.edu", "8560001004", "rider", 5.00, "2025-09-04 12:00:00"),
+ (1005, "late.canceller", "late.canceller@drexel.edu", "8560001005", "rider", 1.90, "2025-09-05 13:00:00"),
+]
+
+# Hardcoded Preexisting Vehicles
+vehicles_existing = [
+ (1,2,"Tesla","Model Y","Blue","NJ-7909",7,2025,"n/a"),
+ (2,5,"Tesla","Model 3","Gray","NJ-6737",5,2024,"n/a"),
+ (3,6,"Toyota","Camry","Silver","NJ-8767",4,2015,"n/a"),
+ (4,8,"Chevrolet","Malibu","Red","NJ-8301",5,2021,"n/a"),
+ (5,9,"Toyota","Camry","Blue","NJ-6823",4,2024,"n/a"),
+ (6,14,"Honda","Civic","White","NJ-7519",5,2022,"n/a"),
+ (100, 1, "Tesla", "Model 3", "Midnight Silver", "PA-ABDUL3", 5, 2024, "Always has lo-fi playing."),
+ (101, 1, "Subaru", "Outback", "Forest Green", "PA-ABDULSUB", 5, 2023, "Perfect for Costco and grocery runs."),
+ (102, 1001, "Honda", "Civic", "Blue", "PA-DRV222", 4, 2018, "Has a Drexel dragon sticker on the back."),
+ (103, 1004, "Toyota", "RAV4", "White", "PA-RAV404", 5, 2022, "Trunk is full of CS textbooks."),
+]
+
+# Hardcoded Preexisting Locations
+locations_existing = [
+ (1,"Drexel Main Building","3141 Chestnut St"),
+ (2,"Korman Center","3220-26 Woodland Walk"),
+ (3,"University Crossings","3175 JFK Blvd"),
+ (4,"30th Street Station","2955 Market St"),
+ (5,"Queen Lane Campus","2900 Queen Ln"),
+ (6,"Vidas Athletic Complex","43rd & Powelton"),
+ (7,"Cira Green","129 S 30th St"),
+ (8,"Wawa 34th Market","3400 Market St"),
+ (9, "PISB - Papadakis Integrated Sciences Building", "3245 Chestnut St"),
+ (10, "Drexel Recreation Center", "3301 Market St"),
+ (11, "Liberty Place", "1625 Chestnut St"),
+]
+
+# Hardcoded Preexisting Message Threads
+threads_existing = [(1, 1, 2, None, "2025-11-30 19:25:00")]
+
+# Hardcoded Preexisting Messages
+messages_existing = [
+ (1, 1, 2, "Hey, are we still on for 5:30 PM?", "2025-11-30 03:03:42"),
+ (2, 1, 1, "Yes, I will be there in 10 minutes.", "2025-11-30 03:03:42"),
+ (3, 1, 2, "Hey Quincy, are we still on for 5:30 PM?", "2025-11-30 19:29:48"),
+ (4, 1, 1, "Yes absolutely! Leaving now.", "2025-11-30 19:29:48"),
+ (5, 1, 2, "Perfect, see you soon!", "2025-11-30 19:29:48"),
+ (6, 1, 1, "Hey Abdul, do you still need a ride tomorrow?", "2025-11-30 19:29:51"),
+ (7, 1, 2, "Yes please! Around 9 AM if possible.", "2025-11-30 19:29:51"),
+ (8, 1, 1, "I ll be there.", "2025-11-30 19:29:51"),
+ (9, 1, 2, "Got you!", "2025-11-30 19:29:51"),
+ (10, 1, 1, "Thanks for the ride earlier!", "2025-11-30 19:29:55"),
+ (11, 1, 2, "Anytime man!", "2025-11-30 19:29:55"),
+ (12, 1, 1, "Left you a 5-star rating too :)", "2025-11-30 19:29:55"),
+ (13, 1, 2, "Hey bro, heading out soon?", "2025-11-30 19:32:38"),
+ (14, 1, 1, "Yeah give me 5 minutes.", "2025-11-30 19:32:38"),
+ (15, 1, 2, "Bet, take your time.", "2025-11-30 19:32:38"),
+ (16, 1, 1, "Traffic is crazy today.", "2025-11-30 19:32:38"),
+ (17, 1, 2, "Fr bro, Drexel roads are wild.", "2025-11-30 19:32:38"),
+ (18, 1, 1, "Want to grab food after?", "2025-11-30 19:32:38"),
+ (19, 1, 2, "m down for halal cart.", "2025-11-30 19:32:38"),
+ (20, 1, 1, "Say less.", "2025-11-30 19:32:38"),
+ (21, 1, 2, "Almost downstairs.", "2025-11-30 19:32:38"),
+ (22, 1, 1, "I see you.", "2025-11-30 19:32:38"),
+ (23, 1, 2, "Don t forget the aux today.", "2025-11-30 19:32:38"),
+ (24, 1, 1, "Haha alright.", "2025-11-30 19:32:38"),
+ (25, 1, 2, "Let s go.", "2025-11-30 19:32:38"),
+ (26, 1, 1, "On my way now.", "2025-11-30 19:32:38"),
+ (27, 1, 2, "You driving today?", "2025-11-30 19:32:38"),
+ (28, 1, 1, "Yeah I got it.", "2025-11-30 19:32:38"),
+ (29, 1, 2, "Fire.", "2025-11-30 19:32:38"),
+ (30, 1, 1, "Good morning! Still need a ride?", "2025-11-30 19:32:38"),
+ (31, 1, 2, "Yes please! Appreciate you.", "2025-11-30 19:32:38"),
+ (32, 1, 1, "Leaving in 10.", "2025-11-30 19:32:38"),
+ (33, 1, 2, "ll meet you outside.", "2025-11-30 19:32:38"),
+ (34, 1, 1, "Perfect.", "2025-11-30 19:32:38"),
+ (35, 1, 2, "Did you do the homework for CS 281?", "2025-11-30 19:32:38"),
+ (36, 1, 1, "Barely, that class is wild.", "2025-11-30 19:32:38"),
+ (37, 1, 2, "Facts bro.", "2025-11-30 19:32:38"),
+ (38, 1, 1, "ll quiz you in the car lol.", "2025-11-30 19:32:38"),
+ (39, 1, 2, "Bet.", "2025-11-30 19:32:38"),
+ (40, 1, 1, "m by the curb.", "2025-11-30 19:32:38"),
+ (41, 1, 2, "Coming now.", "2025-11-30 19:32:38"),
+ (42, 1, 1, "You want coffee?", "2025-11-30 19:32:38"),
+ (43, 1, 2, "Nah I m good, thanks.", "2025-11-30 19:32:38"),
+ (44, 1, 1, "Alright see you.", "2025-11-30 19:32:38"),
+ (45, 1, 2, "Almost there.", "2025-11-30 19:32:38"),
+ (46, 1, 1, "Thanks again for covering me last ride bro.", "2025-11-30 19:32:38"),
+ (47, 1, 2, "No problem at all.", "2025-11-30 19:32:38"),
+ (48, 1, 1, "You good for later today?", "2025-11-30 19:32:38"),
+ (49, 1, 2, "Yep, same time as usual.", "2025-11-30 19:32:38"),
+ (50, 1, 1, "Bet.", "2025-11-30 19:32:38"),
+ (51, 1, 2, "You wanna stop at Wawa after?", "2025-11-30 19:32:38"),
+ (52, 1, 1, "Always bro.", "2025-11-30 19:32:38"),
+ (53, 1, 2, "Haha say less.", "2025-11-30 19:32:38"),
+ (54, 1, 1, "You see the game last night?", "2025-11-30 19:32:38"),
+ (55, 1, 2, "Bro Embiid is insane.", "2025-11-30 19:32:38"),
+ (56, 1, 1, "MVP season.", "2025-11-30 19:32:38"),
+ (57, 1, 2, "100%.", "2025-11-30 19:32:38"),
+ (58, 1, 1, "Heading out now.", "2025-11-30 19:32:38"),
+ (59, 1, 2, "m already outside.", "2025-11-30 19:32:38"),
+ (60, 1, 1, "Coming.", "2025-11-30 19:32:38"),
+]
+
+# Hardcoded Preexisting Ride Offers
+ride_offers_existing = [
+ (1,5,3,3,8,"2025-10-16T01:45:00",2,4.17,0.72,"closed","2025-10-15T01:45:00"),
+ (2,1,6,6,10,"2025-10-15T18:45:00",1,5.31,0.86,"closed","2025-10-14T18:45:00"),
+ (3,12,1,2,3,"2025-10-14T13:00:00",2,4.63,1.13,"open","2025-10-13T13:00:00"),
+ (4,6,2,3,8,"2025-10-14T17:45:00",3,8.52,0.9,"closed","2025-10-13T17:45:00"),
+]
+
+# Hardcoded Preexisting Ride Requests
+ride_requests_existing = [
+ (1,11,7,10,"2025-10-14T19:05:00","2025-10-14T19:45:00",1,"cancelled","2025-10-13T19:15:00"),
+ (2,10,6,8,"2025-10-14T13:15:00","2025-10-14T13:25:00",1,"open","2025-10-13T13:15:00"),
+ (3,7,1,9,"2025-10-14T07:20:00","2025-10-14T07:40:00",1,"matched","2025-10-13T07:30:00"),
+ (4,13,6,7,"2025-10-13T23:50:00","2025-10-14T00:30:00",2,"matched","2025-10-13T00:00:00"),
+ (5,10,7,2,"2025-10-15T21:45:00","2025-10-15T21:55:00",1,"open","2025-10-14T21:45:00"),
+ (6,11,3,2,"2025-10-15T03:00:00","2025-10-15T03:10:00",1,"cancelled","2025-10-14T03:00:00"),
+]
+
+# Hardcoded Preexisting Ride Matches
+matches_existing = [
+ (1,3,3,1,11.71,"completed","2025-10-13T13:30:00"),
+]
+
+# Hardcoded Preexisting Ratings
+ratings_existing = [
+ (1,1,2,12,5,"Friendly","2025-10-13T15:30:00"),
+]
+
+# --- RANDOM MOCK DATA GENERATION LOGIC ---
+EXTRA = 100
+start_user_id = 6
+start_location_id = 12
+start_offer_id = max(o[0] for o in ride_offers_existing) + 1
+start_request_id = max(r[0] for r in ride_requests_existing) + 1
+start_match_id = max(m[0] for m in matches_existing) + 1
+start_rating_id = max(r[0] for r in ratings_existing) + 1
+
+first_names = ["Alex","Jordan","Taylor","Morgan","Cameron","Riley","Casey","Jamie","Avery","Sam","Sydney","Charlie","Drew","Logan","Peyton","Harper","Blake","Quinn","Rowan","Elliot","Maria","Jamal","Priya","Diego","Lina","Marcus"]
+last_names = ["Smith","Johnson","Williams","Brown","Jones","Miller","Davis","Garcia","Rodriguez","Wilson","Martinez","Anderson","Taylor","Thomas","Hernandez","Moore","Martin","Jackson","Thompson","White"]
+car_makes_models = [("Toyota","Camry"),("Honda","Civic"),("Tesla","Model 3"),("Tesla","Model Y"),("Chevrolet","Malibu"),("Hyundai","Elantra"),("Ford","Focus"),("Nissan","Altima")]
+colors = ["Blue","Gray","Silver","Red","Black","White","Green","Gold","Maroon"]
+roles = ["rider","driver","both"]
+
+def rand_datetime_oct13_to_nov30():
+ base = datetime.datetime(2025,10,13)
+ delta = datetime.timedelta(days=random.randint(0,48), hours=random.randint(0,23), minutes=random.randint(0,59))
+ return (base + delta).isoformat(timespec="seconds")
+
+# Users
+users_generated = []
+for i in range(EXTRA):
+ uid = start_user_id + i
+ fn = random.choice(first_names)
+ ln = random.choice(last_names)
+ username = f"{fn.lower()}.{ln.lower()}{uid}"
+ email = f"{fn.lower()}.{ln.lower()}{uid}@drexel.edu"
+ phone = f"215{random.randint(2000000,9999999)}"
+ role = random.choices(roles, weights=[0.45,0.40,0.15], k=1)[0]
+ rating_avg = round(max(2.5, min(5.0, random.gauss(4.4,0.4))),2)
+ created_at = (datetime.datetime(2025,10,1) + datetime.timedelta(days=random.randint(0,60))).isoformat(timespec="seconds")
+ users_generated.append((uid, username, email, phone, role, rating_avg, created_at))
+users_all = users_existing + users_generated
+
+# Locations
+locations_generated = []
+for i in range(EXTRA):
+ lid = start_location_id + i
+ name = f"Philly Point {lid}"
+ address = f"{random.randint(100,3999)} Market St"
+ locations_generated.append((lid, name, address))
+locations_all = locations_existing + locations_generated
+
+# Vehicles
+driver_candidates = [u for u in users_all if u[4] in ("driver","both")]
+if len(driver_candidates) < 40:
+ for j in range(30):
+ idx = j % len(users_generated)
+ u = users_generated[idx]
+ users_generated[idx] = (u[0], u[1], u[2], u[3], "driver", u[5], u[6])
+ users_all = users_existing + users_generated
+ driver_candidates = [u for u in users_all if u[4] in ("driver","both")]
+
+used_vids = set(v[0] for v in vehicles_existing)
+vehicles_generated = []
+vid_counter = 7
+generated_count = 0
+while generated_count < EXTRA:
+ if vid_counter not in used_vids:
+ owner = random.choice(driver_candidates)[0]
+ make, model = random.choice(car_makes_models)
+ color = random.choice(colors)
+ plate = f"NJ-{1000 + vid_counter:04d}"
+ seats = random.choice([4,4,5,5,6])
+ year = random.randint(2010,2025)
+ fun_fact = random.choice(["n/a","student vehicle","rideshare-ready","garage kept"])
+ vehicles_generated.append((vid_counter, owner, make, model, color, plate, seats, year, fun_fact))
+ generated_count += 1
+ vid_counter += 1
+vehicles_all = vehicles_existing + vehicles_generated
+
+# Offers
+offers_generated = []
+for i in range(EXTRA):
+ oid = start_offer_id + i
+ driver = random.choice(driver_candidates)[0]
+ owned = [v for v in vehicles_all if v[1] == driver]
+ vehicle_id = random.choice(owned)[0] if owned else random.choice(vehicles_all)[0]
+ origin = random.choice(locations_all)[0]
+ dest = random.choice(locations_all)[0]
+ if origin == dest:
+ dest = (dest % (start_location_id + EXTRA - 1)) + 1
+ depart_at = rand_datetime_oct13_to_nov30()
+ seats_available = random.choice([1,1,2,2,3,4])
+ price_base = round(random.uniform(3.0,9.0),2)
+ price_per_mile = round(random.uniform(0.5,1.6),2)
+ status = random.choices(["open","closed"], weights=[0.6,0.4])[0]
+ created_at = (datetime.datetime.fromisoformat(depart_at) - datetime.timedelta(days=random.randint(0,7))).isoformat(timespec="seconds")
+ offers_generated.append((oid, driver, vehicle_id, origin, dest, depart_at, seats_available, price_base, price_per_mile, status, created_at))
+offers_all = ride_offers_existing + offers_generated
+
+# Requests
+riders = [u for u in users_all if u[4] in ("rider","both")]
+requests_generated = []
+for i in range(EXTRA):
+ rid = start_request_id + i
+ rider = random.choice(riders)[0]
+ pickup = random.choice(locations_all)[0]
+ dropoff = random.choice(locations_all)[0]
+ if pickup == dropoff:
+ dropoff = (dropoff % (start_location_id + EXTRA - 1)) + 1
+ earliest_dt = datetime.datetime(2025,10,13) + datetime.timedelta(days=random.randint(0,48), hours=random.randint(6,20), minutes=random.randint(0,59))
+ latest_dt = earliest_dt + datetime.timedelta(minutes=random.randint(10,60))
+ seats_needed = random.choice([1,1,2])
+ status = random.choices(["open","matched","cancelled"], weights=[0.55,0.35,0.10])[0]
+ created_at = (earliest_dt - datetime.timedelta(days=random.randint(0,5))).isoformat(timespec="seconds")
+ requests_generated.append((rid, rider, pickup, dropoff, earliest_dt.isoformat(timespec="seconds"), latest_dt.isoformat(timespec="seconds"), seats_needed, status, created_at))
+requests_all = ride_requests_existing + requests_generated
+
+# Matches
+matches_generated = []
+match_id = start_match_id
+for req in requests_generated:
+ if random.random() < 0.6:
+ candidates = [o for o in offers_all if o[6] >= req[6]]
+ if not candidates:
+ continue
+ offer = random.choice(candidates)
+ seats_booked = min(offer[6], req[6])
+ miles = random.uniform(2.0,12.0)
+ price_total = round(offer[7] + offer[8]*miles, 2) if isinstance(offer[7], float) else round(offer[7] + offer[8]*miles,2)
+ state = random.choices(["confirmed","completed","cancelled","no_show"], weights=[0.4,0.35,0.15,0.10])[0]
+ matched_at = (datetime.datetime.fromisoformat(req[4]) - datetime.timedelta(hours=random.randint(0,4))).isoformat(timespec="seconds")
+ matches_generated.append((match_id, req[0], offer[0], seats_booked, price_total, state, matched_at))
+ match_id += 1
+matches_all = matches_existing + matches_generated
+
+# Ratings
+ratings_generated = []
+rating_id = start_rating_id
+for m in matches_generated:
+ if m[5] == "completed" and random.random() < 0.85:
+ req = next((r for r in requests_all if r[0] == m[1]), None)
+ offer = next((o for o in offers_all if o[0] == m[2]), None)
+ if not req or not offer:
+ continue
+ from_user = req[1]
+ to_user = offer[1]
+ if from_user == to_user:
+ continue
+ stars = random.choices([5,4,3,2,1], weights=[0.6,0.25,0.08,0.04,0.03])[0]
+ comment = random.choice(["Great ride","Friendly driver","On time","Would ride again","Car was clean","Driver was late","Helpful with bags"])
+ created_at = (datetime.datetime.fromisoformat(m[6]) + datetime.timedelta(hours=random.randint(1,48))).isoformat(timespec="seconds")
+ ratings_generated.append((rating_id, m[0], from_user, to_user, stars, comment, created_at))
+ rating_id += 1
+
+cleaned_ratings = []
+seen_pairs = set()
+for r in ratings_generated:
+ rid, match_id, from_user, to_user, stars, comment, created_at = r
+ if from_user == to_user:
+ continue
+ pair_key = (from_user, to_user)
+ if pair_key in seen_pairs:
+ continue
+ seen_pairs.add(pair_key)
+ cleaned_ratings.append(r)
+ratings_generated = cleaned_ratings
+ratings_all = ratings_existing + ratings_generated
+
+
+# --- 2. WRITING FILES ---
+def sql_quote(val):
+ if val is None:
+ return "NULL"
+ if isinstance(val, str):
+ return "'" + val.replace("'", "''") + "'"
+ return str(val)
+
+# Write schema.sql
+with open(SCHEMA_PATH, 'w', encoding='utf-8') as f:
+ f.write(SCHEMA_SQL)
+print(f"Created schema file: {SCHEMA_PATH}")
+
+# Write populate.sql
+with open(POPULATE_PATH, 'w', encoding='utf-8') as f:
+ f.write("-- USERS\n")
+ for u in users_all:
+ f.write("INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (%s,%s,%s,%s,%s,%s,%s,%s);\n" % (
+ sql_quote(u[0]), sql_quote(u[1]), sql_quote(u[2]), "'x'", sql_quote(u[3]), sql_quote(u[4]), sql_quote(u[5]), sql_quote(u[6])
+ ))
+ f.write("\n-- VEHICLE\n")
+ for v in vehicles_all:
+ f.write("INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s);\n" % tuple(sql_quote(x) for x in v))
+ f.write("\n-- LOCATION\n")
+ for loc in locations_all:
+ f.write("INSERT INTO LOCATION (location_id,name,address) VALUES (%s,%s,%s);\n" % (sql_quote(loc[0]), sql_quote(loc[1]), sql_quote(loc[2])))
+ f.write("\n-- RIDE_OFFER\n")
+ for o in offers_all:
+ f.write("INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s);\n" % (
+ sql_quote(o[0]), sql_quote(o[1]), sql_quote(o[2]), sql_quote(o[3]), sql_quote(o[4]), sql_quote(o[5]), sql_quote(o[6]), sql_quote(o[7]), sql_quote(o[8]), sql_quote(o[9]), sql_quote(o[10])
+ ))
+ f.write("\n-- RIDE_REQUEST\n")
+ for r in requests_all:
+ f.write("INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s);\n" % (
+ sql_quote(r[0]), sql_quote(r[1]), sql_quote(r[2]), sql_quote(r[3]), sql_quote(r[4]), sql_quote(r[5]), sql_quote(r[6]), sql_quote(r[7]), sql_quote(r[8])
+ ))
+ f.write("\n-- RIDE_MATCH\n")
+ for m in matches_all:
+ f.write("INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (%s,%s,%s,%s,%s,%s,%s);\n" % (
+ sql_quote(m[0]), sql_quote(m[1]), sql_quote(m[2]), sql_quote(m[3]), sql_quote(m[4]), sql_quote(m[5]), sql_quote(m[6])
+ ))
+ f.write("\n-- RATING\n")
+ for rt in ratings_all:
+ f.write("INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (%s,%s,%s,%s,%s,%s,%s);\n" % (
+ sql_quote(rt[0]), sql_quote(rt[1]), sql_quote(rt[2]), sql_quote(rt[3]), sql_quote(rt[4]), sql_quote(rt[5]), sql_quote(rt[6])
+ ))
+ f.write("\n-- MESSAGE_THREAD\n")
+ for th in threads_existing:
+ f.write("INSERT INTO MESSAGE_THREAD (thread_id,user1_id,user2_id,ride_match_id,created_at) VALUES (%s,%s,%s,%s,%s);\n" % (
+ sql_quote(th[0]), sql_quote(th[1]), sql_quote(th[2]), sql_quote(th[3]), sql_quote(th[4])
+ ))
+ f.write("\n-- MESSAGE\n")
+ for msg in messages_existing:
+ f.write("INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (%s,%s,%s,%s,%s);\n" % (
+ sql_quote(msg[0]), sql_quote(msg[1]), sql_quote(msg[2]), sql_quote(msg[3]), sql_quote(msg[4])
+ ))
+print(f"Created data file: {POPULATE_PATH}")
+
+
+# --- 3. BUILD DATABASE ---
+if os.path.exists(CURRENT_DB_PATH):
+ os.remove(CURRENT_DB_PATH)
+ print(f"Removed existing {CURRENT_DB_PATH} to start fresh.")
+
+print(f"Creating new {CURRENT_DB_PATH}...")
+conn = sqlite3.connect(CURRENT_DB_PATH)
+
+# Read and execute schema.sql against the database
+with open(SCHEMA_PATH, 'r', encoding='utf-8') as f:
+ schema_script = f.read()
+ conn.executescript(schema_script)
+ print("Executed schema.sql successfully.")
+
+# Read and execute populate.sql against the database
+with open(POPULATE_PATH, 'r', encoding='utf-8') as f:
+ populate_script = f.read()
+ conn.executescript(populate_script)
+ print("Executed populate.sql successfully.")
+
+conn.commit()
+conn.close()
+print("Done! Database initialized and populated with mock data.")
+
+# --- 4. COPY TO ASSETS ---
+print(f"Copying database to {ASSET_DB_PATH}...")
+shutil.copy2(CURRENT_DB_PATH, ASSET_DB_PATH)
+print("Database copied successfully.")
\ No newline at end of file
diff --git a/database/populate.sql b/database/populate.sql
index 8e1efa7..4139c13 100644
--- a/database/populate.sql
+++ b/database/populate.sql
@@ -1,109 +1,114 @@
-- USERS
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (1,'abdul.bookwala1@example.edu','abdul_bookwala','x','8569861234','driver',4.72,'2025-10-02T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (2,'quincy.lu2@example.edu','quincy_lu','x','8569862334','driver',4.67,'2025-10-09T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (3,'ame.shabuse3@example.edu','ame_shabuse','x','8569861334','rider',4.55,'2025-10-02T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (4,'ame.lu4@example.edu','ame_lu','x','8569831234','both',4.54,'2025-10-10T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (5,'kennan.shajid5@example.edu','kennan_shajid','x','8569861234','both',4.7,'2025-10-01T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (6,'maria.brown6@drexel.edu','maria.brown6','x','2152209805','driver',4.41,'2025-10-07T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (7,'jamal.jackson7@drexel.edu','jamal.jackson7','x','2152729295','driver',4.62,'2025-10-03T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (8,'alex.williams8@drexel.edu','alex.williams8','x','2153834068','rider',3.99,'2025-11-15T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (9,'maria.jackson9@drexel.edu','maria.jackson9','x','2155519187','rider',4.09,'2025-11-07T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (10,'avery.smith10@drexel.edu','avery.smith10','x','2158365337','driver',4.28,'2025-10-10T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (11,'casey.martinez11@drexel.edu','casey.martinez11','x','2152857401','rider',4.05,'2025-10-07T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (12,'charlie.anderson12@drexel.edu','charlie.anderson12','x','2157064421','rider',4.83,'2025-10-08T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (13,'drew.williams13@drexel.edu','drew.williams13','x','2156630852','rider',4.52,'2025-11-10T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (14,'elliot.anderson14@drexel.edu','elliot.anderson14','x','2156843180','rider',4.93,'2025-11-19T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (15,'sam.williams15@drexel.edu','sam.williams15','x','2159174925','rider',4.65,'2025-10-07T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (16,'drew.rodriguez16@drexel.edu','drew.rodriguez16','x','2155803481','driver',4.15,'2025-10-14T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (17,'jamal.rodriguez17@drexel.edu','jamal.rodriguez17','x','2157887295','both',4.69,'2025-11-11T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (18,'taylor.white18@drexel.edu','taylor.white18','x','2157326583','rider',4.37,'2025-10-25T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (19,'avery.jackson19@drexel.edu','avery.jackson19','x','2153842265','driver',4.16,'2025-11-23T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (20,'lina.johnson20@drexel.edu','lina.johnson20','x','2153921394','driver',4.54,'2025-10-05T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (21,'casey.thompson21@drexel.edu','casey.thompson21','x','2159351504','driver',4.02,'2025-10-14T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (22,'maria.moore22@drexel.edu','maria.moore22','x','2155318800','both',4.26,'2025-10-09T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (23,'jamie.jackson23@drexel.edu','jamie.jackson23','x','2156521269','rider',4.23,'2025-11-07T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (24,'logan.thompson24@drexel.edu','logan.thompson24','x','2155350414','rider',4.62,'2025-11-01T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (25,'taylor.johnson25@drexel.edu','taylor.johnson25','x','2159223454','rider',4.4,'2025-11-10T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (26,'riley.thomas26@drexel.edu','riley.thomas26','x','2157003041','rider',3.42,'2025-11-03T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (27,'avery.jackson27@drexel.edu','avery.jackson27','x','2159220743','both',5.0,'2025-11-13T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (28,'diego.brown28@drexel.edu','diego.brown28','x','2157718601','both',4.4,'2025-10-22T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (29,'morgan.wilson29@drexel.edu','morgan.wilson29','x','2155647075','rider',3.72,'2025-10-01T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (30,'diego.rodriguez30@drexel.edu','diego.rodriguez30','x','2156199220','driver',4.21,'2025-11-10T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (31,'sam.martin31@drexel.edu','sam.martin31','x','2157108412','rider',4.39,'2025-10-24T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (32,'lina.miller32@drexel.edu','lina.miller32','x','2156524639','both',5.0,'2025-11-08T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (33,'sydney.moore33@drexel.edu','sydney.moore33','x','2152163382','rider',3.98,'2025-10-24T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (34,'marcus.wilson34@drexel.edu','marcus.wilson34','x','2154008671','rider',5.0,'2025-10-06T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (35,'diego.moore35@drexel.edu','diego.moore35','x','2158845299','rider',3.73,'2025-11-18T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (36,'quinn.jones36@drexel.edu','quinn.jones36','x','2153077025','driver',4.63,'2025-11-03T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (37,'elliot.thomas37@drexel.edu','elliot.thomas37','x','2153776692','both',4.32,'2025-11-18T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (38,'diego.davis38@drexel.edu','diego.davis38','x','2157980649','rider',4.98,'2025-10-29T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (39,'blake.hernandez39@drexel.edu','blake.hernandez39','x','2153015056','rider',4.38,'2025-10-05T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (40,'sydney.smith40@drexel.edu','sydney.smith40','x','2156935091','driver',4.36,'2025-11-15T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (41,'maria.johnson41@drexel.edu','maria.johnson41','x','2153920502','rider',4.37,'2025-10-03T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (42,'sydney.williams42@drexel.edu','sydney.williams42','x','2156313054','rider',4.26,'2025-10-09T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (43,'diego.thompson43@drexel.edu','diego.thompson43','x','2156833613','driver',4.16,'2025-11-20T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (44,'harper.thomas44@drexel.edu','harper.thomas44','x','2153597274','rider',4.2,'2025-10-27T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (45,'peyton.johnson45@drexel.edu','peyton.johnson45','x','2157648591','driver',4.09,'2025-11-11T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (46,'morgan.johnson46@drexel.edu','morgan.johnson46','x','2155377432','driver',4.46,'2025-10-13T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (47,'casey.jackson47@drexel.edu','casey.jackson47','x','2155763243','rider',4.22,'2025-10-12T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (48,'avery.hernandez48@drexel.edu','avery.hernandez48','x','2154095528','both',5.0,'2025-11-24T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (49,'quinn.brown49@drexel.edu','quinn.brown49','x','2152424365','driver',4.73,'2025-11-04T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (50,'alex.williams50@drexel.edu','alex.williams50','x','2159770929','driver',4.43,'2025-10-31T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (51,'casey.taylor51@drexel.edu','casey.taylor51','x','2159570239','rider',4.81,'2025-10-25T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (52,'alex.taylor52@drexel.edu','alex.taylor52','x','2154224684','both',4.47,'2025-11-14T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (53,'diego.jackson53@drexel.edu','diego.jackson53','x','2157552178','driver',4.08,'2025-10-10T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (54,'casey.wilson54@drexel.edu','casey.wilson54','x','2153826207','both',3.96,'2025-11-17T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (55,'sydney.johnson55@drexel.edu','sydney.johnson55','x','2152420624','driver',4.16,'2025-11-02T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (56,'blake.miller56@drexel.edu','blake.miller56','x','2152477140','both',4.62,'2025-11-08T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (57,'taylor.garcia57@drexel.edu','taylor.garcia57','x','2155387114','rider',4.52,'2025-11-26T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (58,'rowan.garcia58@drexel.edu','rowan.garcia58','x','2156856325','driver',4.09,'2025-11-07T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (59,'rowan.martin59@drexel.edu','rowan.martin59','x','2154653902','both',4.12,'2025-10-14T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (60,'jamal.martinez60@drexel.edu','jamal.martinez60','x','2154002242','rider',4.79,'2025-10-30T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (61,'sydney.williams61@drexel.edu','sydney.williams61','x','2152078143','driver',4.82,'2025-11-06T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (62,'morgan.williams62@drexel.edu','morgan.williams62','x','2156510004','rider',4.31,'2025-11-26T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (63,'taylor.garcia63@drexel.edu','taylor.garcia63','x','2155099817','rider',5.0,'2025-10-29T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (64,'quinn.wilson64@drexel.edu','quinn.wilson64','x','2157131124','both',4.57,'2025-11-12T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (65,'quinn.wilson65@drexel.edu','quinn.wilson65','x','2159816136','driver',3.94,'2025-11-30T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (66,'cameron.rodriguez66@drexel.edu','cameron.rodriguez66','x','2152968285','both',4.39,'2025-10-19T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (67,'elliot.davis67@drexel.edu','elliot.davis67','x','2158019767','rider',4.17,'2025-11-13T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (68,'maria.rodriguez68@drexel.edu','maria.rodriguez68','x','2156239678','driver',5.0,'2025-10-06T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (69,'maria.thomas69@drexel.edu','maria.thomas69','x','2158957076','rider',3.97,'2025-10-01T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (70,'sydney.jones70@drexel.edu','sydney.jones70','x','2157344494','both',4.63,'2025-11-15T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (71,'logan.jackson71@drexel.edu','logan.jackson71','x','2152081115','rider',4.77,'2025-11-30T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (72,'priya.jones72@drexel.edu','priya.jones72','x','2156576743','rider',4.05,'2025-10-28T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (73,'cameron.johnson73@drexel.edu','cameron.johnson73','x','2154585858','rider',4.77,'2025-11-29T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (74,'marcus.johnson74@drexel.edu','marcus.johnson74','x','2159540726','rider',4.15,'2025-10-23T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (75,'lina.jackson75@drexel.edu','lina.jackson75','x','2159416659','both',3.86,'2025-11-09T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (76,'diego.jones76@drexel.edu','diego.jones76','x','2159765869','both',5.0,'2025-11-21T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (77,'riley.thomas77@drexel.edu','riley.thomas77','x','2152207923','rider',3.58,'2025-11-29T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (78,'sydney.thomas78@drexel.edu','sydney.thomas78','x','2158729636','driver',4.37,'2025-10-11T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (79,'marcus.brown79@drexel.edu','marcus.brown79','x','2155208992','both',4.1,'2025-11-24T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (80,'harper.garcia80@drexel.edu','harper.garcia80','x','2153674138','driver',4.07,'2025-11-20T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (81,'jamie.garcia81@drexel.edu','jamie.garcia81','x','2152198511','driver',4.48,'2025-10-26T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (82,'sydney.rodriguez82@drexel.edu','sydney.rodriguez82','x','2159250718','rider',4.45,'2025-11-02T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (83,'drew.jackson83@drexel.edu','drew.jackson83','x','2154777886','both',4.03,'2025-10-08T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (84,'avery.miller84@drexel.edu','avery.miller84','x','2156870339','both',4.38,'2025-10-28T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (85,'charlie.martinez85@drexel.edu','charlie.martinez85','x','2155660737','driver',4.59,'2025-11-02T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (86,'morgan.taylor86@drexel.edu','morgan.taylor86','x','2159545592','driver',4.38,'2025-10-01T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (87,'blake.jackson87@drexel.edu','blake.jackson87','x','2157761775','driver',5.0,'2025-11-17T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (88,'diego.davis88@drexel.edu','diego.davis88','x','2155055311','rider',5.0,'2025-11-09T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (89,'sydney.brown89@drexel.edu','sydney.brown89','x','2158037736','both',4.12,'2025-11-02T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (90,'sam.thomas90@drexel.edu','sam.thomas90','x','2154736220','rider',4.34,'2025-10-27T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (91,'jamal.taylor91@drexel.edu','jamal.taylor91','x','2157681754','driver',4.6,'2025-10-12T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (92,'elliot.thompson92@drexel.edu','elliot.thompson92','x','2154524550','rider',4.57,'2025-10-14T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (93,'logan.thompson93@drexel.edu','logan.thompson93','x','2157089046','driver',4.11,'2025-10-30T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (94,'peyton.hernandez94@drexel.edu','peyton.hernandez94','x','2157667572','rider',3.55,'2025-11-20T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (95,'diego.miller95@drexel.edu','diego.miller95','x','2157526840','rider',4.54,'2025-11-02T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (96,'jamal.white96@drexel.edu','jamal.white96','x','2154811762','rider',4.68,'2025-10-20T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (97,'jamie.davis97@drexel.edu','jamie.davis97','x','2153236094','rider',4.31,'2025-10-16T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (98,'harper.white98@drexel.edu','harper.white98','x','2159128116','driver',3.6,'2025-11-06T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (99,'casey.taylor99@drexel.edu','casey.taylor99','x','2156147136','rider',4.63,'2025-10-10T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (100,'maria.smith100@drexel.edu','maria.smith100','x','2159488173','driver',4.42,'2025-10-28T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (101,'jamie.miller101@drexel.edu','jamie.miller101','x','2158745362','both',4.21,'2025-11-03T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (102,'peyton.johnson102@drexel.edu','peyton.johnson102','x','2156675858','rider',4.66,'2025-11-21T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (103,'peyton.martin103@drexel.edu','peyton.martin103','x','2156688605','driver',4.04,'2025-11-30T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (104,'lina.hernandez104@drexel.edu','lina.hernandez104','x','2157139300','driver',4.73,'2025-11-28T00:00:00');
-INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (105,'quinn.hernandez105@drexel.edu','quinn.hernandez105','x','2159526327','rider',4.14,'2025-11-25T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (1,'abdul_bookwala','abdul.bookwala1@example.edu','x','8569861234','driver',4.72,'2025-10-02T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (2,'quincy_lu','quincy.lu2@example.edu','x','8569862334','driver',4.67,'2025-10-09T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (3,'ame_shabuse','ame.shabuse3@example.edu','x','8569861334','rider',4.55,'2025-10-02T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (4,'ame_lu','ame.lu4@example.edu','x','8569831234','both',4.54,'2025-10-10T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (5,'kennan_shajid','kennan.shajid5@example.edu','x','8569861234','both',4.7,'2025-10-01T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (1001,'driver.drexel','driver.drexel@drexel.edu','x','8560001001','driver',4.8,'2025-09-01 09:05:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (1002,'rider.drexel','rider.drexel@drexel.edu','x','8560001002','rider',3.5,'2025-09-02 10:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (1003,'both.guy','both.guy@drexel.edu','x','8560001003','both',4.2,'2025-09-03 11:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (1004,'chill.rider','chill.rider@drexel.edu','x','8560001004','rider',5.0,'2025-09-04 12:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (1005,'late.canceller','late.canceller@drexel.edu','x','8560001005','rider',1.9,'2025-09-05 13:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (6,'maria.brown6','maria.brown6@drexel.edu','x','2152209805','driver',4.41,'2025-10-07T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (7,'jamal.jackson7','jamal.jackson7@drexel.edu','x','2152729295','driver',4.62,'2025-10-03T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (8,'alex.williams8','alex.williams8@drexel.edu','x','2153834068','rider',3.99,'2025-11-15T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (9,'maria.jackson9','maria.jackson9@drexel.edu','x','2155519187','rider',4.09,'2025-11-07T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (10,'avery.smith10','avery.smith10@drexel.edu','x','2158365337','driver',4.28,'2025-10-10T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (11,'casey.martinez11','casey.martinez11@drexel.edu','x','2152857401','rider',4.05,'2025-10-07T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (12,'charlie.anderson12','charlie.anderson12@drexel.edu','x','2157064421','rider',4.83,'2025-10-08T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (13,'drew.williams13','drew.williams13@drexel.edu','x','2156630852','rider',4.52,'2025-11-10T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (14,'elliot.anderson14','elliot.anderson14@drexel.edu','x','2156843180','rider',4.93,'2025-11-19T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (15,'sam.williams15','sam.williams15@drexel.edu','x','2159174925','rider',4.65,'2025-10-07T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (16,'drew.rodriguez16','drew.rodriguez16@drexel.edu','x','2155803481','driver',4.15,'2025-10-14T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (17,'jamal.rodriguez17','jamal.rodriguez17@drexel.edu','x','2157887295','both',4.69,'2025-11-11T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (18,'taylor.white18','taylor.white18@drexel.edu','x','2157326583','rider',4.37,'2025-10-25T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (19,'avery.jackson19','avery.jackson19@drexel.edu','x','2153842265','driver',4.16,'2025-11-23T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (20,'lina.johnson20','lina.johnson20@drexel.edu','x','2153921394','driver',4.54,'2025-10-05T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (21,'casey.thompson21','casey.thompson21@drexel.edu','x','2159351504','driver',4.02,'2025-10-14T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (22,'maria.moore22','maria.moore22@drexel.edu','x','2155318800','both',4.26,'2025-10-09T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (23,'jamie.jackson23','jamie.jackson23@drexel.edu','x','2156521269','rider',4.23,'2025-11-07T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (24,'logan.thompson24','logan.thompson24@drexel.edu','x','2155350414','rider',4.62,'2025-11-01T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (25,'taylor.johnson25','taylor.johnson25@drexel.edu','x','2159223454','rider',4.4,'2025-11-10T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (26,'riley.thomas26','riley.thomas26@drexel.edu','x','2157003041','rider',3.42,'2025-11-03T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (27,'avery.jackson27','avery.jackson27@drexel.edu','x','2159220743','both',5.0,'2025-11-13T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (28,'diego.brown28','diego.brown28@drexel.edu','x','2157718601','both',4.4,'2025-10-22T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (29,'morgan.wilson29','morgan.wilson29@drexel.edu','x','2155647075','rider',3.72,'2025-10-01T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (30,'diego.rodriguez30','diego.rodriguez30@drexel.edu','x','2156199220','driver',4.21,'2025-11-10T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (31,'sam.martin31','sam.martin31@drexel.edu','x','2157108412','rider',4.39,'2025-10-24T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (32,'lina.miller32','lina.miller32@drexel.edu','x','2156524639','both',5.0,'2025-11-08T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (33,'sydney.moore33','sydney.moore33@drexel.edu','x','2152163382','rider',3.98,'2025-10-24T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (34,'marcus.wilson34','marcus.wilson34@drexel.edu','x','2154008671','rider',5.0,'2025-10-06T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (35,'diego.moore35','diego.moore35@drexel.edu','x','2158845299','rider',3.73,'2025-11-18T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (36,'quinn.jones36','quinn.jones36@drexel.edu','x','2153077025','driver',4.63,'2025-11-03T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (37,'elliot.thomas37','elliot.thomas37@drexel.edu','x','2153776692','both',4.32,'2025-11-18T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (38,'diego.davis38','diego.davis38@drexel.edu','x','2157980649','rider',4.98,'2025-10-29T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (39,'blake.hernandez39','blake.hernandez39@drexel.edu','x','2153015056','rider',4.38,'2025-10-05T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (40,'sydney.smith40','sydney.smith40@drexel.edu','x','2156935091','driver',4.36,'2025-11-15T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (41,'maria.johnson41','maria.johnson41@drexel.edu','x','2153920502','rider',4.37,'2025-10-03T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (42,'sydney.williams42','sydney.williams42@drexel.edu','x','2156313054','rider',4.26,'2025-10-09T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (43,'diego.thompson43','diego.thompson43@drexel.edu','x','2156833613','driver',4.16,'2025-11-20T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (44,'harper.thomas44','harper.thomas44@drexel.edu','x','2153597274','rider',4.2,'2025-10-27T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (45,'peyton.johnson45','peyton.johnson45@drexel.edu','x','2157648591','driver',4.09,'2025-11-11T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (46,'morgan.johnson46','morgan.johnson46@drexel.edu','x','2155377432','driver',4.46,'2025-10-13T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (47,'casey.jackson47','casey.jackson47@drexel.edu','x','2155763243','rider',4.22,'2025-10-12T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (48,'avery.hernandez48','avery.hernandez48@drexel.edu','x','2154095528','both',5.0,'2025-11-24T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (49,'quinn.brown49','quinn.brown49@drexel.edu','x','2152424365','driver',4.73,'2025-11-04T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (50,'alex.williams50','alex.williams50@drexel.edu','x','2159770929','driver',4.43,'2025-10-31T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (51,'casey.taylor51','casey.taylor51@drexel.edu','x','2159570239','rider',4.81,'2025-10-25T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (52,'alex.taylor52','alex.taylor52@drexel.edu','x','2154224684','both',4.47,'2025-11-14T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (53,'diego.jackson53','diego.jackson53@drexel.edu','x','2157552178','driver',4.08,'2025-10-10T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (54,'casey.wilson54','casey.wilson54@drexel.edu','x','2153826207','both',3.96,'2025-11-17T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (55,'sydney.johnson55','sydney.johnson55@drexel.edu','x','2152420624','driver',4.16,'2025-11-02T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (56,'blake.miller56','blake.miller56@drexel.edu','x','2152477140','both',4.62,'2025-11-08T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (57,'taylor.garcia57','taylor.garcia57@drexel.edu','x','2155387114','rider',4.52,'2025-11-26T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (58,'rowan.garcia58','rowan.garcia58@drexel.edu','x','2156856325','driver',4.09,'2025-11-07T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (59,'rowan.martin59','rowan.martin59@drexel.edu','x','2154653902','both',4.12,'2025-10-14T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (60,'jamal.martinez60','jamal.martinez60@drexel.edu','x','2154002242','rider',4.79,'2025-10-30T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (61,'sydney.williams61','sydney.williams61@drexel.edu','x','2152078143','driver',4.82,'2025-11-06T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (62,'morgan.williams62','morgan.williams62@drexel.edu','x','2156510004','rider',4.31,'2025-11-26T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (63,'taylor.garcia63','taylor.garcia63@drexel.edu','x','2155099817','rider',5.0,'2025-10-29T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (64,'quinn.wilson64','quinn.wilson64@drexel.edu','x','2157131124','both',4.57,'2025-11-12T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (65,'quinn.wilson65','quinn.wilson65@drexel.edu','x','2159816136','driver',3.94,'2025-11-30T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (66,'cameron.rodriguez66','cameron.rodriguez66@drexel.edu','x','2152968285','both',4.39,'2025-10-19T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (67,'elliot.davis67','elliot.davis67@drexel.edu','x','2158019767','rider',4.17,'2025-11-13T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (68,'maria.rodriguez68','maria.rodriguez68@drexel.edu','x','2156239678','driver',5.0,'2025-10-06T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (69,'maria.thomas69','maria.thomas69@drexel.edu','x','2158957076','rider',3.97,'2025-10-01T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (70,'sydney.jones70','sydney.jones70@drexel.edu','x','2157344494','both',4.63,'2025-11-15T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (71,'logan.jackson71','logan.jackson71@drexel.edu','x','2152081115','rider',4.77,'2025-11-30T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (72,'priya.jones72','priya.jones72@drexel.edu','x','2156576743','rider',4.05,'2025-10-28T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (73,'cameron.johnson73','cameron.johnson73@drexel.edu','x','2154585858','rider',4.77,'2025-11-29T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (74,'marcus.johnson74','marcus.johnson74@drexel.edu','x','2159540726','rider',4.15,'2025-10-23T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (75,'lina.jackson75','lina.jackson75@drexel.edu','x','2159416659','both',3.86,'2025-11-09T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (76,'diego.jones76','diego.jones76@drexel.edu','x','2159765869','both',5.0,'2025-11-21T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (77,'riley.thomas77','riley.thomas77@drexel.edu','x','2152207923','rider',3.58,'2025-11-29T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (78,'sydney.thomas78','sydney.thomas78@drexel.edu','x','2158729636','driver',4.37,'2025-10-11T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (79,'marcus.brown79','marcus.brown79@drexel.edu','x','2155208992','both',4.1,'2025-11-24T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (80,'harper.garcia80','harper.garcia80@drexel.edu','x','2153674138','driver',4.07,'2025-11-20T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (81,'jamie.garcia81','jamie.garcia81@drexel.edu','x','2152198511','driver',4.48,'2025-10-26T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (82,'sydney.rodriguez82','sydney.rodriguez82@drexel.edu','x','2159250718','rider',4.45,'2025-11-02T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (83,'drew.jackson83','drew.jackson83@drexel.edu','x','2154777886','both',4.03,'2025-10-08T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (84,'avery.miller84','avery.miller84@drexel.edu','x','2156870339','both',4.38,'2025-10-28T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (85,'charlie.martinez85','charlie.martinez85@drexel.edu','x','2155660737','driver',4.59,'2025-11-02T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (86,'morgan.taylor86','morgan.taylor86@drexel.edu','x','2159545592','driver',4.38,'2025-10-01T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (87,'blake.jackson87','blake.jackson87@drexel.edu','x','2157761775','driver',5.0,'2025-11-17T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (88,'diego.davis88','diego.davis88@drexel.edu','x','2155055311','rider',5.0,'2025-11-09T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (89,'sydney.brown89','sydney.brown89@drexel.edu','x','2158037736','both',4.12,'2025-11-02T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (90,'sam.thomas90','sam.thomas90@drexel.edu','x','2154736220','rider',4.34,'2025-10-27T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (91,'jamal.taylor91','jamal.taylor91@drexel.edu','x','2157681754','driver',4.6,'2025-10-12T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (92,'elliot.thompson92','elliot.thompson92@drexel.edu','x','2154524550','rider',4.57,'2025-10-14T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (93,'logan.thompson93','logan.thompson93@drexel.edu','x','2157089046','driver',4.11,'2025-10-30T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (94,'peyton.hernandez94','peyton.hernandez94@drexel.edu','x','2157667572','rider',3.55,'2025-11-20T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (95,'diego.miller95','diego.miller95@drexel.edu','x','2157526840','rider',4.54,'2025-11-02T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (96,'jamal.white96','jamal.white96@drexel.edu','x','2154811762','rider',4.68,'2025-10-20T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (97,'jamie.davis97','jamie.davis97@drexel.edu','x','2153236094','rider',4.31,'2025-10-16T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (98,'harper.white98','harper.white98@drexel.edu','x','2159128116','driver',3.6,'2025-11-06T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (99,'casey.taylor99','casey.taylor99@drexel.edu','x','2156147136','rider',4.63,'2025-10-10T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (100,'maria.smith100','maria.smith100@drexel.edu','x','2159488173','driver',4.42,'2025-10-28T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (101,'jamie.miller101','jamie.miller101@drexel.edu','x','2158745362','both',4.21,'2025-11-03T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (102,'peyton.johnson102','peyton.johnson102@drexel.edu','x','2156675858','rider',4.66,'2025-11-21T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (103,'peyton.martin103','peyton.martin103@drexel.edu','x','2156688605','driver',4.04,'2025-11-30T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (104,'lina.hernandez104','lina.hernandez104@drexel.edu','x','2157139300','driver',4.73,'2025-11-28T00:00:00');
+INSERT INTO USER (user_id,username,email,password_hash,phone_number,role,rating_avg,created_at) VALUES (105,'quinn.hernandez105','quinn.hernandez105@drexel.edu','x','2159526327','rider',4.14,'2025-11-25T00:00:00');
-- VEHICLE
INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (1,2,'Tesla','Model Y','Blue','NJ-7909',7,2025,'n/a');
@@ -112,106 +117,110 @@ INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total
INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (4,8,'Chevrolet','Malibu','Red','NJ-8301',5,2021,'n/a');
INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (5,9,'Toyota','Camry','Blue','NJ-6823',4,2024,'n/a');
INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (6,14,'Honda','Civic','White','NJ-7519',5,2022,'n/a');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (7,76,'Toyota','Camry','Gray','NJ-1007',5,2014,'garage kept');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (8,30,'Toyota','Camry','Black','NJ-1008',5,2020,'student vehicle');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (9,61,'Hyundai','Elantra','White','NJ-1009',5,2018,'garage kept');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (10,43,'Honda','Civic','Gold','NJ-1010',4,2011,'rideshare-ready');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (11,37,'Honda','Civic','Blue','NJ-1011',4,2017,'student vehicle');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (12,104,'Toyota','Camry','Silver','NJ-1012',4,2014,'garage kept');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (13,84,'Honda','Civic','Red','NJ-1013',5,2018,'rideshare-ready');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (14,28,'Honda','Civic','Silver','NJ-1014',5,2013,'n/a');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (15,48,'Ford','Focus','Green','NJ-1015',4,2012,'student vehicle');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (16,20,'Chevrolet','Malibu','Gray','NJ-1016',6,2011,'rideshare-ready');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (17,70,'Ford','Focus','White','NJ-1017',4,2020,'n/a');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (18,56,'Nissan','Altima','Gray','NJ-1018',5,2021,'garage kept');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (19,87,'Tesla','Model 3','Green','NJ-1019',4,2018,'garage kept');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (20,61,'Ford','Focus','Black','NJ-1020',5,2017,'n/a');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (21,45,'Nissan','Altima','Red','NJ-1021',5,2022,'rideshare-ready');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (22,7,'Nissan','Altima','White','NJ-1022',4,2025,'student vehicle');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (23,52,'Chevrolet','Malibu','White','NJ-1023',5,2018,'n/a');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (24,68,'Tesla','Model Y','Gray','NJ-1024',4,2023,'garage kept');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (25,75,'Tesla','Model Y','Gold','NJ-1025',5,2024,'n/a');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (26,19,'Chevrolet','Malibu','Red','NJ-1026',5,2017,'rideshare-ready');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (27,84,'Hyundai','Elantra','Gold','NJ-1027',6,2021,'garage kept');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (28,91,'Hyundai','Elantra','White','NJ-1028',5,2018,'rideshare-ready');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (29,43,'Tesla','Model Y','Gray','NJ-1029',4,2020,'n/a');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (30,91,'Tesla','Model 3','Red','NJ-1030',4,2025,'rideshare-ready');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (31,89,'Chevrolet','Malibu','Gray','NJ-1031',4,2019,'student vehicle');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (32,53,'Tesla','Model 3','Black','NJ-1032',4,2014,'rideshare-ready');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (33,10,'Toyota','Camry','Maroon','NJ-1033',5,2014,'garage kept');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (34,20,'Toyota','Camry','Black','NJ-1034',5,2025,'garage kept');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (35,50,'Tesla','Model 3','Blue','NJ-1035',5,2025,'n/a');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (36,103,'Honda','Civic','Green','NJ-1036',5,2012,'n/a');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (37,27,'Tesla','Model 3','Black','NJ-1037',4,2017,'n/a');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (38,75,'Ford','Focus','Red','NJ-1038',6,2022,'garage kept');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (39,59,'Chevrolet','Malibu','Green','NJ-1039',5,2011,'n/a');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (40,93,'Tesla','Model Y','Red','NJ-1040',5,2012,'student vehicle');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (41,40,'Tesla','Model 3','Maroon','NJ-1041',4,2015,'n/a');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (42,56,'Nissan','Altima','Gold','NJ-1042',5,2011,'student vehicle');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (43,46,'Chevrolet','Malibu','Gold','NJ-1043',4,2017,'rideshare-ready');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (44,100,'Tesla','Model Y','Green','NJ-1044',4,2017,'student vehicle');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (45,45,'Tesla','Model 3','Gray','NJ-1045',4,2015,'rideshare-ready');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (46,79,'Chevrolet','Malibu','Gold','NJ-1046',4,2024,'rideshare-ready');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (47,86,'Ford','Focus','Black','NJ-1047',6,2025,'garage kept');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (48,19,'Toyota','Camry','Green','NJ-1048',5,2018,'n/a');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (49,19,'Tesla','Model Y','Blue','NJ-1049',5,2011,'student vehicle');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (50,64,'Nissan','Altima','Black','NJ-1050',4,2023,'garage kept');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (51,19,'Nissan','Altima','White','NJ-1051',5,2020,'rideshare-ready');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (52,84,'Honda','Civic','Silver','NJ-1052',5,2023,'garage kept');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (53,46,'Ford','Focus','Maroon','NJ-1053',4,2024,'n/a');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (54,49,'Chevrolet','Malibu','White','NJ-1054',4,2022,'n/a');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (55,84,'Nissan','Altima','Green','NJ-1055',4,2016,'rideshare-ready');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (56,80,'Nissan','Altima','Gold','NJ-1056',4,2016,'rideshare-ready');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (57,75,'Tesla','Model 3','Black','NJ-1057',5,2025,'n/a');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (58,7,'Tesla','Model Y','Silver','NJ-1058',5,2010,'garage kept');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (59,19,'Tesla','Model Y','Gray','NJ-1059',5,2013,'student vehicle');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (60,65,'Chevrolet','Malibu','Maroon','NJ-1060',5,2023,'garage kept');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (61,64,'Tesla','Model Y','Gold','NJ-1061',6,2014,'garage kept');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (62,32,'Tesla','Model 3','Gray','NJ-1062',5,2023,'rideshare-ready');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (63,100,'Chevrolet','Malibu','Blue','NJ-1063',5,2019,'garage kept');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (64,27,'Nissan','Altima','Maroon','NJ-1064',5,2021,'rideshare-ready');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (65,75,'Ford','Focus','Gold','NJ-1065',5,2016,'student vehicle');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (66,76,'Ford','Focus','Red','NJ-1066',5,2011,'rideshare-ready');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (67,91,'Nissan','Altima','Green','NJ-1067',5,2014,'garage kept');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (68,10,'Tesla','Model 3','Maroon','NJ-1068',6,2020,'n/a');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (69,59,'Honda','Civic','Maroon','NJ-1069',5,2010,'student vehicle');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (70,56,'Tesla','Model 3','Gray','NJ-1070',5,2018,'rideshare-ready');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (71,80,'Ford','Focus','Gray','NJ-1071',5,2022,'rideshare-ready');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (72,81,'Nissan','Altima','Maroon','NJ-1072',4,2012,'student vehicle');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (73,81,'Chevrolet','Malibu','Red','NJ-1073',4,2023,'n/a');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (74,93,'Honda','Civic','Gold','NJ-1074',4,2019,'n/a');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (75,10,'Hyundai','Elantra','Blue','NJ-1075',5,2021,'rideshare-ready');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (76,58,'Tesla','Model 3','Red','NJ-1076',6,2023,'student vehicle');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (77,28,'Tesla','Model 3','Gray','NJ-1077',6,2022,'student vehicle');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (78,65,'Tesla','Model 3','Red','NJ-1078',5,2018,'garage kept');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (79,43,'Toyota','Camry','Gold','NJ-1079',5,2015,'n/a');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (80,59,'Hyundai','Elantra','Black','NJ-1080',5,2018,'garage kept');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (81,48,'Tesla','Model Y','Green','NJ-1081',5,2013,'student vehicle');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (82,54,'Hyundai','Elantra','Black','NJ-1082',5,2010,'garage kept');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (83,45,'Toyota','Camry','Blue','NJ-1083',6,2025,'rideshare-ready');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (84,98,'Tesla','Model Y','White','NJ-1084',4,2016,'rideshare-ready');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (85,85,'Tesla','Model 3','Gray','NJ-1085',4,2019,'garage kept');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (86,10,'Hyundai','Elantra','Silver','NJ-1086',4,2019,'rideshare-ready');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (87,91,'Ford','Focus','Silver','NJ-1087',4,2014,'rideshare-ready');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (88,68,'Chevrolet','Malibu','Silver','NJ-1088',5,2025,'rideshare-ready');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (89,91,'Hyundai','Elantra','Gray','NJ-1089',5,2012,'student vehicle');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (90,93,'Tesla','Model Y','Green','NJ-1090',6,2021,'n/a');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (91,100,'Ford','Focus','Blue','NJ-1091',5,2013,'garage kept');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (92,53,'Chevrolet','Malibu','Green','NJ-1092',5,2013,'student vehicle');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (93,64,'Toyota','Camry','Maroon','NJ-1093',5,2017,'n/a');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (94,81,'Nissan','Altima','Black','NJ-1094',5,2013,'student vehicle');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (95,10,'Toyota','Camry','Black','NJ-1095',5,2013,'n/a');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (96,40,'Tesla','Model 3','Green','NJ-1096',5,2021,'garage kept');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (97,78,'Tesla','Model 3','Green','NJ-1097',4,2025,'garage kept');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (98,45,'Toyota','Camry','White','NJ-1098',4,2024,'garage kept');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (99,40,'Hyundai','Elantra','Gray','NJ-1099',5,2021,'n/a');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (100,55,'Chevrolet','Malibu','Red','NJ-1100',4,2024,'n/a');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (101,84,'Tesla','Model Y','Blue','NJ-1101',4,2020,'student vehicle');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (102,22,'Tesla','Model Y','Gray','NJ-1102',6,2016,'student vehicle');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (103,103,'Tesla','Model Y','White','NJ-1103',4,2010,'rideshare-ready');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (104,27,'Tesla','Model 3','Maroon','NJ-1104',5,2015,'n/a');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (105,84,'Toyota','Camry','Silver','NJ-1105',4,2021,'student vehicle');
-INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (106,78,'Hyundai','Elantra','Blue','NJ-1106',4,2018,'n/a');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (100,1,'Tesla','Model 3','Midnight Silver','PA-ABDUL3',5,2024,'Always has lo-fi playing.');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (101,1,'Subaru','Outback','Forest Green','PA-ABDULSUB',5,2023,'Perfect for Costco and grocery runs.');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (102,1001,'Honda','Civic','Blue','PA-DRV222',4,2018,'Has a Drexel dragon sticker on the back.');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (103,1004,'Toyota','RAV4','White','PA-RAV404',5,2022,'Trunk is full of CS textbooks.');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (7,64,'Toyota','Camry','Gray','NJ-1007',5,2014,'garage kept');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (8,19,'Toyota','Camry','Black','NJ-1008',5,2020,'student vehicle');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (9,53,'Hyundai','Elantra','White','NJ-1009',5,2018,'garage kept');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (10,28,'Honda','Civic','Gold','NJ-1010',4,2011,'rideshare-ready');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (11,22,'Honda','Civic','Blue','NJ-1011',4,2017,'student vehicle');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (12,91,'Toyota','Camry','Silver','NJ-1012',4,2014,'garage kept');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (13,76,'Honda','Civic','Red','NJ-1013',5,2018,'rideshare-ready');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (14,17,'Honda','Civic','Silver','NJ-1014',5,2013,'n/a');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (15,104,'Chevrolet','Malibu','Green','NJ-1015',5,2016,'n/a');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (16,65,'Tesla','Model Y','Gray','NJ-1016',5,2013,'n/a');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (17,43,'Ford','Focus','White','NJ-1017',4,2020,'n/a');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (18,93,'Ford','Focus','Gold','NJ-1018',4,2023,'rideshare-ready');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (19,70,'Nissan','Altima','Silver','NJ-1019',5,2015,'rideshare-ready');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (20,68,'Nissan','Altima','Gold','NJ-1020',5,2018,'rideshare-ready');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (21,93,'Tesla','Model Y','Gray','NJ-1021',5,2024,'student vehicle');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (22,84,'Nissan','Altima','Green','NJ-1022',5,2010,'garage kept');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (23,93,'Hyundai','Elantra','Silver','NJ-1023',5,2016,'rideshare-ready');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (24,87,'Chevrolet','Malibu','White','NJ-1024',5,2018,'n/a');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (25,58,'Tesla','Model Y','Gray','NJ-1025',4,2023,'garage kept');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (26,61,'Tesla','Model Y','Gold','NJ-1026',5,2024,'n/a');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (27,1003,'Chevrolet','Malibu','Red','NJ-1027',5,2017,'rideshare-ready');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (28,76,'Hyundai','Elantra','Gold','NJ-1028',6,2021,'garage kept');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (29,83,'Hyundai','Elantra','White','NJ-1029',5,2018,'rideshare-ready');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (30,28,'Tesla','Model Y','Gray','NJ-1030',4,2020,'n/a');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (31,83,'Tesla','Model 3','Red','NJ-1031',4,2025,'rideshare-ready');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (32,81,'Chevrolet','Malibu','Gray','NJ-1032',4,2019,'student vehicle');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (33,45,'Tesla','Model 3','Black','NJ-1033',4,2014,'rideshare-ready');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (34,4,'Toyota','Camry','Maroon','NJ-1034',5,2014,'garage kept');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (35,6,'Toyota','Camry','Black','NJ-1035',5,2025,'garage kept');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (36,40,'Tesla','Model 3','Blue','NJ-1036',5,2025,'n/a');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (37,89,'Honda','Civic','Green','NJ-1037',5,2012,'n/a');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (38,16,'Tesla','Model 3','Black','NJ-1038',4,2017,'n/a');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (39,61,'Ford','Focus','Red','NJ-1039',6,2022,'garage kept');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (40,103,'Nissan','Altima','Black','NJ-1040',6,2023,'rideshare-ready');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (41,64,'Toyota','Camry','Gray','NJ-1041',4,2016,'rideshare-ready');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (42,76,'Honda','Civic','Silver','NJ-1042',4,2015,'n/a');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (43,17,'Toyota','Camry','Green','NJ-1043',5,2025,'rideshare-ready');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (44,4,'Tesla','Model Y','Black','NJ-1044',5,2024,'n/a');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (45,78,'Tesla','Model Y','Black','NJ-1045',6,2016,'garage kept');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (46,7,'Tesla','Model Y','Silver','NJ-1046',5,2014,'n/a');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (47,5,'Tesla','Model 3','Black','NJ-1047',6,2019,'garage kept');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (48,7,'Nissan','Altima','Black','NJ-1048',5,2018,'garage kept');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (49,52,'Honda','Civic','Blue','NJ-1049',5,2020,'rideshare-ready');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (50,2,'Honda','Civic','Red','NJ-1050',6,2010,'rideshare-ready');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (51,64,'Toyota','Camry','Silver','NJ-1051',5,2024,'rideshare-ready');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (52,19,'Ford','Focus','Gold','NJ-1052',4,2025,'rideshare-ready');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (53,49,'Hyundai','Elantra','White','NJ-1053',4,2015,'rideshare-ready');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (54,49,'Nissan','Altima','Black','NJ-1054',5,2011,'garage kept');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (55,1003,'Hyundai','Elantra','Black','NJ-1055',5,2013,'garage kept');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (56,98,'Toyota','Camry','Maroon','NJ-1056',5,2023,'n/a');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (57,20,'Hyundai','Elantra','Gold','NJ-1057',5,2011,'student vehicle');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (58,30,'Tesla','Model 3','Black','NJ-1058',5,2025,'n/a');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (59,2,'Tesla','Model Y','Silver','NJ-1059',5,2010,'garage kept');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (60,1003,'Tesla','Model Y','Gray','NJ-1060',5,2013,'student vehicle');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (61,55,'Chevrolet','Malibu','Maroon','NJ-1061',5,2023,'garage kept');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (62,54,'Tesla','Model Y','Gold','NJ-1062',6,2014,'garage kept');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (63,20,'Tesla','Model 3','Gray','NJ-1063',5,2023,'rideshare-ready');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (64,104,'Chevrolet','Malibu','Blue','NJ-1064',5,2019,'garage kept');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (65,98,'Tesla','Model 3','Gold','NJ-1065',6,2025,'rideshare-ready');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (66,40,'Ford','Focus','Gold','NJ-1066',5,2016,'student vehicle');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (67,64,'Ford','Focus','Red','NJ-1067',5,2011,'rideshare-ready');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (68,83,'Nissan','Altima','Green','NJ-1068',5,2014,'garage kept');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (69,4,'Tesla','Model 3','Maroon','NJ-1069',6,2020,'n/a');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (70,98,'Nissan','Altima','Gray','NJ-1070',6,2024,'n/a');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (71,81,'Tesla','Model 3','Green','NJ-1071',4,2012,'garage kept');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (72,86,'Chevrolet','Malibu','White','NJ-1072',6,2022,'n/a');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (73,93,'Hyundai','Elantra','Maroon','NJ-1073',5,2020,'garage kept');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (74,98,'Toyota','Camry','Gray','NJ-1074',4,2019,'student vehicle');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (75,83,'Honda','Civic','Green','NJ-1075',4,2013,'garage kept');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (76,17,'Chevrolet','Malibu','Blue','NJ-1076',4,2020,'n/a');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (77,32,'Hyundai','Elantra','White','NJ-1077',5,2014,'student vehicle');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (78,58,'Ford','Focus','Silver','NJ-1078',4,2015,'n/a');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (79,68,'Ford','Focus','Red','NJ-1079',5,2014,'student vehicle');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (80,53,'Chevrolet','Malibu','Gold','NJ-1080',5,2010,'garage kept');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (81,101,'Chevrolet','Malibu','Maroon','NJ-1081',4,2012,'garage kept');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (82,43,'Chevrolet','Malibu','Green','NJ-1082',5,2024,'rideshare-ready');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (83,20,'Ford','Focus','Gold','NJ-1083',4,2017,'garage kept');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (84,64,'Hyundai','Elantra','Black','NJ-1084',5,2010,'garage kept');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (85,30,'Toyota','Camry','Blue','NJ-1085',6,2025,'rideshare-ready');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (86,85,'Tesla','Model Y','White','NJ-1086',4,2016,'rideshare-ready');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (87,78,'Tesla','Model 3','Gray','NJ-1087',4,2019,'garage kept');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (88,4,'Hyundai','Elantra','Silver','NJ-1088',4,2019,'rideshare-ready');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (89,83,'Ford','Focus','Silver','NJ-1089',4,2014,'rideshare-ready');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (90,58,'Chevrolet','Malibu','Silver','NJ-1090',5,2025,'rideshare-ready');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (91,83,'Hyundai','Elantra','Gray','NJ-1091',5,2012,'student vehicle');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (92,84,'Tesla','Model Y','Green','NJ-1092',6,2021,'n/a');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (93,86,'Ford','Focus','Blue','NJ-1093',5,2013,'garage kept');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (94,45,'Chevrolet','Malibu','Green','NJ-1094',5,2013,'student vehicle');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (95,54,'Toyota','Camry','Maroon','NJ-1095',5,2017,'n/a');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (96,70,'Nissan','Altima','Black','NJ-1096',5,2013,'student vehicle');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (97,4,'Toyota','Camry','Black','NJ-1097',5,2013,'n/a');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (98,27,'Tesla','Model 3','Green','NJ-1098',5,2021,'garage kept');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (99,65,'Tesla','Model 3','Green','NJ-1099',4,2025,'garage kept');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (104,30,'Toyota','Camry','White','NJ-1104',4,2024,'garage kept');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (105,27,'Hyundai','Elantra','Gray','NJ-1105',5,2021,'n/a');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (106,48,'Chevrolet','Malibu','Red','NJ-1106',4,2024,'n/a');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (107,76,'Tesla','Model Y','Blue','NJ-1107',4,2020,'student vehicle');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (108,10,'Tesla','Model Y','Gray','NJ-1108',6,2016,'student vehicle');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (109,89,'Tesla','Model Y','White','NJ-1109',4,2010,'rideshare-ready');
+INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (110,93,'Tesla','Model 3','Silver','NJ-1110',6,2018,'student vehicle');
-- LOCATION
INSERT INTO LOCATION (location_id,name,address) VALUES (1,'Drexel Main Building','3141 Chestnut St');
@@ -222,212 +231,215 @@ INSERT INTO LOCATION (location_id,name,address) VALUES (5,'Queen Lane Campus','2
INSERT INTO LOCATION (location_id,name,address) VALUES (6,'Vidas Athletic Complex','43rd & Powelton');
INSERT INTO LOCATION (location_id,name,address) VALUES (7,'Cira Green','129 S 30th St');
INSERT INTO LOCATION (location_id,name,address) VALUES (8,'Wawa 34th Market','3400 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (9,'Philly Point 9','2044 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (10,'Philly Point 10','1943 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (11,'Philly Point 11','1161 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (12,'Philly Point 12','3179 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (13,'Philly Point 13','1112 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (14,'Philly Point 14','3540 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (15,'Philly Point 15','2711 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (16,'Philly Point 16','1235 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (17,'Philly Point 17','3236 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (18,'Philly Point 18','3285 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (19,'Philly Point 19','2235 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (20,'Philly Point 20','2084 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (21,'Philly Point 21','2667 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (22,'Philly Point 22','1079 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (23,'Philly Point 23','1224 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (24,'Philly Point 24','1901 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (25,'Philly Point 25','417 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (26,'Philly Point 26','3022 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (27,'Philly Point 27','1270 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (28,'Philly Point 28','1060 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (29,'Philly Point 29','1212 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (30,'Philly Point 30','1475 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (31,'Philly Point 31','1409 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (32,'Philly Point 32','3757 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (33,'Philly Point 33','2312 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (34,'Philly Point 34','430 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (35,'Philly Point 35','666 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (36,'Philly Point 36','717 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (37,'Philly Point 37','1047 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (38,'Philly Point 38','1668 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (39,'Philly Point 39','2942 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (40,'Philly Point 40','725 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (41,'Philly Point 41','2993 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (42,'Philly Point 42','976 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (43,'Philly Point 43','363 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (44,'Philly Point 44','1799 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (45,'Philly Point 45','1769 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (46,'Philly Point 46','1455 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (47,'Philly Point 47','2322 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (48,'Philly Point 48','2008 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (49,'Philly Point 49','1803 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (50,'Philly Point 50','355 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (51,'Philly Point 51','947 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (52,'Philly Point 52','3511 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (53,'Philly Point 53','1820 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (54,'Philly Point 54','1695 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (55,'Philly Point 55','3806 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (56,'Philly Point 56','3253 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (57,'Philly Point 57','2492 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (58,'Philly Point 58','3975 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (59,'Philly Point 59','2948 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (60,'Philly Point 60','180 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (61,'Philly Point 61','3609 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (62,'Philly Point 62','3707 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (63,'Philly Point 63','3235 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (64,'Philly Point 64','2458 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (65,'Philly Point 65','1658 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (66,'Philly Point 66','2053 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (67,'Philly Point 67','124 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (68,'Philly Point 68','3961 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (69,'Philly Point 69','1540 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (70,'Philly Point 70','1323 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (71,'Philly Point 71','3186 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (72,'Philly Point 72','1697 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (73,'Philly Point 73','3595 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (74,'Philly Point 74','3752 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (75,'Philly Point 75','3523 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (76,'Philly Point 76','1816 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (77,'Philly Point 77','2304 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (78,'Philly Point 78','3161 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (79,'Philly Point 79','3109 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (80,'Philly Point 80','2336 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (81,'Philly Point 81','3376 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (82,'Philly Point 82','2570 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (83,'Philly Point 83','3777 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (84,'Philly Point 84','1003 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (85,'Philly Point 85','2099 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (86,'Philly Point 86','998 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (87,'Philly Point 87','1217 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (88,'Philly Point 88','1885 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (89,'Philly Point 89','2089 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (90,'Philly Point 90','218 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (91,'Philly Point 91','1692 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (92,'Philly Point 92','1476 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (93,'Philly Point 93','2839 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (94,'Philly Point 94','2881 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (95,'Philly Point 95','3369 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (96,'Philly Point 96','1756 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (97,'Philly Point 97','3066 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (98,'Philly Point 98','776 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (99,'Philly Point 99','3542 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (100,'Philly Point 100','2014 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (101,'Philly Point 101','3866 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (102,'Philly Point 102','622 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (103,'Philly Point 103','2648 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (104,'Philly Point 104','2287 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (105,'Philly Point 105','210 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (106,'Philly Point 106','3814 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (107,'Philly Point 107','1713 Market St');
-INSERT INTO LOCATION (location_id,name,address) VALUES (108,'Philly Point 108','2524 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (9,'PISB - Papadakis Integrated Sciences Building','3245 Chestnut St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (10,'Drexel Recreation Center','3301 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (11,'Liberty Place','1625 Chestnut St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (12,'Philly Point 12','2044 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (13,'Philly Point 13','1943 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (14,'Philly Point 14','1161 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (15,'Philly Point 15','3179 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (16,'Philly Point 16','1112 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (17,'Philly Point 17','3540 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (18,'Philly Point 18','2711 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (19,'Philly Point 19','1235 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (20,'Philly Point 20','3236 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (21,'Philly Point 21','3285 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (22,'Philly Point 22','2235 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (23,'Philly Point 23','2084 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (24,'Philly Point 24','2667 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (25,'Philly Point 25','1079 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (26,'Philly Point 26','1224 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (27,'Philly Point 27','1901 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (28,'Philly Point 28','417 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (29,'Philly Point 29','3022 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (30,'Philly Point 30','1270 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (31,'Philly Point 31','1060 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (32,'Philly Point 32','1212 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (33,'Philly Point 33','1475 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (34,'Philly Point 34','1409 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (35,'Philly Point 35','3757 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (36,'Philly Point 36','2312 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (37,'Philly Point 37','430 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (38,'Philly Point 38','666 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (39,'Philly Point 39','717 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (40,'Philly Point 40','1047 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (41,'Philly Point 41','1668 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (42,'Philly Point 42','2942 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (43,'Philly Point 43','725 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (44,'Philly Point 44','2993 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (45,'Philly Point 45','976 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (46,'Philly Point 46','363 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (47,'Philly Point 47','1799 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (48,'Philly Point 48','1769 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (49,'Philly Point 49','1455 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (50,'Philly Point 50','2322 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (51,'Philly Point 51','2008 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (52,'Philly Point 52','1803 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (53,'Philly Point 53','355 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (54,'Philly Point 54','947 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (55,'Philly Point 55','3511 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (56,'Philly Point 56','1820 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (57,'Philly Point 57','1695 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (58,'Philly Point 58','3806 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (59,'Philly Point 59','3253 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (60,'Philly Point 60','2492 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (61,'Philly Point 61','3975 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (62,'Philly Point 62','2948 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (63,'Philly Point 63','180 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (64,'Philly Point 64','3609 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (65,'Philly Point 65','3707 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (66,'Philly Point 66','3235 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (67,'Philly Point 67','2458 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (68,'Philly Point 68','1658 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (69,'Philly Point 69','2053 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (70,'Philly Point 70','124 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (71,'Philly Point 71','3961 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (72,'Philly Point 72','1540 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (73,'Philly Point 73','1323 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (74,'Philly Point 74','3186 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (75,'Philly Point 75','1697 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (76,'Philly Point 76','3595 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (77,'Philly Point 77','3752 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (78,'Philly Point 78','3523 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (79,'Philly Point 79','1816 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (80,'Philly Point 80','2304 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (81,'Philly Point 81','3161 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (82,'Philly Point 82','3109 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (83,'Philly Point 83','2336 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (84,'Philly Point 84','3376 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (85,'Philly Point 85','2570 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (86,'Philly Point 86','3777 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (87,'Philly Point 87','1003 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (88,'Philly Point 88','2099 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (89,'Philly Point 89','998 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (90,'Philly Point 90','1217 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (91,'Philly Point 91','1885 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (92,'Philly Point 92','2089 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (93,'Philly Point 93','218 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (94,'Philly Point 94','1692 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (95,'Philly Point 95','1476 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (96,'Philly Point 96','2839 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (97,'Philly Point 97','2881 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (98,'Philly Point 98','3369 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (99,'Philly Point 99','1756 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (100,'Philly Point 100','3066 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (101,'Philly Point 101','776 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (102,'Philly Point 102','3542 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (103,'Philly Point 103','2014 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (104,'Philly Point 104','3866 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (105,'Philly Point 105','622 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (106,'Philly Point 106','2648 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (107,'Philly Point 107','2287 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (108,'Philly Point 108','210 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (109,'Philly Point 109','3814 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (110,'Philly Point 110','1713 Market St');
+INSERT INTO LOCATION (location_id,name,address) VALUES (111,'Philly Point 111','2524 Market St');
-- RIDE_OFFER
INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (1,5,3,3,8,'2025-10-16T01:45:00',2,4.17,0.72,'closed','2025-10-15T01:45:00');
INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (2,1,6,6,10,'2025-10-15T18:45:00',1,5.31,0.86,'closed','2025-10-14T18:45:00');
INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (3,12,1,2,3,'2025-10-14T13:00:00',2,4.63,1.13,'open','2025-10-13T13:00:00');
INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (4,6,2,3,8,'2025-10-14T17:45:00',3,8.52,0.9,'closed','2025-10-13T17:45:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (5,22,102,68,15,'2025-11-29T02:30:00',2,7.67,1.06,'open','2025-11-26T02:30:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (6,80,56,94,101,'2025-11-24T16:19:00',2,6.86,0.53,'closed','2025-11-18T16:19:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (7,58,76,63,92,'2025-11-10T02:57:00',1,4.93,0.66,'open','2025-11-05T02:57:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (8,54,82,59,65,'2025-11-20T13:06:00',4,3.69,1.22,'closed','2025-11-17T13:06:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (9,58,76,30,53,'2025-11-03T14:25:00',2,7.38,0.84,'open','2025-10-30T14:25:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (10,53,32,88,61,'2025-10-17T02:53:00',1,3.56,0.61,'closed','2025-10-15T02:53:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (11,75,25,76,72,'2025-11-17T10:42:00',1,5.46,1.46,'closed','2025-11-11T10:42:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (12,89,31,37,77,'2025-11-01T11:06:00',3,6.04,0.67,'open','2025-10-31T11:06:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (13,52,23,15,98,'2025-10-30T18:14:00',2,8.07,1.57,'closed','2025-10-30T18:14:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (14,79,46,4,24,'2025-10-30T22:48:00',2,8.53,0.89,'open','2025-10-28T22:48:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (15,76,66,9,19,'2025-11-29T20:01:00',1,7.48,0.74,'open','2025-11-24T20:01:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (16,28,77,40,93,'2025-11-02T18:38:00',1,8.3,0.67,'closed','2025-11-02T18:38:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (17,85,85,35,57,'2025-11-24T13:31:00',3,5.65,0.8,'closed','2025-11-23T13:31:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (18,52,23,15,37,'2025-11-25T21:37:00',2,6.16,0.84,'open','2025-11-25T21:37:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (19,6,3,39,28,'2025-10-21T08:18:00',2,3.72,1.05,'open','2025-10-19T08:18:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (20,54,82,65,72,'2025-11-24T11:04:00',2,8.17,0.55,'open','2025-11-23T11:04:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (21,49,54,74,52,'2025-11-27T20:26:00',2,3.69,0.52,'open','2025-11-20T20:26:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (22,104,12,12,56,'2025-10-19T07:27:00',3,5.4,0.59,'closed','2025-10-14T07:27:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (23,37,11,100,22,'2025-10-17T16:40:00',1,6.18,0.71,'closed','2025-10-12T16:40:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (24,89,31,31,14,'2025-10-22T08:12:00',1,6.61,1.34,'closed','2025-10-20T08:12:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (25,98,84,60,97,'2025-11-18T18:28:00',4,8.54,1.12,'closed','2025-11-13T18:28:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (26,81,73,20,57,'2025-10-17T15:28:00',4,4.82,0.8,'open','2025-10-16T15:28:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (27,48,81,58,5,'2025-10-16T11:53:00',2,3.46,1.45,'closed','2025-10-10T11:53:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (28,61,9,58,104,'2025-11-18T20:12:00',2,6.63,1.05,'closed','2025-11-11T20:12:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (29,20,34,92,11,'2025-11-14T20:11:00',1,4.49,0.98,'open','2025-11-12T20:11:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (30,53,92,37,50,'2025-11-08T10:43:00',3,3.31,1.19,'open','2025-11-03T10:43:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (31,20,34,37,33,'2025-11-28T21:58:00',3,8.24,0.87,'open','2025-11-26T21:58:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (32,52,23,84,90,'2025-11-24T12:08:00',3,7.25,0.59,'open','2025-11-19T12:08:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (33,103,36,86,90,'2025-11-29T21:58:00',3,3.56,1.24,'open','2025-11-29T21:58:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (34,53,92,24,28,'2025-11-03T15:12:00',1,9.0,0.67,'open','2025-11-02T15:12:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (35,66,99,70,107,'2025-11-29T16:02:00',4,5.02,1.34,'open','2025-11-23T16:02:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (36,27,37,24,107,'2025-11-26T19:51:00',1,7.33,0.55,'open','2025-11-23T19:51:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (37,59,80,37,97,'2025-11-29T14:14:00',3,4.44,1.56,'closed','2025-11-26T14:14:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (38,53,92,60,99,'2025-10-31T12:32:00',3,5.51,0.68,'open','2025-10-29T12:32:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (39,43,10,83,62,'2025-11-05T17:59:00',1,7.27,1.07,'open','2025-11-04T17:59:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (40,93,40,35,58,'2025-11-14T04:53:00',2,3.55,1.5,'closed','2025-11-09T04:53:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (41,7,58,7,51,'2025-11-14T11:15:00',2,8.97,0.91,'open','2025-11-13T11:15:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (42,104,12,102,19,'2025-10-21T01:18:00',2,7.18,0.65,'closed','2025-10-14T01:18:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (43,80,56,11,3,'2025-10-29T06:53:00',1,6.29,1.3,'open','2025-10-28T06:53:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (44,98,84,31,39,'2025-10-20T01:15:00',2,6.83,1.19,'open','2025-10-13T01:15:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (45,79,46,81,66,'2025-11-18T07:45:00',1,4.75,0.5,'open','2025-11-12T07:45:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (46,30,8,68,47,'2025-10-17T16:34:00',3,7.72,1.06,'open','2025-10-10T16:34:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (47,10,86,48,33,'2025-11-29T00:22:00',1,5.07,1.31,'closed','2025-11-24T00:22:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (48,22,102,46,70,'2025-11-03T20:11:00',4,5.79,1.26,'closed','2025-11-01T20:11:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (49,17,92,100,59,'2025-10-15T09:12:00',1,7.75,1.47,'open','2025-10-11T09:12:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (50,66,51,105,70,'2025-11-12T08:02:00',4,4.15,0.89,'closed','2025-11-07T08:02:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (51,45,21,103,48,'2025-11-09T12:47:00',2,8.37,0.93,'closed','2025-11-02T12:47:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (52,86,47,48,103,'2025-11-15T08:51:00',1,7.36,0.59,'closed','2025-11-13T08:51:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (53,70,17,42,14,'2025-10-18T10:42:00',2,4.84,1.16,'open','2025-10-11T10:42:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (54,52,23,6,94,'2025-11-04T19:27:00',2,6.84,1.37,'open','2025-10-29T19:27:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (55,53,32,4,19,'2025-11-20T21:50:00',2,3.21,0.57,'closed','2025-11-15T21:50:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (56,53,92,73,5,'2025-11-20T04:43:00',2,8.69,0.91,'closed','2025-11-18T04:43:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (57,68,88,51,41,'2025-11-23T08:15:00',1,3.16,0.7,'open','2025-11-22T08:15:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (58,43,29,91,58,'2025-10-26T19:18:00',4,8.5,1.04,'open','2025-10-25T19:18:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (59,59,39,92,57,'2025-10-18T21:54:00',2,7.01,1.28,'open','2025-10-14T21:54:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (60,48,15,92,91,'2025-11-26T20:11:00',2,6.05,0.63,'open','2025-11-24T20:11:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (61,40,96,4,47,'2025-11-17T18:23:00',2,7.82,1.11,'closed','2025-11-16T18:23:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (62,17,40,51,92,'2025-11-28T15:33:00',2,7.61,1.41,'open','2025-11-23T15:33:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (63,83,10,58,60,'2025-11-25T16:22:00',1,8.27,1.36,'closed','2025-11-23T16:22:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (64,98,84,56,65,'2025-10-16T03:33:00',1,4.82,0.68,'closed','2025-10-13T03:33:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (65,52,23,11,33,'2025-10-25T20:35:00',2,3.75,0.83,'open','2025-10-23T20:35:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (66,78,97,22,85,'2025-11-21T23:57:00',3,5.03,1.52,'open','2025-11-21T23:57:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (67,19,26,83,99,'2025-11-18T08:41:00',1,7.6,0.96,'closed','2025-11-11T08:41:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (68,81,94,38,83,'2025-11-01T15:15:00',4,5.44,1.0,'closed','2025-10-30T15:15:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (69,59,69,62,60,'2025-10-26T10:38:00',1,4.88,1.29,'closed','2025-10-21T10:38:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (70,55,100,98,48,'2025-11-14T17:06:00',2,4.45,0.63,'open','2025-11-12T17:06:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (71,20,16,38,50,'2025-11-21T13:15:00',1,7.88,1.53,'closed','2025-11-18T13:15:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (72,93,40,64,66,'2025-11-11T15:56:00',2,5.98,0.6,'open','2025-11-04T15:56:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (73,40,41,75,46,'2025-10-16T01:18:00',2,6.59,1.43,'closed','2025-10-12T01:18:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (74,70,17,14,56,'2025-10-21T08:46:00',2,7.58,0.9,'open','2025-10-18T08:46:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (75,53,92,10,50,'2025-11-14T14:48:00',3,4.68,1.59,'closed','2025-11-13T14:48:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (76,22,102,51,48,'2025-11-03T17:23:00',1,4.19,1.06,'open','2025-11-03T17:23:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (77,10,68,92,43,'2025-11-12T16:29:00',1,6.64,1.07,'open','2025-11-07T16:29:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (78,28,77,79,95,'2025-11-01T18:21:00',3,7.97,1.09,'closed','2025-10-28T18:21:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (79,64,50,48,43,'2025-11-25T03:26:00',3,4.85,1.49,'closed','2025-11-25T03:26:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (80,79,46,34,84,'2025-11-19T18:14:00',4,3.31,1.03,'open','2025-11-13T18:14:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (81,27,104,32,5,'2025-11-18T22:07:00',1,3.11,0.85,'open','2025-11-15T22:07:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (82,56,70,100,79,'2025-11-12T23:46:00',1,7.23,1.07,'open','2025-11-05T23:46:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (83,68,88,41,23,'2025-11-11T17:21:00',3,5.13,1.35,'closed','2025-11-07T17:21:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (84,80,71,25,32,'2025-10-30T17:19:00',1,8.65,1.58,'closed','2025-10-27T17:19:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (85,86,47,41,62,'2025-11-04T17:59:00',4,4.64,0.63,'closed','2025-10-29T17:59:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (86,55,100,99,103,'2025-10-22T09:02:00',2,8.73,0.59,'closed','2025-10-18T09:02:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (87,91,87,28,26,'2025-11-16T08:59:00',3,7.18,0.65,'closed','2025-11-13T08:59:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (88,40,41,86,68,'2025-10-27T20:14:00',1,3.6,0.86,'open','2025-10-25T20:14:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (89,6,3,53,84,'2025-11-12T15:41:00',1,7.54,0.82,'open','2025-11-12T15:41:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (90,98,84,84,74,'2025-10-27T17:47:00',4,8.1,0.54,'closed','2025-10-21T17:47:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (91,104,12,5,108,'2025-11-07T15:11:00',4,8.77,1.52,'closed','2025-11-07T15:11:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (92,48,15,43,37,'2025-11-11T20:34:00',3,5.96,1.53,'closed','2025-11-04T20:34:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (93,45,45,104,15,'2025-11-03T05:46:00',2,6.89,1.29,'open','2025-10-29T05:46:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (94,100,63,73,87,'2025-11-30T06:11:00',3,8.14,1.49,'closed','2025-11-25T06:11:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (95,19,51,86,13,'2025-10-24T04:30:00',2,8.61,0.51,'open','2025-10-17T04:30:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (96,93,74,43,39,'2025-11-19T23:36:00',1,4.57,0.9,'open','2025-11-18T23:36:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (97,61,20,21,52,'2025-11-25T16:59:00',4,7.62,1.26,'closed','2025-11-21T16:59:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (98,53,32,29,18,'2025-11-12T04:29:00',4,8.93,0.91,'closed','2025-11-05T04:29:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (99,93,90,103,86,'2025-10-26T07:43:00',3,8.24,0.59,'open','2025-10-21T07:43:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (100,17,73,15,8,'2025-11-17T16:12:00',3,6.22,0.68,'closed','2025-11-10T16:12:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (101,21,88,27,92,'2025-11-19T15:05:00',3,5.67,0.56,'open','2025-11-13T15:05:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (102,61,9,72,60,'2025-11-25T09:46:00',1,5.38,1.4,'closed','2025-11-24T09:46:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (103,10,86,45,90,'2025-10-17T17:03:00',1,8.92,1.02,'open','2025-10-15T17:03:00');
-INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (104,98,84,99,83,'2025-11-28T20:26:00',2,8.96,0.92,'closed','2025-11-22T20:26:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (5,7,46,17,2,'2025-11-04T07:37:00',2,3.09,0.79,'open','2025-10-29T07:37:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (6,58,25,96,9,'2025-11-12T14:49:00',2,6.08,0.62,'open','2025-11-12T14:49:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (7,81,71,59,83,'2025-10-14T01:30:00',2,5.56,0.62,'closed','2025-10-07T01:30:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (8,1001,102,42,78,'2025-10-22T02:08:00',2,6.75,1.14,'closed','2025-10-16T02:08:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (9,66,64,38,59,'2025-11-14T19:27:00',1,7.76,0.63,'closed','2025-11-11T19:27:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (10,50,54,30,53,'2025-11-03T14:25:00',2,7.38,0.84,'open','2025-10-30T14:25:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (11,45,33,88,61,'2025-10-17T02:53:00',1,3.56,0.61,'closed','2025-10-15T02:53:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (12,61,26,76,72,'2025-11-17T10:42:00',1,5.46,1.46,'closed','2025-11-11T10:42:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (13,98,56,37,77,'2025-11-01T11:06:00',3,6.04,0.67,'open','2025-10-31T11:06:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (14,43,82,15,98,'2025-10-30T18:14:00',2,8.07,1.57,'closed','2025-10-30T18:14:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (15,66,81,107,89,'2025-10-30T00:11:00',2,7.22,0.84,'open','2025-10-30T00:11:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (16,19,8,73,85,'2025-11-07T02:09:00',4,6.8,0.53,'closed','2025-11-04T02:09:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (17,46,50,59,44,'2025-10-23T11:19:00',4,4.95,1.54,'open','2025-10-23T11:19:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (18,16,38,97,80,'2025-10-16T21:05:00',2,5.66,0.97,'closed','2025-10-10T21:05:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (19,30,58,97,66,'2025-10-20T11:27:00',1,4.7,1.25,'open','2025-10-16T11:27:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (20,4,44,51,77,'2025-10-16T00:13:00',2,8.67,1.34,'closed','2025-10-12T00:13:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (21,37,12,1,64,'2025-11-29T13:11:00',1,5.28,1.27,'open','2025-11-24T13:11:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (22,1001,102,111,95,'2025-10-15T13:01:00',2,8.53,1.45,'open','2025-10-09T13:01:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (23,80,78,54,38,'2025-10-20T12:01:00',2,4.03,1.54,'open','2025-10-15T12:01:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (24,1003,55,109,14,'2025-10-28T13:37:00',2,6.14,0.94,'open','2025-10-23T13:37:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (25,22,11,100,22,'2025-10-17T16:40:00',1,6.18,0.71,'closed','2025-10-12T16:40:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (26,81,32,31,14,'2025-10-22T08:12:00',1,6.61,1.34,'closed','2025-10-20T08:12:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (27,85,86,60,97,'2025-11-18T18:28:00',4,8.54,1.12,'closed','2025-11-13T18:28:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (28,98,70,20,57,'2025-10-17T15:28:00',4,4.82,0.8,'open','2025-10-16T15:28:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (29,36,56,58,5,'2025-10-16T11:53:00',2,3.46,1.45,'closed','2025-10-10T11:53:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (30,53,9,58,104,'2025-11-18T20:12:00',2,6.63,1.05,'closed','2025-11-11T20:12:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (31,6,35,92,11,'2025-11-14T20:11:00',1,4.49,0.98,'open','2025-11-12T20:11:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (32,45,94,37,50,'2025-11-08T10:43:00',3,3.31,1.19,'open','2025-11-03T10:43:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (33,6,35,37,33,'2025-11-28T21:58:00',3,8.24,0.87,'open','2025-11-26T21:58:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (34,103,40,40,84,'2025-11-26T21:25:00',1,6.57,1.53,'open','2025-11-20T21:25:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (35,75,98,43,105,'2025-10-21T21:44:00',4,8.98,1.5,'open','2025-10-15T21:44:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (36,56,43,3,47,'2025-11-01T05:13:00',2,8.69,1.03,'open','2025-10-30T05:13:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (37,16,38,38,109,'2025-10-19T16:49:00',3,8.01,1.47,'open','2025-10-14T16:49:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (38,100,95,80,17,'2025-11-20T12:09:00',1,4.09,1.26,'closed','2025-11-18T12:09:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (39,81,71,6,53,'2025-11-05T21:46:00',1,8.82,1.17,'closed','2025-10-29T21:46:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (40,22,11,40,104,'2025-11-12T06:23:00',4,8.68,1.58,'open','2025-11-08T06:23:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (41,85,86,65,68,'2025-11-08T05:52:00',1,7.81,0.65,'open','2025-11-01T05:52:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (42,98,70,71,14,'2025-11-27T16:54:00',1,4.71,1.34,'open','2025-11-25T16:54:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (43,91,12,12,29,'2025-11-10T11:59:00',1,5.49,0.94,'open','2025-11-04T11:59:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (44,1003,55,29,4,'2025-11-02T03:53:00',4,6.9,1.37,'open','2025-10-29T03:53:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (45,103,40,90,107,'2025-10-21T22:30:00',2,6.69,1.5,'open','2025-10-18T22:30:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (46,91,12,71,94,'2025-11-20T16:27:00',1,7.66,0.76,'open','2025-11-17T16:27:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (47,49,54,9,15,'2025-11-13T19:34:00',1,6.79,1.13,'closed','2025-11-09T19:34:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (48,50,1,79,46,'2025-10-28T18:26:00',1,6.99,0.59,'closed','2025-10-27T18:26:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (49,58,90,65,101,'2025-11-14T17:01:00',2,8.24,0.55,'open','2025-11-09T17:01:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (50,28,10,46,101,'2025-10-17T11:15:00',4,6.94,0.61,'open','2025-10-12T11:15:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (51,10,108,46,70,'2025-11-03T20:11:00',4,5.79,1.26,'closed','2025-11-01T20:11:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (52,1001,102,5,38,'2025-10-25T01:50:00',1,8.31,0.85,'open','2025-10-19T01:50:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (53,89,109,33,5,'2025-11-30T20:12:00',2,5.14,1.36,'closed','2025-11-25T20:12:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (54,30,58,103,48,'2025-11-09T12:47:00',2,8.37,0.93,'closed','2025-11-02T12:47:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (55,79,60,48,103,'2025-11-15T08:51:00',1,7.36,0.59,'closed','2025-11-13T08:51:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (56,59,34,42,14,'2025-10-18T10:42:00',2,4.84,1.16,'open','2025-10-11T10:42:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (57,43,82,6,94,'2025-11-04T19:27:00',2,6.84,1.37,'open','2025-10-29T19:27:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (58,45,33,4,19,'2025-11-20T21:50:00',2,3.21,0.57,'closed','2025-11-15T21:50:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (59,45,94,73,5,'2025-11-20T04:43:00',2,8.69,0.91,'closed','2025-11-18T04:43:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (60,58,78,51,41,'2025-11-23T08:15:00',1,3.16,0.7,'open','2025-11-22T08:15:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (61,28,30,91,58,'2025-10-26T19:18:00',4,8.5,1.04,'open','2025-10-25T19:18:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (62,52,49,92,57,'2025-10-18T21:54:00',2,7.01,1.28,'open','2025-10-14T21:54:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (63,101,81,109,21,'2025-11-27T22:59:00',4,6.83,1.37,'open','2025-11-26T22:59:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (64,20,57,31,102,'2025-11-13T00:23:00',3,6.43,1.01,'closed','2025-11-11T00:23:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (65,68,20,9,40,'2025-11-07T22:46:00',2,6.15,1.35,'closed','2025-11-06T22:46:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (66,10,108,83,10,'2025-11-10T14:43:00',3,5.07,1.47,'closed','2025-11-08T14:43:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (67,85,86,56,65,'2025-10-16T03:33:00',1,4.82,0.68,'closed','2025-10-13T03:33:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (68,43,82,109,11,'2025-10-29T06:40:00',3,4.65,1.19,'closed','2025-10-28T06:40:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (69,56,79,22,76,'2025-11-19T04:10:00',4,6.75,1.49,'open','2025-11-19T04:10:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (70,89,37,11,6,'2025-11-23T18:16:00',4,4.26,1.13,'closed','2025-11-23T18:16:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (71,55,61,83,39,'2025-11-12T07:51:00',4,5.44,1.0,'closed','2025-11-10T07:51:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (72,52,49,62,60,'2025-10-26T10:38:00',1,4.88,1.29,'closed','2025-10-21T10:38:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (73,48,106,98,48,'2025-11-14T17:06:00',2,4.45,0.63,'open','2025-11-12T17:06:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (74,6,3,38,50,'2025-11-21T13:15:00',1,7.88,1.53,'closed','2025-11-18T13:15:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (75,84,22,64,66,'2025-11-11T15:56:00',2,5.98,0.6,'open','2025-11-04T15:56:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (76,27,98,75,46,'2025-10-16T01:18:00',2,6.59,1.43,'closed','2025-10-12T01:18:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (77,59,2,109,14,'2025-11-09T04:56:00',2,7.37,1.34,'open','2025-11-03T04:56:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (78,5,2,47,71,'2025-10-31T02:24:00',3,5.7,1.1,'closed','2025-10-30T02:24:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (79,10,108,51,48,'2025-11-03T17:23:00',1,4.19,1.06,'open','2025-11-03T17:23:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (80,4,44,92,43,'2025-11-12T16:29:00',1,6.64,1.07,'open','2025-11-07T16:29:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (81,17,43,79,95,'2025-11-01T18:21:00',3,7.97,1.09,'closed','2025-10-28T18:21:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (82,54,62,48,43,'2025-11-25T03:26:00',3,4.85,1.49,'closed','2025-11-25T03:26:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (83,66,57,34,84,'2025-11-19T18:14:00',4,3.31,1.03,'open','2025-11-13T18:14:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (84,16,38,5,74,'2025-11-26T03:12:00',1,5.65,0.96,'open','2025-11-23T03:12:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (85,49,54,109,95,'2025-11-28T01:45:00',1,6.11,1.12,'closed','2025-11-21T01:45:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (86,58,78,41,23,'2025-11-11T17:21:00',3,5.13,1.35,'closed','2025-11-07T17:21:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (87,68,79,25,32,'2025-10-30T17:19:00',1,8.65,1.58,'closed','2025-10-27T17:19:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (88,79,87,63,41,'2025-11-12T11:35:00',4,4.64,0.63,'closed','2025-11-06T11:35:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (89,101,81,105,45,'2025-10-22T09:02:00',2,8.73,0.59,'closed','2025-10-18T09:02:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (90,83,75,28,26,'2025-11-16T08:59:00',3,7.18,0.65,'closed','2025-11-13T08:59:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (91,27,98,86,68,'2025-10-27T20:14:00',1,3.6,0.86,'open','2025-10-25T20:14:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (92,1,100,53,84,'2025-11-12T15:41:00',1,7.54,0.82,'open','2025-11-12T15:41:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (93,103,40,84,74,'2025-10-27T17:47:00',4,8.1,0.54,'closed','2025-10-21T17:47:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (94,100,108,23,5,'2025-11-07T15:11:00',4,8.77,1.52,'closed','2025-11-07T15:11:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (95,36,69,78,14,'2025-11-03T09:29:00',4,6.26,1.04,'closed','2025-10-27T09:29:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (96,30,58,104,15,'2025-11-03T05:46:00',2,6.89,1.29,'open','2025-10-29T05:46:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (97,86,93,73,87,'2025-11-30T06:11:00',3,8.14,1.49,'closed','2025-11-25T06:11:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (98,1003,55,86,13,'2025-10-24T04:30:00',2,8.61,0.51,'open','2025-10-17T04:30:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (99,84,92,43,39,'2025-11-19T23:36:00',1,4.57,0.9,'open','2025-11-18T23:36:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (100,53,80,21,52,'2025-11-25T16:59:00',4,7.62,1.26,'closed','2025-11-21T16:59:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (101,45,33,29,18,'2025-11-12T04:29:00',4,8.93,0.91,'closed','2025-11-05T04:29:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (102,84,22,98,32,'2025-11-25T19:55:00',1,6.15,1.08,'open','2025-11-24T19:55:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (103,5,2,74,69,'2025-10-22T05:20:00',3,5.65,1.25,'closed','2025-10-15T05:20:00');
+INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (104,1003,60,58,104,'2025-10-16T14:08:00',3,5.49,1.12,'open','2025-10-12T14:08:00');
-- RIDE_REQUEST
INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (1,11,7,10,'2025-10-14T19:05:00','2025-10-14T19:45:00',1,'cancelled','2025-10-13T19:15:00');
@@ -436,196 +448,253 @@ INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_locatio
INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (4,13,6,7,'2025-10-13T23:50:00','2025-10-14T00:30:00',2,'matched','2025-10-13T00:00:00');
INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (5,10,7,2,'2025-10-15T21:45:00','2025-10-15T21:55:00',1,'open','2025-10-14T21:45:00');
INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (6,11,3,2,'2025-10-15T03:00:00','2025-10-15T03:10:00',1,'cancelled','2025-10-14T03:00:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (7,75,11,88,'2025-11-24T19:34:00','2025-11-24T19:52:00',2,'matched','2025-11-24T19:34:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (8,37,69,51,'2025-11-15T08:46:00','2025-11-15T09:10:00',1,'matched','2025-11-13T08:46:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (9,94,87,93,'2025-11-16T12:34:00','2025-11-16T13:08:00',1,'open','2025-11-14T12:34:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (10,33,36,25,'2025-11-28T18:07:00','2025-11-28T18:19:00',2,'open','2025-11-28T18:07:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (11,51,27,9,'2025-10-19T15:02:00','2025-10-19T15:40:00',2,'matched','2025-10-19T15:02:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (12,52,95,6,'2025-11-07T13:14:00','2025-11-07T13:58:00',1,'matched','2025-11-07T13:14:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (13,31,65,38,'2025-10-27T19:58:00','2025-10-27T20:54:00',2,'open','2025-10-23T19:58:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (14,67,31,39,'2025-10-22T16:33:00','2025-10-22T16:57:00',1,'open','2025-10-22T16:33:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (15,37,81,87,'2025-11-09T14:31:00','2025-11-09T14:44:00',1,'cancelled','2025-11-04T14:31:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (16,75,101,68,'2025-11-02T17:26:00','2025-11-02T18:02:00',1,'open','2025-11-01T17:26:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (17,95,31,29,'2025-11-01T19:45:00','2025-11-01T20:04:00',1,'cancelled','2025-10-28T19:45:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (18,82,54,72,'2025-11-15T08:24:00','2025-11-15T08:49:00',1,'open','2025-11-10T08:24:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (19,23,58,48,'2025-10-18T14:46:00','2025-10-18T15:08:00',1,'open','2025-10-13T14:46:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (20,14,10,25,'2025-11-30T15:46:00','2025-11-30T16:38:00',2,'open','2025-11-29T15:46:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (21,69,39,2,'2025-10-26T20:12:00','2025-10-26T21:09:00',1,'matched','2025-10-23T20:12:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (22,52,90,78,'2025-11-27T09:25:00','2025-11-27T09:50:00',2,'open','2025-11-25T09:25:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (23,75,60,69,'2025-11-23T11:19:00','2025-11-23T11:45:00',1,'open','2025-11-20T11:19:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (24,94,13,103,'2025-11-28T13:48:00','2025-11-28T14:18:00',1,'open','2025-11-25T13:48:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (25,14,73,29,'2025-11-29T08:01:00','2025-11-29T08:27:00',2,'cancelled','2025-11-25T08:01:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (26,83,38,20,'2025-10-25T11:14:00','2025-10-25T11:48:00',2,'matched','2025-10-22T11:14:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (27,70,33,98,'2025-11-13T17:41:00','2025-11-13T18:38:00',1,'open','2025-11-08T17:41:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (28,72,22,18,'2025-11-28T14:31:00','2025-11-28T14:52:00',2,'cancelled','2025-11-28T14:31:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (29,13,108,10,'2025-11-24T06:48:00','2025-11-24T06:58:00',1,'open','2025-11-19T06:48:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (30,48,9,91,'2025-10-22T06:13:00','2025-10-22T06:55:00',1,'open','2025-10-18T06:13:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (31,96,85,63,'2025-10-14T06:34:00','2025-10-14T07:19:00',1,'open','2025-10-10T06:34:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (32,59,69,37,'2025-10-14T14:52:00','2025-10-14T15:46:00',2,'open','2025-10-13T14:52:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (33,26,13,68,'2025-10-22T09:12:00','2025-10-22T10:01:00',2,'open','2025-10-20T09:12:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (34,57,102,51,'2025-10-18T11:25:00','2025-10-18T12:04:00',2,'open','2025-10-13T11:25:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (35,47,39,88,'2025-10-18T16:54:00','2025-10-18T17:45:00',1,'open','2025-10-15T16:54:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (36,75,71,61,'2025-10-16T16:00:00','2025-10-16T16:54:00',1,'open','2025-10-13T16:00:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (37,69,73,13,'2025-11-15T20:02:00','2025-11-15T20:26:00',1,'cancelled','2025-11-10T20:02:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (38,95,35,6,'2025-10-17T16:59:00','2025-10-17T17:26:00',2,'matched','2025-10-17T16:59:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (39,37,41,3,'2025-10-26T15:09:00','2025-10-26T16:07:00',2,'matched','2025-10-26T15:09:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (40,63,21,73,'2025-10-28T15:53:00','2025-10-28T16:27:00',2,'matched','2025-10-26T15:53:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (41,76,97,95,'2025-10-21T18:44:00','2025-10-21T19:40:00',1,'open','2025-10-19T18:44:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (42,15,13,56,'2025-10-27T19:04:00','2025-10-27T19:35:00',2,'matched','2025-10-23T19:04:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (43,77,100,42,'2025-10-14T16:17:00','2025-10-14T17:17:00',1,'open','2025-10-12T16:17:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (44,75,56,24,'2025-11-25T15:42:00','2025-11-25T16:16:00',1,'matched','2025-11-23T15:42:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (45,52,92,10,'2025-10-18T10:09:00','2025-10-18T10:43:00',2,'matched','2025-10-17T10:09:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (46,76,41,47,'2025-10-19T07:00:00','2025-10-19T07:29:00',1,'open','2025-10-17T07:00:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (47,26,17,12,'2025-10-24T12:28:00','2025-10-24T13:13:00',2,'open','2025-10-24T12:28:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (48,12,12,46,'2025-11-17T07:38:00','2025-11-17T08:26:00',1,'matched','2025-11-17T07:38:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (49,62,53,50,'2025-10-18T17:57:00','2025-10-18T18:42:00',1,'matched','2025-10-17T17:57:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (50,75,22,18,'2025-10-30T10:17:00','2025-10-30T10:58:00',1,'open','2025-10-27T10:17:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (51,59,54,39,'2025-11-12T18:04:00','2025-11-12T18:37:00',1,'cancelled','2025-11-07T18:04:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (52,99,77,79,'2025-10-25T13:06:00','2025-10-25T13:24:00',1,'open','2025-10-22T13:06:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (53,69,108,80,'2025-11-06T18:55:00','2025-11-06T19:26:00',1,'open','2025-11-01T18:55:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (54,31,39,42,'2025-11-20T17:12:00','2025-11-20T17:52:00',1,'open','2025-11-17T17:12:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (55,66,38,95,'2025-11-26T16:31:00','2025-11-26T17:17:00',1,'open','2025-11-24T16:31:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (56,77,47,15,'2025-11-18T09:37:00','2025-11-18T10:21:00',1,'matched','2025-11-14T09:37:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (57,12,94,60,'2025-11-27T09:28:00','2025-11-27T09:56:00',2,'open','2025-11-24T09:28:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (58,99,18,82,'2025-11-01T09:16:00','2025-11-01T10:08:00',1,'matched','2025-10-29T09:16:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (59,22,58,77,'2025-11-12T15:25:00','2025-11-12T16:09:00',2,'cancelled','2025-11-07T15:25:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (60,83,70,5,'2025-11-05T17:57:00','2025-11-05T18:41:00',2,'matched','2025-11-05T17:57:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (61,26,99,32,'2025-11-24T16:22:00','2025-11-24T16:42:00',2,'matched','2025-11-20T16:22:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (62,79,97,43,'2025-11-09T07:00:00','2025-11-09T07:16:00',1,'open','2025-11-04T07:00:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (63,105,72,75,'2025-11-26T15:14:00','2025-11-26T15:52:00',1,'open','2025-11-21T15:14:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (64,101,20,45,'2025-10-14T13:06:00','2025-10-14T13:34:00',1,'cancelled','2025-10-14T13:06:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (65,27,107,94,'2025-10-22T11:19:00','2025-10-22T11:50:00',1,'matched','2025-10-18T11:19:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (66,96,45,61,'2025-10-19T13:46:00','2025-10-19T14:40:00',1,'open','2025-10-17T13:46:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (67,14,104,91,'2025-10-20T06:54:00','2025-10-20T07:25:00',2,'open','2025-10-19T06:54:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (68,52,67,23,'2025-11-17T08:21:00','2025-11-17T09:06:00',1,'cancelled','2025-11-16T08:21:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (69,79,81,24,'2025-10-24T16:42:00','2025-10-24T17:19:00',1,'open','2025-10-20T16:42:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (70,41,58,76,'2025-11-09T12:00:00','2025-11-09T12:55:00',1,'open','2025-11-04T12:00:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (71,18,74,14,'2025-11-16T08:23:00','2025-11-16T08:53:00',1,'open','2025-11-14T08:23:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (72,97,68,82,'2025-11-02T15:24:00','2025-11-02T16:13:00',1,'matched','2025-10-31T15:24:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (73,72,59,80,'2025-10-24T19:43:00','2025-10-24T20:38:00',1,'cancelled','2025-10-20T19:43:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (74,23,87,18,'2025-11-02T07:15:00','2025-11-02T07:44:00',1,'open','2025-10-28T07:15:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (75,32,66,50,'2025-11-08T15:08:00','2025-11-08T15:54:00',1,'open','2025-11-05T15:08:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (76,37,72,22,'2025-11-13T10:08:00','2025-11-13T10:29:00',1,'matched','2025-11-09T10:08:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (77,15,46,2,'2025-11-13T08:12:00','2025-11-13T08:46:00',2,'open','2025-11-10T08:12:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (78,82,88,63,'2025-11-08T17:53:00','2025-11-08T18:31:00',1,'open','2025-11-04T17:53:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (79,12,102,98,'2025-10-27T10:02:00','2025-10-27T10:29:00',1,'open','2025-10-26T10:02:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (80,92,73,96,'2025-11-13T14:32:00','2025-11-13T14:49:00',2,'open','2025-11-09T14:32:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (81,73,70,106,'2025-11-30T06:48:00','2025-11-30T07:44:00',1,'open','2025-11-27T06:48:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (82,26,95,107,'2025-11-23T18:15:00','2025-11-23T18:44:00',1,'open','2025-11-21T18:15:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (83,24,57,16,'2025-10-28T09:51:00','2025-10-28T10:48:00',2,'matched','2025-10-23T09:51:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (84,84,22,79,'2025-10-21T18:13:00','2025-10-21T18:36:00',1,'matched','2025-10-17T18:13:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (85,59,77,69,'2025-10-23T11:45:00','2025-10-23T12:13:00',1,'matched','2025-10-19T11:45:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (86,25,18,97,'2025-11-08T20:03:00','2025-11-08T20:30:00',2,'open','2025-11-07T20:03:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (87,52,19,92,'2025-11-02T19:15:00','2025-11-02T20:13:00',2,'open','2025-11-01T19:15:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (88,57,81,54,'2025-11-06T13:04:00','2025-11-06T13:54:00',1,'open','2025-11-01T13:04:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (89,59,89,48,'2025-11-11T13:20:00','2025-11-11T14:07:00',1,'matched','2025-11-06T13:20:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (90,24,94,59,'2025-11-22T16:44:00','2025-11-22T17:16:00',1,'matched','2025-11-19T16:44:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (91,44,55,106,'2025-10-26T13:17:00','2025-10-26T13:47:00',1,'open','2025-10-22T13:17:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (92,29,73,63,'2025-11-03T20:43:00','2025-11-03T21:41:00',1,'open','2025-10-29T20:43:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (93,92,3,16,'2025-10-23T13:29:00','2025-10-23T13:39:00',1,'open','2025-10-18T13:29:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (94,29,84,39,'2025-10-23T19:59:00','2025-10-23T20:26:00',1,'matched','2025-10-21T19:59:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (95,23,48,86,'2025-11-23T08:03:00','2025-11-23T08:38:00',2,'open','2025-11-18T08:03:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (96,48,55,84,'2025-10-18T17:06:00','2025-10-18T17:16:00',1,'open','2025-10-17T17:06:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (97,47,67,88,'2025-11-10T06:00:00','2025-11-10T06:54:00',1,'matched','2025-11-07T06:00:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (98,29,62,10,'2025-10-27T12:05:00','2025-10-27T13:01:00',1,'open','2025-10-25T12:05:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (99,63,18,49,'2025-11-30T19:08:00','2025-11-30T19:59:00',2,'open','2025-11-26T19:08:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (100,9,79,84,'2025-10-23T13:22:00','2025-10-23T14:18:00',1,'matched','2025-10-22T13:22:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (101,82,79,88,'2025-11-10T19:13:00','2025-11-10T19:28:00',1,'open','2025-11-10T19:13:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (102,76,45,55,'2025-11-02T18:08:00','2025-11-02T18:33:00',1,'matched','2025-11-01T18:08:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (103,60,100,89,'2025-10-14T19:54:00','2025-10-14T20:46:00',1,'open','2025-10-10T19:54:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (104,102,25,96,'2025-11-07T10:41:00','2025-11-07T10:54:00',1,'open','2025-11-07T10:41:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (105,51,64,82,'2025-11-20T07:33:00','2025-11-20T07:43:00',1,'cancelled','2025-11-19T07:33:00');
-INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (106,76,108,73,'2025-11-08T11:34:00','2025-11-08T12:27:00',1,'matched','2025-11-08T11:34:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (7,5,51,33,'2025-10-13T17:13:00','2025-10-13T18:00:00',1,'open','2025-10-11T17:13:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (8,9,70,8,'2025-10-17T20:30:00','2025-10-17T20:42:00',1,'open','2025-10-16T20:30:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (9,73,48,49,'2025-11-10T19:58:00','2025-11-10T20:32:00',1,'open','2025-11-05T19:58:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (10,97,18,84,'2025-11-04T07:11:00','2025-11-04T07:55:00',1,'open','2025-10-30T07:11:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (11,35,107,1,'2025-11-30T06:19:00','2025-11-30T06:58:00',2,'matched','2025-11-27T06:19:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (12,96,49,106,'2025-10-27T09:29:00','2025-10-27T10:01:00',1,'open','2025-10-22T09:29:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (13,17,5,104,'2025-11-24T12:39:00','2025-11-24T13:38:00',1,'open','2025-11-24T12:39:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (14,14,77,5,'2025-11-10T15:43:00','2025-11-10T16:38:00',1,'open','2025-11-10T15:43:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (15,71,57,30,'2025-11-16T09:48:00','2025-11-16T10:47:00',1,'open','2025-11-14T09:48:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (16,37,105,94,'2025-11-18T11:36:00','2025-11-18T12:24:00',2,'matched','2025-11-17T11:36:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (17,52,19,85,'2025-11-15T09:26:00','2025-11-15T09:55:00',1,'open','2025-11-11T09:26:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (18,28,81,87,'2025-11-09T14:31:00','2025-11-09T14:44:00',1,'cancelled','2025-11-04T14:31:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (19,67,101,68,'2025-11-02T17:26:00','2025-11-02T18:02:00',1,'open','2025-11-01T17:26:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (20,96,61,31,'2025-10-27T10:54:00','2025-10-27T11:49:00',1,'matched','2025-10-27T10:54:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (21,101,53,54,'2025-11-17T14:08:00','2025-11-17T14:42:00',1,'open','2025-11-15T14:08:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (22,12,58,109,'2025-11-05T07:34:00','2025-11-05T08:30:00',1,'open','2025-11-02T07:34:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (23,1004,10,25,'2025-11-30T15:46:00','2025-11-30T16:38:00',2,'open','2025-11-29T15:46:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (24,59,39,2,'2025-10-26T20:12:00','2025-10-26T21:09:00',1,'matched','2025-10-23T20:12:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (25,39,90,78,'2025-11-27T09:25:00','2025-11-27T09:50:00',2,'open','2025-11-25T09:25:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (26,67,60,69,'2025-11-23T11:19:00','2025-11-23T11:45:00',1,'open','2025-11-20T11:19:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (27,82,13,103,'2025-11-28T13:48:00','2025-11-28T14:18:00',1,'open','2025-11-25T13:48:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (28,1004,73,111,'2025-10-27T17:09:00','2025-10-27T17:20:00',1,'matched','2025-10-23T17:09:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (29,73,38,20,'2025-10-25T11:14:00','2025-10-25T11:48:00',2,'matched','2025-10-22T11:14:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (30,99,84,88,'2025-11-03T10:48:00','2025-11-03T11:29:00',2,'matched','2025-10-31T10:48:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (31,79,22,94,'2025-11-04T08:08:00','2025-11-04T09:04:00',2,'open','2025-10-31T08:08:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (32,8,68,5,'2025-10-17T19:42:00','2025-10-17T19:55:00',1,'open','2025-10-12T19:42:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (33,37,9,91,'2025-10-22T06:13:00','2025-10-22T06:55:00',1,'open','2025-10-18T06:13:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (34,84,85,63,'2025-10-14T06:34:00','2025-10-14T07:19:00',1,'open','2025-10-10T06:34:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (35,47,69,37,'2025-10-14T14:52:00','2025-10-14T15:46:00',2,'open','2025-10-13T14:52:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (36,15,13,68,'2025-10-22T09:12:00','2025-10-22T10:01:00',2,'open','2025-10-20T09:12:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (37,44,102,51,'2025-10-18T11:25:00','2025-10-18T12:04:00',2,'open','2025-10-13T11:25:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (38,35,39,88,'2025-10-18T16:54:00','2025-10-18T17:45:00',1,'open','2025-10-15T16:54:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (39,67,71,61,'2025-10-16T16:00:00','2025-10-16T16:54:00',1,'open','2025-10-13T16:00:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (40,59,73,110,'2025-10-19T20:33:00','2025-10-19T20:45:00',1,'open','2025-10-14T20:33:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (41,102,61,35,'2025-10-15T20:04:00','2025-10-15T20:57:00',1,'matched','2025-10-11T20:04:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (42,1003,23,109,'2025-11-02T06:13:00','2025-11-02T07:00:00',1,'matched','2025-10-28T06:13:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (43,70,10,39,'2025-10-23T15:15:00','2025-10-23T16:01:00',1,'matched','2025-10-19T15:15:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (44,59,50,97,'2025-11-29T08:50:00','2025-11-29T09:44:00',2,'open','2025-11-24T08:50:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (45,62,7,13,'2025-11-09T09:53:00','2025-11-09T10:07:00',1,'matched','2025-11-05T09:53:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (46,70,100,42,'2025-10-14T16:17:00','2025-10-14T17:17:00',1,'open','2025-10-12T16:17:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (47,99,49,56,'2025-10-24T16:37:00','2025-10-24T17:29:00',1,'open','2025-10-20T16:37:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (48,51,103,32,'2025-11-27T07:05:00','2025-11-27T07:32:00',1,'open','2025-11-22T07:05:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (49,25,95,50,'2025-11-02T11:06:00','2025-11-02T11:21:00',1,'cancelled','2025-10-30T11:06:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (50,64,98,35,'2025-10-19T08:05:00','2025-10-19T08:26:00',1,'open','2025-10-15T08:05:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (51,92,53,14,'2025-10-14T07:22:00','2025-10-14T08:07:00',1,'matched','2025-10-12T07:22:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (52,69,2,38,'2025-11-08T12:49:00','2025-11-08T13:04:00',2,'cancelled','2025-11-07T12:49:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (53,105,67,22,'2025-11-25T12:10:00','2025-11-25T12:28:00',1,'open','2025-11-22T12:10:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (54,24,9,22,'2025-11-09T10:26:00','2025-11-09T10:55:00',1,'matched','2025-11-07T10:26:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (55,41,32,93,'2025-11-22T13:38:00','2025-11-22T14:27:00',1,'open','2025-11-21T13:38:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (56,52,1,51,'2025-11-03T19:39:00','2025-11-03T20:13:00',1,'open','2025-10-31T19:39:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (57,23,39,42,'2025-11-20T17:12:00','2025-11-20T17:52:00',1,'open','2025-11-17T17:12:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (58,56,38,95,'2025-11-26T16:31:00','2025-11-26T17:17:00',1,'open','2025-11-24T16:31:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (59,70,47,15,'2025-11-18T09:37:00','2025-11-18T10:21:00',1,'matched','2025-11-14T09:37:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (60,1002,94,60,'2025-11-27T09:28:00','2025-11-27T09:56:00',2,'open','2025-11-24T09:28:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (61,89,18,82,'2025-11-01T09:16:00','2025-11-01T10:08:00',1,'matched','2025-10-29T09:16:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (62,11,58,77,'2025-11-12T15:25:00','2025-11-12T16:09:00',2,'cancelled','2025-11-07T15:25:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (63,73,70,5,'2025-11-05T17:57:00','2025-11-05T18:41:00',2,'matched','2025-11-05T17:57:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (64,15,99,32,'2025-11-24T16:22:00','2025-11-24T16:42:00',2,'matched','2025-11-20T16:22:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (65,71,97,43,'2025-11-09T07:00:00','2025-11-09T07:16:00',1,'open','2025-11-04T07:00:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (66,94,72,75,'2025-11-26T15:14:00','2025-11-26T15:52:00',1,'open','2025-11-21T15:14:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (67,90,20,45,'2025-10-14T13:06:00','2025-10-14T13:34:00',1,'cancelled','2025-10-14T13:06:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (68,17,107,94,'2025-10-22T11:19:00','2025-10-22T11:50:00',1,'matched','2025-10-18T11:19:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (69,84,45,61,'2025-10-19T13:46:00','2025-10-19T14:40:00',1,'open','2025-10-17T13:46:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (70,1004,104,91,'2025-10-20T06:54:00','2025-10-20T07:25:00',2,'open','2025-10-19T06:54:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (71,39,67,23,'2025-11-17T08:21:00','2025-11-17T09:06:00',1,'cancelled','2025-11-16T08:21:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (72,71,81,24,'2025-10-24T16:42:00','2025-10-24T17:19:00',1,'open','2025-10-20T16:42:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (73,32,58,76,'2025-11-09T12:00:00','2025-11-09T12:55:00',1,'open','2025-11-04T12:00:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (74,9,74,14,'2025-11-16T08:23:00','2025-11-16T08:53:00',1,'open','2025-11-14T08:23:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (75,88,68,82,'2025-11-02T15:24:00','2025-11-02T16:13:00',1,'matched','2025-10-31T15:24:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (76,63,109,59,'2025-11-21T08:52:00','2025-11-21T09:45:00',2,'matched','2025-11-17T08:52:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (77,12,87,18,'2025-11-02T07:15:00','2025-11-02T07:44:00',1,'open','2025-10-28T07:15:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (78,24,66,50,'2025-11-08T15:08:00','2025-11-08T15:54:00',1,'open','2025-11-05T15:08:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (79,96,89,83,'2025-10-24T14:10:00','2025-10-24T14:51:00',1,'open','2025-10-22T14:10:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (80,77,80,7,'2025-11-04T06:31:00','2025-11-04T06:49:00',1,'matched','2025-10-31T06:31:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (81,90,84,64,'2025-11-08T16:31:00','2025-11-08T17:07:00',2,'matched','2025-11-05T16:31:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (82,27,11,73,'2025-10-14T18:48:00','2025-10-14T19:12:00',1,'open','2025-10-13T18:48:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (83,96,37,22,'2025-11-11T15:47:00','2025-11-11T16:46:00',1,'open','2025-11-11T15:47:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (84,105,15,35,'2025-11-16T19:23:00','2025-11-16T20:07:00',1,'matched','2025-11-13T19:23:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (85,97,28,55,'2025-10-19T17:53:00','2025-10-19T18:44:00',1,'open','2025-10-19T17:53:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (86,77,34,45,'2025-10-18T13:55:00','2025-10-18T14:12:00',1,'open','2025-10-13T13:55:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (87,62,111,91,'2025-11-21T16:27:00','2025-11-21T16:47:00',2,'open','2025-11-20T16:27:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (88,33,104,8,'2025-11-18T11:34:00','2025-11-18T12:01:00',2,'open','2025-11-16T11:34:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (89,51,38,74,'2025-10-30T19:32:00','2025-10-30T20:25:00',1,'open','2025-10-27T19:32:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (90,8,36,111,'2025-11-23T08:44:00','2025-11-23T09:02:00',1,'open','2025-11-21T08:44:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (91,39,98,108,'2025-11-25T12:31:00','2025-11-25T12:50:00',2,'matched','2025-11-20T12:31:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (92,73,49,58,'2025-10-17T16:50:00','2025-10-17T17:49:00',1,'open','2025-10-12T16:50:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (93,47,89,48,'2025-11-11T13:20:00','2025-11-11T14:07:00',1,'matched','2025-11-06T13:20:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (94,13,94,59,'2025-11-22T16:44:00','2025-11-22T17:16:00',1,'matched','2025-11-19T16:44:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (95,34,55,106,'2025-10-26T13:17:00','2025-10-26T13:47:00',1,'open','2025-10-22T13:17:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (96,22,73,110,'2025-11-13T18:21:00','2025-11-13T19:14:00',1,'open','2025-11-08T18:21:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (97,79,3,16,'2025-10-23T13:29:00','2025-10-23T13:39:00',1,'open','2025-10-18T13:29:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (98,22,84,39,'2025-10-23T19:59:00','2025-10-23T20:26:00',1,'matched','2025-10-21T19:59:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (99,12,48,86,'2025-11-23T08:03:00','2025-11-23T08:38:00',2,'open','2025-11-18T08:03:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (100,37,55,84,'2025-10-18T17:06:00','2025-10-18T17:16:00',1,'open','2025-10-17T17:06:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (101,35,67,88,'2025-11-10T06:00:00','2025-11-10T06:54:00',1,'matched','2025-11-07T06:00:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (102,22,62,10,'2025-10-27T12:05:00','2025-10-27T13:01:00',1,'open','2025-10-25T12:05:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (103,52,18,49,'2025-11-30T19:08:00','2025-11-30T19:59:00',2,'open','2025-11-26T19:08:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (104,102,2,79,'2025-11-23T08:28:00','2025-11-23T09:00:00',2,'open','2025-11-18T08:28:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (105,25,53,79,'2025-11-25T13:55:00','2025-11-25T14:18:00',1,'matched','2025-11-24T13:55:00');
+INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (106,18,76,94,'2025-11-06T11:27:00','2025-11-06T11:57:00',1,'open','2025-11-01T11:27:00');
-- RIDE_MATCH
INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (1,3,3,1,11.71,'completed','2025-10-13T13:30:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (2,4,7,1,7.95,'cancelled','2025-10-13T06:00:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (3,8,16,2,14.77,'confirmed','2025-10-15T02:00:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (4,9,16,1,15.16,'no_show','2025-10-13T08:00:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (5,15,2,1,11.01,'completed','2025-10-13T16:45:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (6,21,3,1,10.23,'no_show','2025-10-13T23:45:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (7,7,13,2,11.43,'confirmed','2025-11-24T19:34:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (8,9,37,1,17.37,'completed','2025-11-16T09:34:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (9,10,75,2,9.17,'completed','2025-11-28T14:07:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (10,11,17,2,9.73,'completed','2025-10-19T14:02:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (11,13,60,2,9.77,'completed','2025-10-27T19:58:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (12,14,56,1,17.15,'cancelled','2025-10-22T16:33:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (13,15,56,1,11.57,'completed','2025-11-09T14:31:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (14,16,95,1,12.99,'confirmed','2025-11-02T17:26:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (15,19,45,1,8.37,'cancelled','2025-10-18T12:46:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (16,20,25,2,19.18,'cancelled','2025-11-30T15:46:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (17,22,80,2,8.17,'confirmed','2025-11-27T05:25:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (18,24,66,1,13.54,'completed','2025-11-28T09:48:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (19,25,30,2,8.73,'confirmed','2025-11-29T04:01:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (20,27,95,1,12.23,'confirmed','2025-11-13T17:41:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (21,28,91,2,17.34,'no_show','2025-11-28T13:31:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (22,31,92,1,20.35,'completed','2025-10-14T04:34:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (23,32,59,2,13.04,'confirmed','2025-10-14T12:52:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (24,33,9,2,10.57,'cancelled','2025-10-22T09:12:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (25,35,60,1,12.77,'cancelled','2025-10-18T14:54:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (26,36,71,1,17.93,'confirmed','2025-10-16T14:00:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (27,37,39,1,17.32,'confirmed','2025-11-15T18:02:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (28,38,94,2,25.99,'cancelled','2025-10-17T12:59:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (29,40,1,2,12.32,'confirmed','2025-10-28T11:53:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (30,41,89,1,16.37,'no_show','2025-10-21T18:44:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (31,43,95,1,13.34,'confirmed','2025-10-14T12:17:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (32,45,12,2,13.9,'confirmed','2025-10-18T08:09:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (33,46,30,1,16.37,'completed','2025-10-19T05:00:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (34,47,17,2,11.9,'completed','2025-10-24T09:28:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (35,48,73,1,10.34,'completed','2025-11-17T06:38:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (36,49,34,1,16.04,'no_show','2025-10-18T14:57:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (37,52,35,1,10.83,'no_show','2025-10-25T12:06:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (38,54,20,1,9.85,'completed','2025-11-20T15:12:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (39,55,69,1,12.08,'cancelled','2025-11-26T13:31:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (40,56,34,1,16.05,'confirmed','2025-11-18T07:37:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (41,59,60,2,8.24,'completed','2025-11-12T13:25:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (42,60,9,2,14.75,'confirmed','2025-11-05T16:57:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (43,61,75,2,21.45,'completed','2025-11-24T12:22:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (44,62,60,1,7.95,'cancelled','2025-11-09T07:00:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (45,63,100,1,14.26,'confirmed','2025-11-26T12:14:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (46,64,81,1,12.78,'confirmed','2025-10-14T09:06:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (47,65,10,1,4.91,'confirmed','2025-10-22T08:19:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (48,67,61,2,11.4,'no_show','2025-10-20T06:54:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (49,68,74,1,13.28,'completed','2025-11-17T05:21:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (50,69,88,1,8.99,'confirmed','2025-10-24T15:42:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (51,70,58,1,19.77,'confirmed','2025-11-09T08:00:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (52,73,71,1,18.27,'confirmed','2025-10-24T16:43:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (53,74,26,1,11.56,'cancelled','2025-11-02T06:15:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (54,80,101,2,11.17,'confirmed','2025-11-13T10:32:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (55,81,50,1,8.34,'confirmed','2025-11-30T02:48:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (56,82,104,1,17.96,'confirmed','2025-11-23T15:15:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (57,83,42,2,14.81,'confirmed','2025-10-28T08:51:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (58,84,102,1,21.02,'confirmed','2025-10-21T17:13:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (59,85,103,1,14.92,'no_show','2025-10-23T10:45:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (60,86,59,2,19.89,'completed','2025-11-08T16:03:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (61,87,66,2,22.47,'completed','2025-11-02T15:15:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (62,92,23,1,11.08,'confirmed','2025-11-03T19:43:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (63,94,62,1,10.44,'no_show','2025-10-23T18:59:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (64,96,23,1,14.12,'cancelled','2025-10-18T15:06:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (65,97,19,1,11.98,'no_show','2025-11-10T06:00:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (66,101,85,1,8.39,'confirmed','2025-11-10T17:13:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (67,102,21,1,8.54,'completed','2025-11-02T15:08:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (68,104,72,1,11.34,'no_show','2025-11-07T08:41:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (69,105,76,1,14.94,'confirmed','2025-11-20T05:33:00');
-INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (70,106,94,1,11.15,'completed','2025-11-08T07:34:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (2,7,71,1,13.45,'completed','2025-10-13T15:13:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (3,12,67,1,10.31,'confirmed','2025-10-27T06:29:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (4,13,7,1,11.66,'cancelled','2025-11-24T11:39:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (5,14,15,1,10.93,'completed','2025-11-10T15:43:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (6,15,2,1,15.52,'no_show','2025-11-16T08:48:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (7,16,78,2,11.92,'completed','2025-11-18T08:36:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (8,19,9,1,9.11,'confirmed','2025-11-02T17:26:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (9,21,37,1,20.19,'completed','2025-11-17T11:08:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (10,22,52,1,10.71,'completed','2025-11-05T03:34:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (11,23,15,2,11.5,'completed','2025-11-30T14:46:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (12,25,63,2,14.92,'completed','2025-11-27T09:25:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (13,26,56,1,15.63,'cancelled','2025-11-23T11:19:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (14,27,56,1,8.51,'completed','2025-11-28T13:48:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (15,28,95,1,15.18,'confirmed','2025-10-27T17:09:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (16,31,66,2,15.7,'cancelled','2025-11-04T06:08:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (17,32,17,1,19.58,'cancelled','2025-10-17T19:42:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (18,34,104,1,12.54,'completed','2025-10-14T04:34:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (19,36,14,2,19.0,'completed','2025-10-22T09:12:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (20,37,30,2,12.59,'confirmed','2025-10-18T11:25:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (21,40,95,1,13.63,'confirmed','2025-10-19T20:33:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (22,41,61,1,14.37,'no_show','2025-10-15T19:04:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (23,44,78,2,10.37,'confirmed','2025-11-29T06:50:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (24,45,10,1,13.86,'confirmed','2025-11-09T09:53:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (25,46,103,1,14.6,'confirmed','2025-10-14T13:17:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (26,47,60,1,10.62,'cancelled','2025-10-24T14:37:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (27,48,71,1,12.01,'confirmed','2025-11-27T05:05:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (28,49,39,1,19.81,'confirmed','2025-11-02T09:06:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (29,50,64,1,18.53,'cancelled','2025-10-19T04:05:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (30,52,1,2,12.32,'confirmed','2025-11-08T08:49:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (31,53,89,1,15.09,'no_show','2025-11-25T12:10:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (32,55,95,1,15.9,'confirmed','2025-11-22T09:38:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (33,57,8,1,20.12,'confirmed','2025-11-20T15:12:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (34,58,30,1,18.15,'completed','2025-11-26T14:31:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (35,59,96,1,10.57,'confirmed','2025-11-18T08:37:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (36,61,76,1,17.57,'completed','2025-11-01T08:16:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (37,62,53,2,19.43,'no_show','2025-11-12T12:25:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (38,65,35,1,15.48,'no_show','2025-11-09T06:00:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (39,67,20,1,12.77,'completed','2025-10-14T11:06:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (40,68,69,1,15.07,'cancelled','2025-10-22T08:19:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (41,69,34,1,22.67,'confirmed','2025-10-19T11:46:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (42,72,42,1,9.38,'completed','2025-10-24T14:42:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (43,73,7,1,11.0,'confirmed','2025-11-09T11:00:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (44,74,52,1,17.27,'completed','2025-11-16T04:23:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (45,75,60,1,5.27,'cancelled','2025-11-02T15:24:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (46,76,44,2,11.43,'cancelled','2025-11-21T06:52:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (47,79,76,1,13.15,'cancelled','2025-10-24T14:10:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (48,81,83,2,13.71,'confirmed','2025-11-08T14:31:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (49,82,60,1,4.91,'no_show','2025-10-14T15:48:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (50,84,4,1,13.76,'completed','2025-11-16T17:23:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (51,85,24,1,10.6,'completed','2025-10-19T16:53:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (52,86,80,1,15.38,'completed','2025-10-18T10:55:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (53,87,104,2,13.1,'confirmed','2025-11-21T13:27:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (54,88,36,2,17.37,'cancelled','2025-11-18T10:34:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (55,94,70,1,15.35,'confirmed','2025-11-22T12:44:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (56,95,50,1,9.81,'confirmed','2025-10-26T09:17:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (57,96,104,1,16.45,'confirmed','2025-11-13T15:21:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (58,97,99,1,11.63,'confirmed','2025-10-23T12:29:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (59,98,85,1,11.05,'completed','2025-10-23T19:59:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (60,101,103,1,13.01,'no_show','2025-11-10T05:00:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (61,102,76,1,14.0,'completed','2025-10-27T10:05:00');
+INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (62,104,68,2,18.31,'completed','2025-11-23T04:28:00');
-- RATING
INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (1,1,2,12,5,'Friendly','2025-10-13T15:30:00');
-INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (3,8,9,4,5,'Great ride','2025-10-13T04:45:00');
-INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (4,8,94,59,1,'Car was clean','2025-11-16T12:34:00');
-INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (5,9,33,53,5,'Helpful with bags','2025-11-30T06:07:00');
-INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (6,10,51,85,5,'Would ride again','2025-10-20T00:02:00');
-INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (7,11,31,48,5,'On time','2025-10-28T16:58:00');
-INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (8,13,37,53,3,'Great ride','2025-11-11T12:31:00');
-INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (9,18,94,78,4,'On time','2025-11-29T14:48:00');
-INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (10,22,96,48,5,'Driver was late','2025-10-14T09:34:00');
-INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (11,33,76,53,4,'Driver was late','2025-10-19T06:00:00');
-INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (12,34,26,85,5,'Would ride again','2025-10-24T20:28:00');
-INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (13,38,31,54,5,'Car was clean','2025-11-21T16:12:00');
-INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (14,41,22,48,5,'Helpful with bags','2025-11-14T02:25:00');
-INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (15,43,26,53,4,'Friendly driver','2025-11-25T17:22:00');
-INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (16,49,52,70,4,'On time','2025-11-17T07:21:00');
-INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (17,60,25,59,2,'Great ride','2025-11-09T14:03:00');
-INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (18,61,52,78,5,'Driver was late','2025-11-02T20:15:00');
-INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (19,67,76,49,3,'Great ride','2025-11-03T13:08:00');
-INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (20,70,76,100,4,'Would ride again','2025-11-10T05:34:00');
+INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (2,2,5,55,1,'Car was clean','2025-10-14T11:13:00');
+INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (3,5,14,66,5,'Great ride','2025-11-11T06:43:00');
+INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (4,7,37,5,5,'Would ride again','2025-11-18T09:36:00');
+INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (5,9,101,16,4,'Helpful with bags','2025-11-17T20:08:00');
+INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (6,10,12,1001,5,'Helpful with bags','2025-11-06T00:34:00');
+INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (7,11,1004,66,5,'Driver was late','2025-12-01T00:46:00');
+INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (8,12,39,101,4,'Helpful with bags','2025-11-28T09:25:00');
+INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (9,18,84,1003,2,'Would ride again','2025-10-15T18:34:00');
+INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (10,19,15,43,5,'Friendly driver','2025-10-22T20:12:00');
+INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (11,34,56,53,5,'Driver was late','2025-11-27T22:31:00');
+INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (12,36,89,27,5,'Car was clean','2025-11-03T05:16:00');
+INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (13,42,71,98,5,'Driver was late','2025-10-26T04:42:00');
+INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (14,44,9,1001,5,'Driver was late','2025-11-18T01:23:00');
+INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (15,50,105,6,5,'Great ride','2025-11-18T14:23:00');
+INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (16,52,77,4,1,'Car was clean','2025-10-18T13:55:00');
+INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (17,59,22,49,5,'Helpful with bags','2025-10-25T11:59:00');
+INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (18,61,22,27,5,'Would ride again','2025-10-27T20:05:00');
+INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (19,62,102,43,5,'On time','2025-11-24T01:28:00');
+
+-- MESSAGE_THREAD
+INSERT INTO MESSAGE_THREAD (thread_id,user1_id,user2_id,ride_match_id,created_at) VALUES (1,1,2,NULL,'2025-11-30 19:25:00');
+
+-- MESSAGE
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (1,1,2,'Hey, are we still on for 5:30 PM?','2025-11-30 03:03:42');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (2,1,1,'Yes, I will be there in 10 minutes.','2025-11-30 03:03:42');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (3,1,2,'Hey Quincy, are we still on for 5:30 PM?','2025-11-30 19:29:48');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (4,1,1,'Yes absolutely! Leaving now.','2025-11-30 19:29:48');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (5,1,2,'Perfect, see you soon!','2025-11-30 19:29:48');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (6,1,1,'Hey Abdul, do you still need a ride tomorrow?','2025-11-30 19:29:51');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (7,1,2,'Yes please! Around 9 AM if possible.','2025-11-30 19:29:51');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (8,1,1,'I ll be there.','2025-11-30 19:29:51');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (9,1,2,'Got you!','2025-11-30 19:29:51');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (10,1,1,'Thanks for the ride earlier!','2025-11-30 19:29:55');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (11,1,2,'Anytime man!','2025-11-30 19:29:55');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (12,1,1,'Left you a 5-star rating too :)','2025-11-30 19:29:55');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (13,1,2,'Hey bro, heading out soon?','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (14,1,1,'Yeah give me 5 minutes.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (15,1,2,'Bet, take your time.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (16,1,1,'Traffic is crazy today.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (17,1,2,'Fr bro, Drexel roads are wild.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (18,1,1,'Want to grab food after?','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (19,1,2,'m down for halal cart.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (20,1,1,'Say less.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (21,1,2,'Almost downstairs.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (22,1,1,'I see you.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (23,1,2,'Don t forget the aux today.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (24,1,1,'Haha alright.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (25,1,2,'Let s go.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (26,1,1,'On my way now.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (27,1,2,'You driving today?','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (28,1,1,'Yeah I got it.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (29,1,2,'Fire.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (30,1,1,'Good morning! Still need a ride?','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (31,1,2,'Yes please! Appreciate you.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (32,1,1,'Leaving in 10.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (33,1,2,'ll meet you outside.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (34,1,1,'Perfect.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (35,1,2,'Did you do the homework for CS 281?','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (36,1,1,'Barely, that class is wild.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (37,1,2,'Facts bro.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (38,1,1,'ll quiz you in the car lol.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (39,1,2,'Bet.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (40,1,1,'m by the curb.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (41,1,2,'Coming now.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (42,1,1,'You want coffee?','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (43,1,2,'Nah I m good, thanks.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (44,1,1,'Alright see you.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (45,1,2,'Almost there.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (46,1,1,'Thanks again for covering me last ride bro.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (47,1,2,'No problem at all.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (48,1,1,'You good for later today?','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (49,1,2,'Yep, same time as usual.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (50,1,1,'Bet.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (51,1,2,'You wanna stop at Wawa after?','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (52,1,1,'Always bro.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (53,1,2,'Haha say less.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (54,1,1,'You see the game last night?','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (55,1,2,'Bro Embiid is insane.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (56,1,1,'MVP season.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (57,1,2,'100%.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (58,1,1,'Heading out now.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (59,1,2,'m already outside.','2025-11-30 19:32:38');
+INSERT INTO MESSAGE (message_id,thread_id,sender_id,body,sent_at) VALUES (60,1,1,'Coming.','2025-11-30 19:32:38');
diff --git a/database/ran_sql_cmds_in_script.sql b/database/ran_sql_cmds_in_script.sql
new file mode 100644
index 0000000..942e005
--- /dev/null
+++ b/database/ran_sql_cmds_in_script.sql
@@ -0,0 +1,173 @@
+-- =====================================================================
+-- SQL used at runtime by the Find-My-Ride application
+-- =====================================================================
+
+-- ---------------------------------------------------------------------
+-- Source: app/composeApp/src/androidMain/kotlin/com/example/demo/AndroidAuthRepository.kt
+-- ---------------------------------------------------------------------
+
+-- Login: check email + password
+SELECT user_id, email, username, role
+FROM "USER"
+WHERE email = ? AND password_hash = ?;
+
+-- Check if email already exists (used in registration & login)
+SELECT 1 FROM "USER" WHERE email = ? LIMIT 1;
+
+-- Register new user (default role = 'rider')
+INSERT INTO "USER"(email, username, password_hash, role)
+VALUES(?, ?, ?, 'rider');
+
+-- (Second use of the same existence check)
+SELECT 1 FROM "USER" WHERE email = ? LIMIT 1;
+
+
+
+-- ---------------------------------------------------------------------
+-- Source: app/composeApp/src/androidMain/kotlin/com/example/demo/AndroidMessagesRepository.kt
+-- Was used for debugging errors in script execution
+-- ---------------------------------------------------------------------
+
+-- Seed message threads (demo data)
+INSERT INTO MESSAGE_THREAD (thread_id, user1_id, user2_id)
+VALUES
+ (1, 1, 2),
+ (2, 1, 3),
+ (3, 1, 4);
+
+-- Seed messages - Thread 1: Abdul <-> Quincy
+INSERT INTO MESSAGE (thread_id, sender_id, body) VALUES
+ (1, 1, 'Hey Quincy, are we still on for 5:30 PM?'),
+ (1, 2, 'Yes! I''ll be there in 10 minutes.'),
+ (1, 1, 'Perfect, see you soon.');
+
+-- Seed messages - Thread 2: Abdul <-> Ame
+INSERT INTO MESSAGE (thread_id, sender_id, body) VALUES
+ (2, 3, 'Hey Abdul, do you still need a ride tomorrow?'),
+ (2, 1, 'Yeah! Morning around 9 would be amazing.'),
+ (2, 3, 'Got you, I''ll swing by then.');
+
+-- Seed messages - Thread 3: Abdul <-> Kennan
+INSERT INTO MESSAGE (thread_id, sender_id, body) VALUES
+ (3, 1, 'Thanks again for the last ride!'),
+ (3, 4, 'No problem, happy to help.'),
+ (3, 1, 'I left you a 5-star rating too :)');
+
+-- Load all message threads for the logged-in user (with last message preview)
+SELECT
+ t.thread_id,
+ CASE
+ WHEN t.user1_id = ? THEN u2.username
+ ELSE u1.username
+ END AS contact_name,
+ COALESCE(last_msg.body, 'No messages yet') AS last_message,
+ last_msg.sent_at AS last_sent_at
+FROM MESSAGE_THREAD t
+ JOIN "USER" u1 ON t.user1_id = u1.user_id
+ JOIN "USER" u2 ON t.user2_id = u2.user_id
+ LEFT JOIN MESSAGE last_msg ON last_msg.message_id = (
+ SELECT m.message_id
+ FROM MESSAGE m
+ WHERE m.thread_id = t.thread_id
+ ORDER BY m.sent_at DESC, m.message_id DESC
+ LIMIT 1
+ )
+WHERE t.user1_id = ? OR t.user2_id = ?
+ORDER BY
+ (last_sent_at IS NULL),
+ last_sent_at DESC,
+ t.thread_id DESC;
+
+-- Load all messages inside a specific thread
+SELECT message_id, sender_id, body, sent_at
+FROM MESSAGE
+WHERE thread_id = ?
+ORDER BY sent_at ASC, message_id ASC;
+
+-- Insert a new chat message
+INSERT INTO MESSAGE(thread_id, sender_id, body)
+VALUES (?, ?, ?);
+
+-- Get sent_at for a specific message (used after insert)
+SELECT sent_at
+FROM MESSAGE
+WHERE message_id = ?;
+
+
+
+-- ---------------------------------------------------------------------
+-- Source: app/composeApp/src/androidMain/kotlin/com/example/demo/AndroidProfileRepository.kt
+-- ---------------------------------------------------------------------
+
+-- Load main profile info for the logged-in user
+SELECT username, email, phone_number, rating_avg
+FROM "USER"
+WHERE user_id = ?
+ LIMIT 1;
+
+-- Load all vehicles owned by logged-in user
+SELECT vehicle_id, make, model, color, plate, seats_total, year, fun_fact
+FROM "VEHICLE"
+WHERE owner_user_id = ?
+ORDER BY vehicle_id;
+
+-- Update profile fields (username, email, phone number)
+UPDATE "USER"
+SET username = ?, email = ?, phone_number = ?
+WHERE user_id = ?;
+
+-- Get all vehicle IDs for current user (used to compute inserts/updates/deletes)
+SELECT vehicle_id
+FROM "VEHICLE"
+WHERE owner_user_id = ?;
+
+-- Update an existing vehicle
+UPDATE "VEHICLE"
+SET make = ?, model = ?, color = ?, plate = ?,
+ seats_total = ?, year = ?, fun_fact = ?
+WHERE vehicle_id = ?;
+
+-- Insert a new vehicle
+INSERT INTO "VEHICLE"(
+ vehicle_id, owner_user_id, make, model, color,
+ plate, seats_total, year, fun_fact
+) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);
+
+-- Delete a vehicle
+DELETE FROM "VEHICLE" WHERE vehicle_id = ?;
+
+
+
+-- ---------------------------------------------------------------------
+-- Source: app/composeApp/src/androidMain/kotlin/com/example/demo/AndroidRideRepository.kt
+-- ---------------------------------------------------------------------
+
+-- Load open ride offers, joined with location names for origin/destination
+SELECT
+ o.offer_id,
+ o.driver_id,
+ o.vehicle_id,
+ lo_from.name AS from_name,
+ lo_to.name AS to_name,
+ o.depart_at,
+ o.seats_available,
+ o.price_base
+FROM RIDE_OFFER o
+ JOIN LOCATION lo_from ON o.original_location_id = lo_from.location_id
+ JOIN LOCATION lo_to ON o.dest_location_id = lo_to.location_id
+WHERE o.status = 'open'
+ORDER BY o.depart_at ASC;
+
+
+
+-- ---------------------------------------------------------------------
+-- Source: app/composeApp/src/androidMain/kotlin/com/example/demo/RideShareDbHelper.kt
+-- (Legacy/demo local table not tied to main project schema but just included here for completeness)
+-- ---------------------------------------------------------------------
+
+CREATE TABLE IF NOT EXISTS rides(
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ pickup TEXT NOT NULL,
+ dropoff TEXT NOT NULL,
+ ride_time TEXT NOT NULL
+);
diff --git a/database/schema.sql b/database/schema.sql
index e925393..0e47d49 100644
--- a/database/schema.sql
+++ b/database/schema.sql
@@ -1,6 +1,6 @@
-PRAGMA foreign_keys = ON; -- Enforces foreign key constraints
--- USER Table For Users
+PRAGMA foreign_keys = ON;
+
CREATE TABLE "USER" (
user_id INTEGER PRIMARY KEY,
email TEXT NOT NULL UNIQUE,
@@ -12,7 +12,6 @@ CREATE TABLE "USER" (
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
--- VEHICLE Table for Vehicles
CREATE TABLE "VEHICLE" (
vehicle_id INTEGER PRIMARY KEY,
owner_user_id INTEGER NOT NULL,
@@ -21,19 +20,17 @@ CREATE TABLE "VEHICLE" (
color TEXT,
plate TEXT NOT NULL UNIQUE,
seats_total INTEGER NOT NULL CHECK (seats_total BETWEEN 1 AND 8),
- year INTEGER CHECK (year BETWEEN 1900 AND 2100), -- We did 2100 because why not keeping it future proof
- fun_fact TEXT, -- Just for fun to add some personality for each vehicle
+ year INTEGER CHECK (year BETWEEN 1900 AND 2100),
+ fun_fact TEXT,
FOREIGN KEY (owner_user_id) REFERENCES "USER"(user_id)
);
--- LOCATION Table for Locations
CREATE TABLE "LOCATION" (
location_id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
address TEXT NOT NULL
);
--- RIDE_OFFER Table for Ride Offers
CREATE TABLE "RIDE_OFFER" (
offer_id INTEGER PRIMARY KEY,
driver_id INTEGER NOT NULL,
@@ -52,7 +49,6 @@ CREATE TABLE "RIDE_OFFER" (
FOREIGN KEY (dest_location_id) REFERENCES "LOCATION"(location_id)
);
--- RIDE_REQUEST Table for Ride Requests
CREATE TABLE "RIDE_REQUEST" (
request_id INTEGER PRIMARY KEY,
rider_id INTEGER NOT NULL,
@@ -69,7 +65,6 @@ CREATE TABLE "RIDE_REQUEST" (
CHECK (latest_pickup IS NULL OR latest_pickup >= earliest_pickup)
);
--- RIDE_MATCH Table for Matches between Requests and Offers
CREATE TABLE "RIDE_MATCH" (
match_id INTEGER PRIMARY KEY,
request_id INTEGER NOT NULL,
@@ -83,7 +78,6 @@ CREATE TABLE "RIDE_MATCH" (
UNIQUE (request_id, offer_id)
);
--- RATING Table for User Ratings
CREATE TABLE "RATING" (
rating_id INTEGER PRIMARY KEY,
match_id INTEGER NOT NULL,
@@ -98,3 +92,25 @@ CREATE TABLE "RATING" (
CHECK (from_user_id <> to_user_id),
UNIQUE (match_id, from_user_id, to_user_id)
);
+
+CREATE TABLE "MESSAGE_THREAD" (
+ thread_id INTEGER PRIMARY KEY,
+ user1_id INTEGER NOT NULL,
+ user2_id INTEGER NOT NULL,
+ ride_match_id INTEGER,
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
+ FOREIGN KEY (user1_id) REFERENCES "USER"(user_id),
+ FOREIGN KEY (user2_id) REFERENCES "USER"(user_id),
+ FOREIGN KEY (ride_match_id) REFERENCES "RIDE_MATCH"(match_id),
+ CHECK (user1_id <> user2_id)
+);
+
+CREATE TABLE "MESSAGE" (
+ message_id INTEGER PRIMARY KEY,
+ thread_id INTEGER NOT NULL,
+ sender_id INTEGER NOT NULL,
+ body TEXT NOT NULL,
+ sent_at TEXT NOT NULL DEFAULT (datetime('now')),
+ FOREIGN KEY (thread_id) REFERENCES "MESSAGE_THREAD"(thread_id),
+ FOREIGN KEY (sender_id) REFERENCES "USER"(user_id)
+);
diff --git a/demo/.idea/workspace.xml b/demo/.idea/workspace.xml
new file mode 100644
index 0000000..3b00bca
--- /dev/null
+++ b/demo/.idea/workspace.xml
@@ -0,0 +1,242 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {}
+ {
+ "isMigrated": true
+}
+ {
+ "associatedIndex": 1
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ true
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+ true
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ true
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1762914791700
+
+
+ 1762914791700
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/BlackBoard_Deliverables/Deliverable_4/live_demo/Live_Demo.webm b/docs/BlackBoard_Deliverables/Deliverable_4/live_demo/Live_Demo.webm
new file mode 100644
index 0000000..24d05ad
Binary files /dev/null and b/docs/BlackBoard_Deliverables/Deliverable_4/live_demo/Live_Demo.webm differ
diff --git a/docs/BlackBoard_Deliverables/Deliverable_4/personal_contributions/Mustafa-Individual Deliverable 04.pdf b/docs/BlackBoard_Deliverables/Deliverable_4/personal_contributions/Mustafa-Individual Deliverable 04.pdf
new file mode 100644
index 0000000..fc27a5e
Binary files /dev/null and b/docs/BlackBoard_Deliverables/Deliverable_4/personal_contributions/Mustafa-Individual Deliverable 04.pdf differ
diff --git a/docs/BlackBoard_Deliverables/Deliverable_4/personal_contributions/Samii_Individual_Deliverable.pdf b/docs/BlackBoard_Deliverables/Deliverable_4/personal_contributions/Samii_Individual_Deliverable.pdf
new file mode 100644
index 0000000..3deaa7e
Binary files /dev/null and b/docs/BlackBoard_Deliverables/Deliverable_4/personal_contributions/Samii_Individual_Deliverable.pdf differ
diff --git a/docs/BlackBoard_Deliverables/Deliverable_4/personal_reflections/Final Reflection.pdf b/docs/BlackBoard_Deliverables/Deliverable_4/personal_reflections/Final Reflection.pdf
new file mode 100644
index 0000000..2a83dac
Binary files /dev/null and b/docs/BlackBoard_Deliverables/Deliverable_4/personal_reflections/Final Reflection.pdf differ
diff --git a/docs/BlackBoard_Deliverables/Deliverable_4/personal_reflections/Samii_Reflection.pdf b/docs/BlackBoard_Deliverables/Deliverable_4/personal_reflections/Samii_Reflection.pdf
new file mode 100644
index 0000000..493da6b
Binary files /dev/null and b/docs/BlackBoard_Deliverables/Deliverable_4/personal_reflections/Samii_Reflection.pdf differ
diff --git a/docs/BlackBoard_Deliverables/Deliverable_4/presentation/FindMyRide Final Presentation.pptx b/docs/BlackBoard_Deliverables/Deliverable_4/presentation/FindMyRide Final Presentation.pptx
new file mode 100644
index 0000000..1e29579
Binary files /dev/null and b/docs/BlackBoard_Deliverables/Deliverable_4/presentation/FindMyRide Final Presentation.pptx differ
diff --git a/docs/dev_journal/Kennan.md b/docs/dev_journal/Kennan.md
index a772d0d..01e91bf 100644
--- a/docs/dev_journal/Kennan.md
+++ b/docs/dev_journal/Kennan.md
@@ -1,3 +1,52 @@
+# 11/20/2025
+
+Experienced a bunch of issues with trying to build and run the app.
+
+Solution: Deleted the .gradle folder in the app subdirectory and forced it to be rebuilt.
+
+# 11/16/2025
+
+## Development Strategy
+
+As we team we focused and came up with the strategy with 3 Phases.
+
+We are following Three Phase Strategy:
+
+UI Development -> UI Connection Development -> Frontend to Backend Database Connection
+
+And [ChatGPT](https://chatgpt.com/share/691a8a83-69c0-800e-be1c-aa304a8a901f) verified this is the correct process that modern companies use.
+
+Following paging structure with react where we have folders for our features.
+
+## Importing Icons
+
+You should use your own SVG/XML files if you want to import icons onto the UI for the screens.
+
+ 1. Get your icons - Download the icons you want as .svg or .xml (Android Vector) files. (Sites like Heroicons or Phosphor Icons are good sources).
+
+ 2. Place them in the resources folder Go to composeApp/src/commonMain/composeResources/drawable. Paste your files there (e.g., ic_calendar.xml, ic_location.xml).
+
+ 3. Update the code to use painterResource
+
+EXAMPLE CALL using painterResource:
+icon = painterResource(Res.drawable.ic_calendar)
+
+#### 11/21/2025 Update: ANDROID DOES NOT SUPPORT SVG FILES
+
+Here's how to convert them to XML before use:
+
+ 1. In the Project view (left panel), right-click on your commonMain/composeResources/drawable folder.
+
+ 2. Select New > Vector Asset.
+
+ 3. In the "Asset Type" section, choose Local file (SVG, PSD).
+
+ 4. Click the folder icon next to Path and select your original SVG file.
+
+ 5. Click Next and then Finish.
+
+This will create a new .xml file in that folder. Delete the old .svg file so there is no confusion.
+
# 11/2/2025
- Initialize the SQLite Database
diff --git a/docs/dev_journal/Samii.md b/docs/dev_journal/Samii.md
index 9d5d8dd..f3482a9 100644
--- a/docs/dev_journal/Samii.md
+++ b/docs/dev_journal/Samii.md
@@ -1,3 +1,141 @@
+# 11/16/2025
+
+## Development Strategy
+
+As we team we focused and came up with the strategy with 3 Phases.
+
+We are following Three Phase Strategy:
+
+UI Development -> UI Connection Development -> Frontend to Backend Database Connection
+
+And [ChatGPT](https://chatgpt.com/share/691a8a83-69c0-800e-be1c-aa304a8a901f) verified this is the correct process that modern companies use.
+
+Following paging structure with react where we have folders for our features.
+
+- I'm make the structure
+
+```txt
+feature:
+ - auth/ # all authentication related stuff
+ - rides/ # all main ride related stuff
+ - .../ # insert other features
+```
+
+- I added a thing to allow colors to be used all over by every file in this project.
+ - I think Ken is also asking me about my hex color irl so idk how to think about that. /s
+
+Now need to focus on getting the code for login page to make sense.
+
+- I asked a question but he left on delivered.
+
+## KMP Notes:
+
+### @Composable
+- `@Composable` marks this as a composable fucntion - describing UI in Compose
+ - annotation in Jetpack Compose (and Compose Multiplatform)
+ - marks a function as part of the UI tree:
+ - Tells the compiler "this function describes teh UI, not normal code"
+ - It gets compiled into a declarative UI node
+ - Compose knows when to recompse it when states change
+ - Describes how its drawn on Android, iOS, DeskTop, and Web
+ - Compose can optimize it, skip re-rendering, or redraw specific parts.
+ - Without it, function is just normal Kotlin function; Compose cannot treat it as UI.
+
+### @Composable Function Signature
+Example Code:
+```kotlin
+@Composable
+fun LoginScreen(
+ onLoginClick: (email: String, password: string) -> Unit = { _, _ -> },
+ onForgotPasswordClick: () -> Unit = {},
+ onSignUpClick: () -> Unit = {}
+) {
+ //...
+}
+```
+- `@Composable`: Marks this as a composable function -> describes UI in Compose.
+- `fun LoginScreen(..)`: This is the login screen component.
+- `onLoginClick`: A callback that takes an email and password. Default will be an empty lambda that does nothing
+- `onForgotPasswordClick`: Callback when "Forgot password?" is clicked, default does nothing.
+- `onSignUpClick`: Callback when "Sign Up now" is clicked, default is nothing.
+- The defaults make it easy to preview or use this composable without wiring up real logic immediately.
+- `_` == means "I don't care about this input"
+
+#### WHY it looks weird
+- The function parameters are functions -- NOT Integers, Strings, or Booleans.
+
+Example of a normal param:
+```kotlin
+fun doSomething(name: String) {}
+```
+
+Kotlin also allows:
+```kotlin
+fun doSomething(callback: () -> Unit)
+
+// Which means callback is a function with no parameters that return nothing.
+```
+
+#### Why do this?
+Because your UI composable should NOT contain business logic.
+
+Example:
+Inside your screen you will have buttons like:
+```kotlin
+Button(onClick = { onLoginClick(email, password) }) { //... }
+```
+
+But the actual logic (like Firebase login, Drexel API login, etc.) will come from parent screen, not this UI.
+
+### Local state (email & password)
+Example:
+```kotlin
+var email by remember { mutableStateOf("")
+var password by remember { mutableStateOf("") }
+```
+- `remember { mutableStateOf("") }`: Create state that Compose tracks and remember between recompositions.
+
+- Ken said I'm not the right type of asian so I'm not invited to the asian friendsgiving
+
+# 11/10/2025
+
+Tried developing more not working
+
+- SDK broken nothing works.
+- Kennan said something weird about my skin-color.
+
+# 10/5/2025
+
+- Developing Kotlin Multiplatform
+
+Got it to set up.
+
+I went to this page to download the plugin
+
+[Plugin Link](https://plugins.jetbrains.com/plugin/14936-kotlin-multiplatform?_gl=1%2A5nmlkh%2A_gcl_aw%2AR0NMLjE3NTk2ODk2MzkuQ2owS0NRandyb2pIQmhEZEFSSXNBSmRFSl9jbzlqd2wtaE01ZUlSM3RsbnQ4OWZlRGZjREx3MGNhT3AyYTQ1MlFwa3dyUVJfZWV3SzNUZ2FBc3hGRUFMd193Y0I.%2A_gcl_au%2AMTU1OTY1NjY0OC4xNzU5NTEyNjU4LjQ2NTY5OTIyOS4xNzU5Njg5OTU3LjE3NTk2ODk5NTY.%2AFPAU%2AMjk4MjQwNjQwLjE3NTk1MTI2Nzk.%2A_ga%2AMTk1NjQ5NDYzMC4xNzU5NTEyNjU5%2A_ga_9J976DJZ68%2AczE3NTk2ODk0NjMkbzIkZzEkdDE3NTk2OTAxNTgkajU5JGwwJGgw)
+
+Ignore the rating ;-)
+
+I downloaded the zip package
+
+Nvm tried pressing button computer crashed using some middle zip file they afftered.
+
+# 10/3/2025
+
+## Pipeline Stuff
+
+- Create first pipeline to work off of.
+ - Simple and only focuses on creating test cases for the files we start with.
+
+- Updating README so it will be easy for us to follow professional standards when it comes to commits and etc.
+
+- Trying this new pipeline format that adds this weekly security check.
+ - It's new so I'm excited to see if I did it right and what I have done, so let's hope for the best.
+
+## Creating Kotlin Base for Multiplatform
+
+[Documentation Link](https://www.jetbrains.com/help/kotlin-multiplatform-dev/quickstart.html#set-up-the-environment)
+
# 11/2/2025
- Initialize the SQLite Database
diff --git a/identifier.sqlite b/identifier.sqlite
new file mode 100644
index 0000000..e69de29
diff --git a/script_generation/generate_seed.py b/script_generation/generate_seed.py
deleted file mode 100644
index 920e60f..0000000
--- a/script_generation/generate_seed.py
+++ /dev/null
@@ -1,309 +0,0 @@
-#!/usr/bin/env python3
-"""
-generate_seed.py
-Generates sql/seed_full.sql with realistic synthetic data for Find-My-Ride.
-"""
-
-import os, random, datetime
-from pathlib import Path
-
-random.seed(42)
-
-OUT_DIR = Path(".")
-OUT_DIR.mkdir(parents=True, exist_ok=True)
-SQL_PATH = OUT_DIR / "populate.sql"
-
-# --- keep existing sample rows ---
-users_existing = [
- # (user_id, full_name_or_username, email, phone, role, rating_avg, created_at)
- (1, "abdul_bookwala", "abdul.bookwala1@example.edu", "8569861234", "driver", 4.72, "2025-10-02T00:00:00"),
- (2, "quincy_lu", "quincy.lu2@example.edu", "8569862334", "driver", 4.67, "2025-10-09T00:00:00"),
- (3, "ame_shabuse", "ame.shabuse3@example.edu", "8569861334", "rider", 4.55, "2025-10-02T00:00:00"),
- (4, "ame_lu", "ame.lu4@example.edu", "8569831234", "both", 4.54, "2025-10-10T00:00:00"),
- (5, "kennan_shajid", "kennan.shajid5@example.edu", "8569861234", "both", 4.70, "2025-10-01T00:00:00"),
-]
-
-vehicles_existing = [
- # (vehicle_id, owner_user_id, make, model, color, plate, seats_total, year, fun_fact)
- (1,2,"Tesla","Model Y","Blue","NJ-7909",7,2025,"n/a"),
- (2,5,"Tesla","Model 3","Gray","NJ-6737",5,2024,"n/a"),
- (3,6,"Toyota","Camry","Silver","NJ-8767",4,2015,"n/a"),
- (4,8,"Chevrolet","Malibu","Red","NJ-8301",5,2021,"n/a"),
- (5,9,"Toyota","Camry","Blue","NJ-6823",4,2024,"n/a"),
- (6,14,"Honda","Civic","White","NJ-7519",5,2022,"n/a"),
-]
-
-locations_existing = [
- (1,"Drexel Main Building","3141 Chestnut St"),
- (2,"Korman Center","3220-26 Woodland Walk"),
- (3,"University Crossings","3175 JFK Blvd"),
- (4,"30th Street Station","2955 Market St"),
- (5,"Queen Lane Campus","2900 Queen Ln"),
- (6,"Vidas Athletic Complex","43rd & Powelton"),
- (7,"Cira Green","129 S 30th St"),
- (8,"Wawa 34th Market","3400 Market St"),
-]
-
-ride_offers_existing = [
- # (offer_id,driver_id,vehicle_id,origin_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at)
- (1,5,3,3,8,"2025-10-16T01:45:00",2,4.17,0.72,"closed","2025-10-15T01:45:00"),
- (2,1,6,6,10,"2025-10-15T18:45:00",1,5.31,0.86,"closed","2025-10-14T18:45:00"),
- (3,12,1,2,3,"2025-10-14T13:00:00",2,4.63,1.13,"open","2025-10-13T13:00:00"),
- (4,6,2,3,8,"2025-10-14T17:45:00",3,8.52,0.9,"closed","2025-10-13T17:45:00"),
-]
-
-ride_requests_existing = [
- # (request_id, rider_id, pickup_location_id, dropoff_location_id, earliest_pickup, latest_pickup, seats_needed, status, created_at)
- (1,11,7,10,"2025-10-14T19:05:00","2025-10-14T19:45:00",1,"cancelled","2025-10-13T19:15:00"),
- (2,10,6,8,"2025-10-14T13:15:00","2025-10-14T13:25:00",1,"open","2025-10-13T13:15:00"),
- (3,7,1,9,"2025-10-14T07:20:00","2025-10-14T07:40:00",1,"matched","2025-10-13T07:30:00"),
- (4,13,6,7,"2025-10-13T23:50:00","2025-10-14T00:30:00",2,"matched","2025-10-13T00:00:00"),
- (5,10,7,2,"2025-10-15T21:45:00","2025-10-15T21:55:00",1,"open","2025-10-14T21:45:00"),
- (6,11,3,2,"2025-10-15T03:00:00","2025-10-15T03:10:00",1,"cancelled","2025-10-14T03:00:00"),
-]
-
-matches_existing = [
- # (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at)
- (1,3,3,1,11.71,"completed","2025-10-13T13:30:00"),
- (2,4,7,1,7.95,"cancelled","2025-10-13T06:00:00"),
- (3,8,16,2,14.77,"confirmed","2025-10-15T02:00:00"),
- (4,9,16,1,15.16,"no_show","2025-10-13T08:00:00"),
- (5,15,2,1,11.01,"completed","2025-10-13T16:45:00"),
- (6,21,3,1,10.23,"no_show","2025-10-13T23:45:00"),
-]
-
-ratings_existing = [
- # (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at)
- (1,1,2,12,5,"Friendly","2025-10-13T15:30:00"),
- (3,8,9,4,5,"Great ride","2025-10-13T04:45:00"),
-]
-
-# --- generation parameters ---
-EXTRA = 100 # number of rows to add per table
-start_user_id = max(u[0] for u in users_existing) + 1 # 6
-start_vehicle_id = max((v[0] for v in vehicles_existing), default=0) + 1
-start_location_id = max(l[0] for l in locations_existing) + 1
-start_offer_id = max(o[0] for o in ride_offers_existing) + 1
-start_request_id = max(r[0] for r in ride_requests_existing) + 1
-start_match_id = max(m[0] for m in matches_existing) + 1
-start_rating_id = max(r[0] for r in ratings_existing) + 1
-
-first_names = ["Alex","Jordan","Taylor","Morgan","Cameron","Riley","Casey","Jamie","Avery","Sam","Sydney","Charlie","Drew","Logan","Peyton","Harper","Blake","Quinn","Rowan","Elliot","Maria","Jamal","Priya","Diego","Lina","Marcus"]
-last_names = ["Smith","Johnson","Williams","Brown","Jones","Miller","Davis","Garcia","Rodriguez","Wilson","Martinez","Anderson","Taylor","Thomas","Hernandez","Moore","Martin","Jackson","Thompson","White"]
-car_makes_models = [("Toyota","Camry"),("Honda","Civic"),("Tesla","Model 3"),("Tesla","Model Y"),("Chevrolet","Malibu"),("Hyundai","Elantra"),("Ford","Focus"),("Nissan","Altima")]
-colors = ["Blue","Gray","Silver","Red","Black","White","Green","Gold","Maroon"]
-roles = ["rider","driver","both"]
-
-# helper to produce ISO timestamps in Oct 2025 window
-def rand_datetime_oct13_to_nov30():
- base = datetime.datetime(2025,10,13)
- delta = datetime.timedelta(days=random.randint(0,48), hours=random.randint(0,23), minutes=random.randint(0,59))
- return (base + delta).isoformat(timespec="seconds")
-
-# --- generate users ---
-users_generated = []
-for i in range(EXTRA):
- uid = start_user_id + i
- fn = random.choice(first_names)
- ln = random.choice(last_names)
- username = f"{fn.lower()}.{ln.lower()}{uid}"
- email = f"{fn.lower()}.{ln.lower()}{uid}@drexel.edu"
- phone = f"215{random.randint(2000000,9999999)}"
- role = random.choices(roles, weights=[0.45,0.40,0.15], k=1)[0]
- rating_avg = round(max(2.5, min(5.0, random.gauss(4.4,0.4))),2)
- created_at = (datetime.datetime(2025,10,1) + datetime.timedelta(days=random.randint(0,60))).isoformat(timespec="seconds")
- users_generated.append((uid, email, username, "x", phone, role, rating_avg, created_at))
-
-users_all = users_existing + users_generated
-
-# --- generate locations ---
-locations_generated = []
-for i in range(EXTRA):
- lid = start_location_id + i
- name = f"Philly Point {lid}"
- address = f"{random.randint(100,3999)} Market St"
- locations_generated.append((lid, name, address))
-locations_all = locations_existing + locations_generated
-
-# --- generate vehicles (assign to drivers/both) ---
-driver_candidates = [u for u in users_all if u[5] in ("driver","both")]
-# ensure enough drivers by converting some generated users if needed
-if len(driver_candidates) < 40:
- for j in range(30):
- idx = j % len(users_generated)
- u = users_generated[idx]
- users_generated[idx] = (u[0], u[1], u[2], u[3], u[4], "driver", u[6], u[7])
- users_all = users_existing + users_generated
- driver_candidates = [u for u in users_all if u[5] in ("driver","both")]
-
-vehicles_generated = []
-for i in range(EXTRA):
- vid = start_vehicle_id + i
- owner = random.choice(driver_candidates)[0]
- make, model = random.choice(car_makes_models)
- color = random.choice(colors)
- plate = f"NJ-{1000 + vid:04d}"
- seats = random.choice([4,4,5,5,6])
- year = random.randint(2010,2025)
- fun_fact = random.choice(["n/a","student vehicle","rideshare-ready","garage kept"])
- vehicles_generated.append((vid, owner, make, model, color, plate, seats, year, fun_fact))
-vehicles_all = vehicles_existing + vehicles_generated
-
-# --- generate ride_offers ---
-offers_generated = []
-for i in range(EXTRA):
- oid = start_offer_id + i
- driver = random.choice(driver_candidates)[0]
- # pick a vehicle by this owner or random
- owned = [v for v in vehicles_all if v[1] == driver]
- vehicle_id = random.choice(owned)[0] if owned else random.choice(vehicles_all)[0]
- origin = random.choice(locations_all)[0]
- dest = random.choice(locations_all)[0]
- if origin == dest:
- dest = (dest % (start_location_id + EXTRA - 1)) + 1
- depart_at = rand_datetime_oct13_to_nov30()
- seats_available = random.choice([1,1,2,2,3,4])
- price_base = round(random.uniform(3.0,9.0),2)
- price_per_mile = round(random.uniform(0.5,1.6),2)
- status = random.choices(["open","closed"], weights=[0.6,0.4])[0]
- created_at = (datetime.datetime.fromisoformat(depart_at) - datetime.timedelta(days=random.randint(0,7))).isoformat(timespec="seconds")
- offers_generated.append((oid, driver, vehicle_id, origin, dest, depart_at, seats_available, price_base, price_per_mile, status, created_at))
-offers_all = ride_offers_existing + offers_generated
-
-# --- generate ride_requests ---
-riders = [u for u in users_all if u[5] in ("rider","both")]
-requests_generated = []
-for i in range(EXTRA):
- rid = start_request_id + i
- rider = random.choice(riders)[0]
- pickup = random.choice(locations_all)[0]
- dropoff = random.choice(locations_all)[0]
- if pickup == dropoff:
- dropoff = (dropoff % (start_location_id + EXTRA - 1)) + 1
- earliest_dt = datetime.datetime(2025,10,13) + datetime.timedelta(days=random.randint(0,48), hours=random.randint(6,20), minutes=random.randint(0,59))
- latest_dt = earliest_dt + datetime.timedelta(minutes=random.randint(10,60))
- seats_needed = random.choice([1,1,2])
- status = random.choices(["open","matched","cancelled"], weights=[0.55,0.35,0.10])[0]
- created_at = (earliest_dt - datetime.timedelta(days=random.randint(0,5))).isoformat(timespec="seconds")
- requests_generated.append((rid, rider, pickup, dropoff, earliest_dt.isoformat(timespec="seconds"), latest_dt.isoformat(timespec="seconds"), seats_needed, status, created_at))
-requests_all = ride_requests_existing + requests_generated
-
-# --- generate matches by trying to align requests to offers ---
-matches_generated = []
-match_id = start_match_id
-# simple matching: randomly attempt to pair some requests to offers
-for req in requests_generated:
- if random.random() < 0.6: # 60% of generated requests get a match
- # pick a candidate offer where seats >= seats_needed
- candidates = [o for o in offers_all if o[6] >= req[6]]
- if not candidates:
- continue
- offer = random.choice(candidates)
- seats_booked = min(offer[6], req[6])
- miles = random.uniform(2.0,12.0)
- price_total = round(offer[7] + offer[8]*miles, 2) if isinstance(offer[7], float) else round(offer[7] + offer[8]*miles,2)
- state = random.choices(["confirmed","completed","cancelled","no_show"], weights=[0.4,0.35,0.15,0.10])[0]
- matched_at = (datetime.datetime.fromisoformat(req[4]) - datetime.timedelta(hours=random.randint(0,4))).isoformat(timespec="seconds")
- matches_generated.append((match_id, req[0], offer[0], seats_booked, price_total, state, matched_at))
- match_id += 1
-
-matches_all = matches_existing + matches_generated
-
-# --- generate ratings for completed matches ---
-ratings_generated = []
-rating_id = start_rating_id
-for m in matches_generated:
- if m[5] == "completed" and random.random() < 0.85:
- # find from_user (rider) and to_user (driver)
- req = next((r for r in requests_all if r[0] == m[1]), None)
- offer = next((o for o in offers_all if o[0] == m[2]), None)
- if not req or not offer:
- continue
- from_user = req[1]
- to_user = offer[1]
- if from_user == to_user:
- continue
- stars = random.choices([5,4,3,2,1], weights=[0.6,0.25,0.08,0.04,0.03])[0]
- comment = random.choice(["Great ride","Friendly driver","On time","Would ride again","Car was clean","Driver was late","Helpful with bags"])
- created_at = (datetime.datetime.fromisoformat(m[6]) + datetime.timedelta(hours=random.randint(1,48))).isoformat(timespec="seconds")
- ratings_generated.append((rating_id, m[0], from_user, to_user, stars, comment, created_at))
- rating_id += 1
-
-# --- Clean up ratings to ensure no self-ratings or duplicates ---
-cleaned_ratings = []
-seen_pairs = set()
-for r in ratings_generated:
- rid, match_id, from_user, to_user, stars, comment, created_at = r
- if from_user == to_user:
- continue
- pair_key = (from_user, to_user)
- if pair_key in seen_pairs:
- continue
- seen_pairs.add(pair_key)
- cleaned_ratings.append(r)
-ratings_generated = cleaned_ratings
-
-ratings_all = ratings_existing + ratings_generated
-
-# --- write SQL file ---
-def sql_quote(val):
- if val is None:
- return "NULL"
- if isinstance(val, str):
- return "'" + val.replace("'", "''") + "'"
- return str(val)
-
-with open(SQL_PATH, "w", encoding="utf-8") as f:
- f.write("-- USERS\n")
- for u in users_all:
- if len(u) == 7:
- # older sample format (id, fullname, email, phone, role, rating, created_at)
- uid, uname, email, phone, role, rating, created = u if len(u) == 7 else (u[0], u[2], u[1], u[3], u[4], u[5], u[6])
- # if the sample stored full name, ensure username is simple
- # but our existing list is normalized so treat generically:
- f.write("INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (%s,%s,%s,%s,%s,%s,%s,%s);\n" % (
- sql_quote(uid), sql_quote(email), sql_quote(uname), sql_quote("x"), sql_quote(phone), sql_quote(role), sql_quote(rating), sql_quote(created)
- ))
- else:
- # generated entries: (uid, email, username, pw, phone, role, rating, created_at)
- uid, email, uname, pw, phone, role, rating, created = u
- f.write("INSERT INTO USER (user_id,email,username,password_hash,phone_number,role,rating_avg,created_at) VALUES (%s,%s,%s,%s,%s,%s,%s,%s);\n" % (
- sql_quote(uid), sql_quote(email), sql_quote(uname), sql_quote(pw), sql_quote(phone), sql_quote(role), sql_quote(rating), sql_quote(created)
- ))
-
- f.write("\n-- VEHICLE\n")
- for v in vehicles_all:
- f.write("INSERT INTO VEHICLE (vehicle_id,owner_user_id,make,model,color,plate,seats_total,year,fun_fact) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s);\n" % tuple(sql_quote(x) for x in v))
-
- f.write("\n-- LOCATION\n")
- for loc in locations_all:
- f.write("INSERT INTO LOCATION (location_id,name,address) VALUES (%s,%s,%s);\n" % (sql_quote(loc[0]), sql_quote(loc[1]), sql_quote(loc[2])))
-
- f.write("\n-- RIDE_OFFER\n")
- for o in offers_all:
- f.write("INSERT INTO RIDE_OFFER (offer_id,driver_id,vehicle_id,original_location_id,dest_location_id,depart_at,seats_available,price_base,price_per_mile,status,created_at) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s);\n" % (
- sql_quote(o[0]), sql_quote(o[1]), sql_quote(o[2]), sql_quote(o[3]), sql_quote(o[4]), sql_quote(o[5]), sql_quote(o[6]), sql_quote(o[7]), sql_quote(o[8]), sql_quote(o[9]), sql_quote(o[10])
- ))
-
- f.write("\n-- RIDE_REQUEST\n")
- for r in requests_all:
- f.write("INSERT INTO RIDE_REQUEST (request_id,rider_id,pickup_location_id,dropoff_location_id,earliest_pickup,latest_pickup,seats_needed,status,created_at) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s);\n" % (
- sql_quote(r[0]), sql_quote(r[1]), sql_quote(r[2]), sql_quote(r[3]), sql_quote(r[4]), sql_quote(r[5]), sql_quote(r[6]), sql_quote(r[7]), sql_quote(r[8])
- ))
-
- f.write("\n-- RIDE_MATCH\n")
- for m in matches_all:
- f.write("INSERT INTO RIDE_MATCH (match_id,request_id,offer_id,seats_booked,price_total,state,matched_at) VALUES (%s,%s,%s,%s,%s,%s,%s);\n" % (
- sql_quote(m[0]), sql_quote(m[1]), sql_quote(m[2]), sql_quote(m[3]), sql_quote(m[4]), sql_quote(m[5]), sql_quote(m[6])
- ))
-
- f.write("\n-- RATING\n")
- for rt in ratings_all:
- f.write("INSERT INTO RATING (rating_id,match_id,from_user_id,to_user_id,stars,comment,created_at) VALUES (%s,%s,%s,%s,%s,%s,%s);\n" % (
- sql_quote(rt[0]), sql_quote(rt[1]), sql_quote(rt[2]), sql_quote(rt[3]), sql_quote(rt[4]), sql_quote(rt[5]), sql_quote(rt[6])
- ))
-
-print("Wrote SQL to:", SQL_PATH)
-print("Row counts (approx): USERS=%d VEHICLE=%d LOCATION=%d RIDE_OFFER=%d RIDE_REQUEST=%d RIDE_MATCH=%d RATING=%d" % (
- len(users_all), len(vehicles_all), len(locations_all), len(offers_all), len(requests_all), len(matches_all), len(ratings_all)
-))
\ No newline at end of file