first commit
This commit is contained in:
711
app/Http/Controllers/GeoTrackController.php
Normal file
711
app/Http/Controllers/GeoTrackController.php
Normal file
@ -0,0 +1,711 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\SalesRoute;
|
||||
use App\Models\SalesRouteSummary;
|
||||
use App\Models\Waypoint;
|
||||
use App\Helpers\GeoHelper;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class GeoTrackController extends Controller
|
||||
{
|
||||
/**
|
||||
* GET /api/sales-routes
|
||||
* Get all routes with optional filters
|
||||
*/
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
$query = SalesRoute::with('user:id,employee_id,name,color');
|
||||
|
||||
// Filter by date
|
||||
if ($request->has('date')) {
|
||||
$query->whereDate('date', $request->date);
|
||||
}
|
||||
|
||||
// Filter by salesId
|
||||
// Filter by salesId
|
||||
if ($request->has('salesId')) {
|
||||
$user = User::where('employee_id', $request->salesId)->first(['id']);
|
||||
if ($user) {
|
||||
$query->where('user_id', $user->id);
|
||||
} else {
|
||||
// If user not found with that employee_id, return empty result immediately
|
||||
// preventing return of all routes or failing
|
||||
$query->where('user_id', 'non_existent_id');
|
||||
}
|
||||
}
|
||||
|
||||
$routes = $query->orderBy('date', 'desc')->get();
|
||||
|
||||
// Format routes with waypoints
|
||||
$formattedRoutes = $routes->map(function ($route) use ($request) {
|
||||
$waypoints = $route->waypoints;
|
||||
|
||||
// Filter waypoints by time if specified
|
||||
if ($request->has('timeFrom') || $request->has('timeTo')) {
|
||||
$waypoints = $waypoints->filter(function ($wp) use ($request) {
|
||||
$wpTime = Carbon::parse($wp->recorded_at)->format('H:i');
|
||||
if ($request->timeFrom && $wpTime < $request->timeFrom)
|
||||
return false;
|
||||
if ($request->timeTo && $wpTime > $request->timeTo)
|
||||
return false;
|
||||
return true;
|
||||
})->values();
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => (string) $route->id,
|
||||
'salesId' => $route->user->employee_id,
|
||||
'salesName' => $route->user->name,
|
||||
'salesColor' => $route->user->color,
|
||||
'date' => $route->date->format('Y-m-d'),
|
||||
'waypoints' => $waypoints->map(function ($wp) {
|
||||
return [
|
||||
'lat' => (float) $wp->latitude,
|
||||
'lng' => (float) $wp->longitude,
|
||||
'time' => Carbon::parse($wp->recorded_at)->format('H:i'),
|
||||
'location' => $wp->location_name ?? 'GPS Point',
|
||||
'type' => $wp->type,
|
||||
];
|
||||
})->values(),
|
||||
];
|
||||
})->filter(fn($route) => count($route['waypoints']) > 0)->values();
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'summary' => [
|
||||
'totalRoutes' => $formattedRoutes->count(),
|
||||
'dateRange' => $request->date ?? 'all',
|
||||
'timeRange' => ($request->timeFrom || $request->timeTo)
|
||||
? ($request->timeFrom ?? '00:00') . ' - ' . ($request->timeTo ?? '23:59')
|
||||
: 'all',
|
||||
],
|
||||
'routes' => $formattedRoutes,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/sales-routes/dates
|
||||
* Get available dates
|
||||
*/
|
||||
public function dates(): JsonResponse
|
||||
{
|
||||
// MongoDB Aggregation to get unique dates
|
||||
$rawDates = SalesRoute::raw(function ($collection) {
|
||||
return $collection->aggregate([
|
||||
[
|
||||
'$project' => [
|
||||
'dateOnly' => ['$dateToString' => ['format' => '%Y-%m-%d', 'date' => '$date']]
|
||||
]
|
||||
],
|
||||
[
|
||||
'$group' => [
|
||||
'_id' => '$dateOnly'
|
||||
]
|
||||
],
|
||||
[
|
||||
'$sort' => ['_id' => -1]
|
||||
]
|
||||
]);
|
||||
});
|
||||
|
||||
$dates = collect($rawDates)->map(function ($item) {
|
||||
return $item['_id'];
|
||||
})->values();
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'dates' => $dates,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/sales-routes/sales
|
||||
* Get sales list
|
||||
*/
|
||||
public function sales(): JsonResponse
|
||||
{
|
||||
$users = User::where('is_active', true)
|
||||
->where('role', 'sales')
|
||||
->get(['id', 'employee_id', 'name', 'color']);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'salesList' => $users->map(fn($u) => [
|
||||
'id' => $u->employee_id,
|
||||
'name' => $u->name,
|
||||
'color' => $u->color,
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/sales-routes/{id}
|
||||
* Get single route
|
||||
*/
|
||||
public function show(string $id): JsonResponse
|
||||
{
|
||||
$route = SalesRoute::with(['user:id,employee_id,name,color', 'waypoints'])->find($id);
|
||||
|
||||
if (!$route) {
|
||||
return response()->json(['success' => false, 'error' => 'Route not found'], 404);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'route' => [
|
||||
'id' => (string) $route->id,
|
||||
'salesId' => $route->user->employee_id,
|
||||
'salesName' => $route->user->name,
|
||||
'salesColor' => $route->user->color,
|
||||
'date' => $route->date->format('Y-m-d'),
|
||||
'waypoints' => $route->waypoints->map(function ($wp) {
|
||||
return [
|
||||
'lat' => (float) $wp->latitude,
|
||||
'lng' => (float) $wp->longitude,
|
||||
'time' => Carbon::parse($wp->recorded_at)->format('H:i'),
|
||||
'location' => $wp->location_name ?? 'GPS Point',
|
||||
'type' => $wp->type,
|
||||
];
|
||||
}),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/sales-routes/waypoints
|
||||
* Create waypoint (for mobile app)
|
||||
*/
|
||||
public function storeWaypoint(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'user_id' => 'required|string',
|
||||
'type' => 'required|in:checkin,checkout,gps,lunch,visit',
|
||||
'latitude' => 'required|numeric',
|
||||
'longitude' => 'required|numeric',
|
||||
]);
|
||||
|
||||
// Find user
|
||||
$user = User::where('employee_id', $request->user_id)->first();
|
||||
if (!$user) {
|
||||
return response()->json(['success' => false, 'error' => 'User not found'], 404);
|
||||
}
|
||||
|
||||
// Get or create today's route
|
||||
$today = Carbon::today();
|
||||
$route = SalesRoute::firstOrCreate(
|
||||
['user_id' => $user->id, 'date' => $today],
|
||||
['status' => 'active', 'started_at' => now()]
|
||||
);
|
||||
|
||||
// Create waypoint
|
||||
$waypoint = Waypoint::create([
|
||||
'sales_route_id' => $route->id,
|
||||
'type' => $request->type,
|
||||
'latitude' => $request->latitude,
|
||||
'longitude' => $request->longitude,
|
||||
'recorded_at' => now(),
|
||||
'location_name' => $request->location_name,
|
||||
'address' => $request->address,
|
||||
'notes' => $request->notes,
|
||||
'photo_url' => $request->photo_url,
|
||||
]);
|
||||
|
||||
// If checkout, update route status
|
||||
if ($request->type === 'checkout') {
|
||||
$waypoints = $route->waypoints()->get()->toArray();
|
||||
$totalDistance = GeoHelper::calculateRouteDistance($waypoints);
|
||||
|
||||
$firstWp = $route->waypoints()->orderBy('recorded_at')->first();
|
||||
$durationMinutes = $firstWp ? now()->diffInMinutes($firstWp->recorded_at) : 0;
|
||||
$visitCount = $route->waypoints()->where('type', 'visit')->count();
|
||||
|
||||
$route->update([
|
||||
'status' => 'completed',
|
||||
'ended_at' => now(),
|
||||
'total_distance_km' => $totalDistance,
|
||||
'total_distance_km' => $totalDistance,
|
||||
'total_duration_minutes' => $durationMinutes,
|
||||
'total_visits' => $visitCount,
|
||||
]);
|
||||
|
||||
// Save to Sales Link Summary
|
||||
SalesRouteSummary::create([
|
||||
'user_id' => $user->id,
|
||||
'sales_route_id' => $route->id,
|
||||
'date' => $today,
|
||||
'total_distance_km' => $totalDistance,
|
||||
'total_duration_minutes' => $durationMinutes,
|
||||
'total_visits' => $visitCount,
|
||||
'started_at' => $route->started_at,
|
||||
'ended_at' => now(),
|
||||
]);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'id' => $waypoint->id,
|
||||
'sales_route_id' => $route->id,
|
||||
'type' => $waypoint->type,
|
||||
'latitude' => $waypoint->latitude,
|
||||
'longitude' => $waypoint->longitude,
|
||||
'recorded_at' => $waypoint->recorded_at,
|
||||
'location_name' => $waypoint->location_name,
|
||||
],
|
||||
'message' => 'Waypoint recorded successfully',
|
||||
]);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// MOBILE API ENDPOINTS (For Flutter App)
|
||||
// ============================================================
|
||||
|
||||
/**
|
||||
* POST /api/mobile/login
|
||||
* Authenticate user and return settings + profile
|
||||
*/
|
||||
public function mobileLogin(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'username' => 'required|string',
|
||||
'password' => 'required',
|
||||
]);
|
||||
|
||||
// Login supports both email and employee_id (username) - case insensitive
|
||||
$username = strtolower($request->username);
|
||||
|
||||
// Find user by email or employee_id
|
||||
$user = User::where('email', $username)
|
||||
->orWhere('employee_id', $username)
|
||||
->first();
|
||||
|
||||
// Check password (Hash or plain text for legacy/demo)
|
||||
if (!$user) {
|
||||
return response()->json(['success' => false, 'error' => 'Invalid credentials'], 401);
|
||||
}
|
||||
|
||||
// 1. Check Hashed Password (New Standard)
|
||||
if (Hash::check($request->password, $user->password)) {
|
||||
// Valid
|
||||
}
|
||||
// 2. Fallback: Check hardcoded 'password' for demo users who haven't changed pass yet
|
||||
// OR check plain text match if DB seeded with plain text
|
||||
else if ($request->password === 'password' || $request->password === $user->password) {
|
||||
// Valid (Legacy/Demo) - You might want to auto-hash it here if you want to migrate
|
||||
} else {
|
||||
return response()->json(['success' => false, 'error' => 'Invalid credentials'], 401);
|
||||
}
|
||||
|
||||
if (!$user) {
|
||||
return response()->json(['success' => false, 'error' => 'Invalid credentials'], 401);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'user' => [
|
||||
'id' => (string) $user->id,
|
||||
'employee_id' => $user->employee_id,
|
||||
'name' => $user->name,
|
||||
'email' => $user->email,
|
||||
'role' => $user->role,
|
||||
'color' => $user->color,
|
||||
],
|
||||
'company' => [
|
||||
'name' => 'GeoReach Intelligence',
|
||||
'address' => 'Jakarta, Indonesia',
|
||||
'logo_url' => 'assets/logo.png',
|
||||
],
|
||||
'settings' => [
|
||||
'local_storage_interval_seconds' => 300, // 5 minutes (Save to local)
|
||||
'server_sync_interval_seconds' => 1800, // 30 minutes (Submit to server)
|
||||
'gps_accuracy_filter_meters' => 50,
|
||||
],
|
||||
'message' => 'Login successful',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/mobile/checkin
|
||||
* Clock in / start tracking for today
|
||||
*/
|
||||
public function mobileCheckin(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'user_id' => 'required|string',
|
||||
'latitude' => 'required|numeric',
|
||||
'longitude' => 'required|numeric',
|
||||
]);
|
||||
|
||||
// Find user
|
||||
$user = User::where('employee_id', $request->user_id)->first();
|
||||
if (!$user) {
|
||||
return response()->json(['success' => false, 'error' => 'User not found'], 404);
|
||||
}
|
||||
|
||||
$today = Carbon::today();
|
||||
|
||||
// Check if already checked in today
|
||||
$existingRoute = SalesRoute::where('user_id', $user->id)
|
||||
->whereDate('date', $today)
|
||||
->first();
|
||||
|
||||
if ($existingRoute && $existingRoute->status === 'active') {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'error' => 'Already checked in today',
|
||||
'route_id' => $existingRoute->id,
|
||||
], 409);
|
||||
}
|
||||
|
||||
// Create new route for today
|
||||
$route = SalesRoute::create([
|
||||
'user_id' => $user->id,
|
||||
'date' => $today,
|
||||
'status' => 'active',
|
||||
'started_at' => now(),
|
||||
]);
|
||||
|
||||
// Create check-in waypoint
|
||||
Waypoint::create([
|
||||
'sales_route_id' => $route->id,
|
||||
'type' => 'checkin',
|
||||
'latitude' => $request->latitude,
|
||||
'longitude' => $request->longitude,
|
||||
'recorded_at' => now(),
|
||||
'location_name' => $request->location_name ?? 'Check-in Point',
|
||||
'notes' => $request->device_info,
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'route_id' => $route->id,
|
||||
'checked_in_at' => now()->format('Y-m-d H:i:s'),
|
||||
'message' => 'Check-in successful',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/mobile/checkout
|
||||
* Clock out / stop tracking
|
||||
*/
|
||||
public function mobileCheckout(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'user_id' => 'required|string',
|
||||
'latitude' => 'required|numeric',
|
||||
'longitude' => 'required|numeric',
|
||||
]);
|
||||
|
||||
// Find user
|
||||
$user = User::where('employee_id', $request->user_id)->first();
|
||||
if (!$user) {
|
||||
return response()->json(['success' => false, 'error' => 'User not found'], 404);
|
||||
}
|
||||
|
||||
$today = Carbon::today();
|
||||
|
||||
// Find active route (from today or previous days)
|
||||
$route = SalesRoute::where('user_id', $user->id)
|
||||
->where('status', 'active')
|
||||
->orderBy('date', 'desc') // Get latest if multiple?
|
||||
->first();
|
||||
|
||||
if (!$route) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'error' => 'No active route found. Please check-in first. (User: ' . $user->employee_id . ')',
|
||||
], 400);
|
||||
}
|
||||
|
||||
// Create checkout waypoint
|
||||
Waypoint::create([
|
||||
'sales_route_id' => $route->id,
|
||||
'type' => 'checkout',
|
||||
'latitude' => $request->latitude,
|
||||
'longitude' => $request->longitude,
|
||||
'recorded_at' => now(),
|
||||
'location_name' => $request->location_name ?? 'Check-out Point',
|
||||
]);
|
||||
|
||||
// Calculate totals
|
||||
$waypoints = $route->waypoints()->get()->toArray();
|
||||
$totalDistance = GeoHelper::calculateRouteDistance($waypoints);
|
||||
$firstWp = $route->waypoints()->orderBy('recorded_at')->first();
|
||||
$durationMinutes = $firstWp ? now()->diffInMinutes($firstWp->recorded_at) : 0;
|
||||
$visitCount = $route->waypoints()->where('type', 'visit')->count();
|
||||
|
||||
// Update route status
|
||||
$route->update([
|
||||
'status' => 'completed',
|
||||
'ended_at' => now(),
|
||||
'total_distance_km' => $totalDistance,
|
||||
'total_duration_minutes' => $durationMinutes,
|
||||
'total_visits' => $visitCount,
|
||||
]);
|
||||
|
||||
// Save to Sales Link Summary
|
||||
SalesRouteSummary::create([
|
||||
'user_id' => $user->id,
|
||||
'sales_route_id' => $route->id,
|
||||
'date' => $today,
|
||||
'total_distance_km' => $totalDistance,
|
||||
'total_duration_minutes' => $durationMinutes,
|
||||
'total_visits' => $visitCount,
|
||||
'started_at' => $route->started_at,
|
||||
'ended_at' => now(),
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Check-out successful',
|
||||
'summary' => [
|
||||
'route_id' => $route->id,
|
||||
'total_distance_km' => $totalDistance,
|
||||
'total_duration_minutes' => $durationMinutes,
|
||||
'total_visits' => $visitCount,
|
||||
'waypoints_count' => count($waypoints),
|
||||
'checked_out_at' => now()->format('Y-m-d H:i:s'),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/mobile/waypoints/batch
|
||||
* Batch submit waypoints (for offline support)
|
||||
*/
|
||||
public function storeBatchWaypoints(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'user_id' => 'required|string',
|
||||
'waypoints' => 'required|array|min:1',
|
||||
'waypoints.*.type' => 'required|in:gps,visit,lunch,checkin,checkout',
|
||||
'waypoints.*.latitude' => 'required|numeric',
|
||||
'waypoints.*.longitude' => 'required|numeric',
|
||||
]);
|
||||
|
||||
// Find user
|
||||
$user = User::where('employee_id', $request->user_id)->first();
|
||||
if (!$user) {
|
||||
return response()->json(['success' => false, 'error' => 'User not found'], 404);
|
||||
}
|
||||
|
||||
$today = Carbon::today();
|
||||
|
||||
// Get or create today's route
|
||||
$route = SalesRoute::firstOrCreate(
|
||||
['user_id' => $user->id, 'date' => $today],
|
||||
['status' => 'active', 'started_at' => now()]
|
||||
);
|
||||
|
||||
$inserted = 0;
|
||||
|
||||
foreach ($request->waypoints as $wp) {
|
||||
Waypoint::create([
|
||||
'sales_route_id' => $route->id,
|
||||
'type' => $wp['type'],
|
||||
'latitude' => $wp['latitude'],
|
||||
'longitude' => $wp['longitude'],
|
||||
'recorded_at' => isset($wp['recorded_at']) ? Carbon::parse($wp['recorded_at']) : now(),
|
||||
'location_name' => $wp['location_name'] ?? null,
|
||||
'address' => $wp['address'] ?? null,
|
||||
'notes' => $wp['notes'] ?? null,
|
||||
'photo_url' => $wp['photo_url'] ?? null,
|
||||
]);
|
||||
$inserted++;
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'inserted' => $inserted,
|
||||
'route_id' => $route->id,
|
||||
'message' => "{$inserted} waypoints recorded successfully",
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/mobile/route/today
|
||||
* Get today's route for user
|
||||
*/
|
||||
public function getTodayRoute(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'user_id' => 'required|string',
|
||||
]);
|
||||
|
||||
// Find user
|
||||
$user = User::where('employee_id', $request->user_id)->first();
|
||||
if (!$user) {
|
||||
return response()->json(['success' => false, 'error' => 'User not found'], 404);
|
||||
}
|
||||
|
||||
$today = Carbon::today();
|
||||
|
||||
$route = SalesRoute::with('waypoints')
|
||||
->where('user_id', $user->id)
|
||||
->whereDate('date', $today)
|
||||
->first();
|
||||
|
||||
if (!$route) {
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'route' => null,
|
||||
'message' => 'No route found for today',
|
||||
]);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'route' => [
|
||||
'id' => $route->id,
|
||||
'date' => $route->date->format('Y-m-d'),
|
||||
'status' => $route->status,
|
||||
'started_at' => $route->started_at?->format('Y-m-d H:i:s'),
|
||||
'ended_at' => $route->ended_at?->format('Y-m-d H:i:s'),
|
||||
'total_distance_km' => $route->total_distance_km,
|
||||
'total_duration_minutes' => $route->total_duration_minutes,
|
||||
'waypoints' => $route->waypoints->map(fn($wp) => [
|
||||
'id' => $wp->id,
|
||||
'type' => $wp->type,
|
||||
'lat' => (float) $wp->latitude,
|
||||
'lng' => (float) $wp->longitude,
|
||||
'time' => Carbon::parse($wp->recorded_at)->format('Y-m-d H:i:s'),
|
||||
'location' => $wp->location_name ?? 'GPS Point',
|
||||
'notes' => $wp->notes,
|
||||
]),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/mobile/status
|
||||
* Get current tracking status for user
|
||||
*/
|
||||
public function getTrackingStatus(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'user_id' => 'required|string',
|
||||
]);
|
||||
|
||||
// Find user
|
||||
$user = User::where('employee_id', $request->user_id)->first();
|
||||
if (!$user) {
|
||||
return response()->json(['success' => false, 'error' => 'User not found'], 404);
|
||||
}
|
||||
|
||||
$today = Carbon::today();
|
||||
|
||||
// MongoDB compatible: get route without withCount
|
||||
$route = SalesRoute::where('user_id', $user->id)
|
||||
->whereDate('date', $today)
|
||||
->first();
|
||||
|
||||
if (!$route) {
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'is_checked_in' => false,
|
||||
'route_id' => null,
|
||||
'checked_in_at' => null,
|
||||
'waypoints_today' => 0,
|
||||
]);
|
||||
}
|
||||
|
||||
// Count waypoints manually for MongoDB compatibility
|
||||
$waypointsCount = $route->waypoints()->count();
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'is_checked_in' => $route->status === 'active',
|
||||
'is_completed' => $route->status === 'completed',
|
||||
'route_id' => $route->id,
|
||||
'checked_in_at' => $route->started_at?->format('Y-m-d H:i:s'),
|
||||
'checked_out_at' => $route->ended_at?->format('Y-m-d H:i:s'),
|
||||
'waypoints_today' => $waypointsCount,
|
||||
'total_distance_km' => $route->total_distance_km,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/mobile/profile
|
||||
* Update user profile
|
||||
*/
|
||||
public function updateProfile(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'user_id' => 'required|string', // Employee ID or User ID
|
||||
'name' => 'sometimes|string',
|
||||
'email' => 'sometimes|email',
|
||||
'phone' => 'sometimes|string',
|
||||
]);
|
||||
|
||||
$user = User::where('employee_id', $request->user_id)->orWhere('id', $request->user_id)->first();
|
||||
if (!$user) {
|
||||
return response()->json(['success' => false, 'error' => 'User not found'], 404);
|
||||
}
|
||||
|
||||
if ($request->has('name'))
|
||||
$user->name = $request->name;
|
||||
if ($request->has('email'))
|
||||
$user->email = $request->email;
|
||||
if ($request->has('phone'))
|
||||
$user->phone = $request->phone;
|
||||
|
||||
$user->save();
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Profile updated successfully',
|
||||
'user' => [
|
||||
'id' => (string) $user->id,
|
||||
'employee_id' => $user->employee_id,
|
||||
'name' => $user->name,
|
||||
'email' => $user->email,
|
||||
'phone' => $user->phone,
|
||||
'role' => $user->role,
|
||||
'color' => $user->color,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/mobile/change-password
|
||||
* Change user password
|
||||
*/
|
||||
public function changePassword(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'user_id' => 'required|string',
|
||||
'current_password' => 'required',
|
||||
'new_password' => 'required|min:8', // Add regex if strict is needed
|
||||
]);
|
||||
|
||||
$user = User::where('employee_id', $request->user_id)->orWhere('id', $request->user_id)->first();
|
||||
if (!$user) {
|
||||
return response()->json(['success' => false, 'error' => 'User not found'], 404);
|
||||
}
|
||||
|
||||
// Verify current password
|
||||
// Check hash OR Check basic 'password' fallback
|
||||
$isValidCurrent = Hash::check($request->current_password, $user->password)
|
||||
|| ($request->current_password === 'password' && !$user->password) // If initial state
|
||||
|| $request->current_password === $user->password;
|
||||
|
||||
if (!$isValidCurrent) {
|
||||
return response()->json(['success' => false, 'error' => 'Password lama salah'], 401);
|
||||
}
|
||||
|
||||
// Update to new hashed password
|
||||
$user->password = Hash::make($request->new_password);
|
||||
$user->save();
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Password berhasil diubah',
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user