diff --git a/salon-booking-api b/salon-booking-api deleted file mode 160000 index 91c25f6..0000000 --- a/salon-booking-api +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 91c25f66f2f79bb4ab607c2e049b740f393a9810 diff --git a/salon-booking-api/.editorconfig b/salon-booking-api/.editorconfig new file mode 100644 index 0000000..8f0de65 --- /dev/null +++ b/salon-booking-api/.editorconfig @@ -0,0 +1,18 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml}] +indent_size = 2 + +[docker-compose.yml] +indent_size = 4 diff --git a/salon-booking-api/.env.example b/salon-booking-api/.env.example new file mode 100644 index 0000000..ea0665b --- /dev/null +++ b/salon-booking-api/.env.example @@ -0,0 +1,59 @@ +APP_NAME=Laravel +APP_ENV=local +APP_KEY= +APP_DEBUG=true +APP_URL=http://localhost + +LOG_CHANNEL=stack +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=debug + +DB_CONNECTION=mysql +DB_HOST=127.0.0.1 +DB_PORT=3306 +DB_DATABASE=laravel +DB_USERNAME=root +DB_PASSWORD= + +BROADCAST_DRIVER=log +CACHE_DRIVER=file +FILESYSTEM_DISK=local +QUEUE_CONNECTION=sync +SESSION_DRIVER=file +SESSION_LIFETIME=120 + +MEMCACHED_HOST=127.0.0.1 + +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_MAILER=smtp +MAIL_HOST=mailpit +MAIL_PORT=1025 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_ENCRYPTION=null +MAIL_FROM_ADDRESS="hello@example.com" +MAIL_FROM_NAME="${APP_NAME}" + +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_DEFAULT_REGION=us-east-1 +AWS_BUCKET= +AWS_USE_PATH_STYLE_ENDPOINT=false + +PUSHER_APP_ID= +PUSHER_APP_KEY= +PUSHER_APP_SECRET= +PUSHER_HOST= +PUSHER_PORT=443 +PUSHER_SCHEME=https +PUSHER_APP_CLUSTER=mt1 + +VITE_APP_NAME="${APP_NAME}" +VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}" +VITE_PUSHER_HOST="${PUSHER_HOST}" +VITE_PUSHER_PORT="${PUSHER_PORT}" +VITE_PUSHER_SCHEME="${PUSHER_SCHEME}" +VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" diff --git a/salon-booking-api/.gitattributes b/salon-booking-api/.gitattributes new file mode 100644 index 0000000..fcb21d3 --- /dev/null +++ b/salon-booking-api/.gitattributes @@ -0,0 +1,11 @@ +* text=auto eol=lf + +*.blade.php diff=html +*.css diff=css +*.html diff=html +*.md diff=markdown +*.php diff=php + +/.github export-ignore +CHANGELOG.md export-ignore +.styleci.yml export-ignore diff --git a/salon-booking-api/.gitignore b/salon-booking-api/.gitignore new file mode 100644 index 0000000..7fe978f --- /dev/null +++ b/salon-booking-api/.gitignore @@ -0,0 +1,19 @@ +/.phpunit.cache +/node_modules +/public/build +/public/hot +/public/storage +/storage/*.key +/vendor +.env +.env.backup +.env.production +.phpunit.result.cache +Homestead.json +Homestead.yaml +auth.json +npm-debug.log +yarn-error.log +/.fleet +/.idea +/.vscode diff --git a/salon-booking-api/README.md b/salon-booking-api/README.md new file mode 100644 index 0000000..1a4c26b --- /dev/null +++ b/salon-booking-api/README.md @@ -0,0 +1,66 @@ +
+ + + +## About Laravel + +Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as: + +- [Simple, fast routing engine](https://laravel.com/docs/routing). +- [Powerful dependency injection container](https://laravel.com/docs/container). +- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage. +- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent). +- Database agnostic [schema migrations](https://laravel.com/docs/migrations). +- [Robust background job processing](https://laravel.com/docs/queues). +- [Real-time event broadcasting](https://laravel.com/docs/broadcasting). + +Laravel is accessible, powerful, and provides tools required for large, robust applications. + +## Learning Laravel + +Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework. + +You may also try the [Laravel Bootcamp](https://bootcamp.laravel.com), where you will be guided through building a modern Laravel application from scratch. + +If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library. + +## Laravel Sponsors + +We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the [Laravel Partners program](https://partners.laravel.com). + +### Premium Partners + +- **[Vehikl](https://vehikl.com/)** +- **[Tighten Co.](https://tighten.co)** +- **[WebReinvent](https://webreinvent.com/)** +- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)** +- **[64 Robots](https://64robots.com)** +- **[Curotec](https://www.curotec.com/services/technologies/laravel/)** +- **[Cyber-Duck](https://cyber-duck.co.uk)** +- **[DevSquad](https://devsquad.com/hire-laravel-developers)** +- **[Jump24](https://jump24.co.uk)** +- **[Redberry](https://redberry.international/laravel/)** +- **[Active Logic](https://activelogic.com)** +- **[byte5](https://byte5.de)** +- **[OP.GG](https://op.gg)** + +## Contributing + +Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions). + +## Code of Conduct + +In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct). + +## Security Vulnerabilities + +If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed. + +## License + +The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT). diff --git a/salon-booking-api/app/Console/Kernel.php b/salon-booking-api/app/Console/Kernel.php new file mode 100644 index 0000000..e6b9960 --- /dev/null +++ b/salon-booking-api/app/Console/Kernel.php @@ -0,0 +1,27 @@ +command('inspire')->hourly(); + } + + /** + * Register the commands for the application. + */ + protected function commands(): void + { + $this->load(__DIR__.'/Commands'); + + require base_path('routes/console.php'); + } +} diff --git a/salon-booking-api/app/Exceptions/Handler.php b/salon-booking-api/app/Exceptions/Handler.php new file mode 100644 index 0000000..56af264 --- /dev/null +++ b/salon-booking-api/app/Exceptions/Handler.php @@ -0,0 +1,30 @@ + + */ + protected $dontFlash = [ + 'current_password', + 'password', + 'password_confirmation', + ]; + + /** + * Register the exception handling callbacks for the application. + */ + public function register(): void + { + $this->reportable(function (Throwable $e) { + // + }); + } +} diff --git a/salon-booking-api/app/Http/Controllers/AuthController.php b/salon-booking-api/app/Http/Controllers/AuthController.php new file mode 100644 index 0000000..58e3fc8 --- /dev/null +++ b/salon-booking-api/app/Http/Controllers/AuthController.php @@ -0,0 +1,77 @@ +validate([ + 'name' => 'required|string|max:255', + 'email' => 'required|string|email|max:255|unique:users', + 'password' => 'required|string|min:6|confirmed', + 'role' => 'required|in:pelanggan,karyawan,pemilik', + ]); + + $user = User::create([ + 'name' => $request->name, + 'email' => $request->email, + 'password' => Hash::make($request->password), + 'role' => $request->role, + ]); + + event(new Registered($user)); // Kirim email verifikasi otomatis + + return response()->json(['message' => 'Registrasi berhasil, silakan cek email untuk verifikasi'], 201); +} + + public function login(Request $request) +{ + $request->validate([ + 'email' => 'required|string|email', + 'password' => 'required|string', + ]); + + $user = User::where('email', $request->email)->first(); + + if (! $user || ! Hash::check($request->password, $user->password)) { + throw ValidationException::withMessages([ + 'email' => ['Email atau password salah.'], + ]); + } + + // Tambahan: hanya izinkan login jika sudah disetujui + if ($user->role === 'karyawan' && ! $user->is_approved) { + return response()->json(['message' => 'Akun Anda belum disetujui oleh pemilik.'], 403); + } + + $token = $user->createToken('auth_token')->plainTextToken; + + return response()->json([ + 'access_token' => $token, + 'token_type' => 'Bearer', + 'user' => $user, + ]); +} + + + // User info + public function user(Request $request) + { + return $request->user(); + } + + // Logout + public function logout(Request $request) + { + $request->user()->tokens()->delete(); + return response()->json(['message'=>'Logged out']); + } +} diff --git a/salon-booking-api/app/Http/Controllers/BookingController.php b/salon-booking-api/app/Http/Controllers/BookingController.php new file mode 100644 index 0000000..be458d0 --- /dev/null +++ b/salon-booking-api/app/Http/Controllers/BookingController.php @@ -0,0 +1,146 @@ +user(); + + $query = Booking::with(['user', 'service:id,name,price']); // ← tambahkan price + + if ($user->role == 'pelanggan') { + $query->where('user_id', $user->id); + } elseif ($user->role == 'karyawan') { + $query->where('status', 'menunggu'); + } + + return response()->json($query->get()); + } + + + public function store(Request $request) +{ + $request->validate([ + 'service_id' => 'required|exists:services,id', + 'tanggal_booking' => 'required|date', + 'jam' => 'required|date_format:H:i', + ]); + + $user = $request->user(); + $tanggal = $request->tanggal_booking; + $jamMulai = Carbon::createFromFormat('H:i', $request->jam); + + // Ambil durasi dari layanan + $service = \App\Models\Service::findOrFail($request->service_id); + $durasi = $service->duration; // dalam menit + $jamSelesai = $jamMulai->copy()->addMinutes($durasi); + + // Cek apakah sudah ada 3 booking diterima pada jam yang sama + $jumlahBookingJamSama = Booking::where('tanggal_booking', $tanggal) + ->where('jam', $request->jam) + ->where('service_id', $request->service_id) + ->where('status', 'diterima') + ->count(); + + if ($jumlahBookingJamSama >= 3) { + return response()->json([ + 'message' => 'Slot sudah penuh. Silakan pilih jam lain.' + ], 409); + } + + // Cek bentrok dengan slot waktu berdasarkan durasi + $semuaBookingHariItu = Booking::with('service:id,duration') + ->where('tanggal_booking', $tanggal) + ->where('status', 'diterima') + ->get(); + + foreach ($semuaBookingHariItu as $booking) { + $bookingJamMulai = Carbon::createFromFormat('H:i:s', $booking->jam); + $bookingJamSelesai = $bookingJamMulai->copy()->addMinutes($booking->service->duration); + + // Jika booking baru bentrok dengan salah satu yang ada + if ( + $jamMulai->between($bookingJamMulai, $bookingJamSelesai->subMinute()) || + $jamSelesai->between($bookingJamMulai->addMinute(), $bookingJamSelesai) || + ($jamMulai->lte($bookingJamMulai) && $jamSelesai->gte($bookingJamSelesai)) + ) { + return response()->json([ + 'message' => 'Slot bertabrakan dengan booking lain. Silakan pilih jam berbeda.' + ], 409); + } + } + + // Jika aman, simpan booking + $booking = Booking::create([ + 'user_id' => $user->id, + 'service_id' => $request->service_id, + 'tanggal_booking' => $tanggal, + 'jam' => $request->jam, + 'status' => 'menunggu', + ]); + + $booking->load('service:id,name,price'); + + return response()->json([ + 'message' => 'Booking berhasil dibuat', + 'booking' => $booking + ], 201); +} + public function acceptedBookings(Request $request) + { + $user = $request->user(); + + if (!in_array($user->role, ['karyawan', 'pemilik'])) { + return response()->json(['message' => 'Unauthorized'], 403); + } + + $bookings = Booking::with(['user', 'service:id,name,price']) + ->whereIn('status', ['menunggu', 'diterima']) + ->get(); + + return response()->json($bookings); + } + + public function update(Request $request) + { + if ($request->user()->role !== 'karyawan') { + return response()->json([ + 'message' => 'Unauthorized. Hanya karyawan yang bisa mengubah status booking' + ], 403); + } + + $request->validate([ + 'user_id' => 'required|exists:users,id', + 'tanggal_booking' => 'required|date', + 'status' => 'required|in:menunggu,diterima,ditolak', + ]); + + $bookings = Booking::with(['user', 'service:id,name']) + ->where('user_id', $request->user_id) + ->where('tanggal_booking', $request->tanggal_booking) + ->get(); + + if ($bookings->isEmpty()) { + return response()->json([ + 'message' => 'Tidak ada booking ditemukan untuk user dan tanggal tersebut' + ], 404); + } + + foreach ($bookings as $booking) { + $booking->status = $request->status; + $booking->save(); + } + + return response()->json([ + 'message' => 'Status semua booking berhasil diperbarui', + 'jumlah_diperbarui' => $bookings->count(), + 'data' => $bookings + ]); + } +} diff --git a/salon-booking-api/app/Http/Controllers/CartController.php b/salon-booking-api/app/Http/Controllers/CartController.php new file mode 100644 index 0000000..56bb14b --- /dev/null +++ b/salon-booking-api/app/Http/Controllers/CartController.php @@ -0,0 +1,180 @@ +where('user_id', $request->user()->id) + ->get(); + + return response()->json($carts); + } + + /** + * Menambahkan layanan ke dalam keranjang. + */ + public function store(Request $request) + { + $request->validate([ + 'service_id' => 'required|exists:services,id', + 'quantity' => 'required|integer|min:1', + ]); + + $cart = Cart::create([ + 'user_id' => $request->user()->id, + 'service_id' => $request->service_id, + 'quantity' => $request->quantity, + ]); + + return response()->json([ + 'message' => 'Item added to cart', + 'data' => $cart, + ], 201); + } + + /** + * Mengubah jumlah layanan di keranjang. + */ + public function update(Request $request, $id) + { + $request->validate([ + 'quantity' => 'required|integer|min:1', + ]); + + $cart = Cart::where('id', $id) + ->where('user_id', $request->user()->id) + ->firstOrFail(); + + $cart->update([ + 'quantity' => $request->quantity, + ]); + + return response()->json([ + 'message' => 'Quantity updated', + 'data' => $cart, + ]); + } + + /** + * Menghapus item dari keranjang. + */ + public function destroy(Request $request, $id) + { + $cart = Cart::where('id', $id) + ->where('user_id', $request->user()->id) + ->firstOrFail(); + + $cart->delete(); + + return response()->json([ + 'message' => 'Item removed from cart', + ]); + } + + /** + * Checkout keranjang dan buat booking. + */ + public function checkout(Request $request) +{ + $user = $request->user(); + + $request->validate([ + 'tanggal_booking' => 'required|date', + 'jam' => 'required|date_format:H:i', + ]); + + $tanggal = $request->tanggal_booking; + $jamMulai = \Carbon\Carbon::parse($request->jam); // ← fleksibel, bisa H:i atau H:i:s + + $carts = \App\Models\Cart::with('service') + ->where('user_id', $user->id) + ->get(); + + if ($carts->isEmpty()) { + return response()->json(['message' => 'Keranjang kosong'], 400); + } + + \DB::beginTransaction(); + + try { + foreach ($carts as $cart) { + $service = $cart->service; + $durasi = $service->duration; + $jamSelesai = $jamMulai->copy()->addMinutes($durasi); + + // 1. Cek slot penuh (3 booking diterima untuk layanan ini di jam sama) + $jumlahBookingJamSama = \App\Models\Booking::where('tanggal_booking', $tanggal) + ->where('jam', $request->jam) + ->where('service_id', $service->id) + ->where('status', 'diterima') + ->count(); + + if ($jumlahBookingJamSama >= 3) { + \DB::rollBack(); + return response()->json([ + 'message' => "Slot penuh untuk layanan '{$service->name}' pada jam {$request->jam}." + ], 409); + } + + // 2. Cek bentrok waktu + $semuaBooking = \App\Models\Booking::with('service:id,duration') + ->where('tanggal_booking', $tanggal) + ->where('service_id', $service->id) + ->where('status', 'diterima') + ->get(); + + foreach ($semuaBooking as $booking) { + $bookingMulai = \Carbon\Carbon::parse($booking->jam); // ← fix here + $bookingSelesai = $bookingMulai->copy()->addMinutes($booking->service->duration); + + if ( + $jamMulai->between($bookingMulai, $bookingSelesai->subMinute()) || + $jamSelesai->between($bookingMulai->addMinute(), $bookingSelesai) || + ($jamMulai->lte($bookingMulai) && $jamSelesai->gte($bookingSelesai)) + ) { + \DB::rollBack(); + return response()->json([ + 'message' => "Layanan '{$service->name}' bentrok dengan jadwal lain di jam {$booking->jam}." + ], 409); + } + } + + // 3. Simpan booking + \App\Models\Booking::create([ + 'user_id' => $user->id, + 'service_id' => $cart->service_id, + 'tanggal_booking' => $tanggal, + 'jam' => $request->jam, + 'status' => 'menunggu', + ]); + } + + // 4. Bersihkan keranjang + \App\Models\Cart::where('user_id', $user->id)->delete(); + + \DB::commit(); + + return response()->json(['message' => 'Semua item berhasil dibooking'], 201); + + } catch (\Exception $e) { + \DB::rollBack(); + return response()->json([ + 'message' => 'Terjadi kesalahan saat booking', + 'error' => $e->getMessage(), + ], 500); + } +} + +} diff --git a/salon-booking-api/app/Http/Controllers/CategoryController.php b/salon-booking-api/app/Http/Controllers/CategoryController.php new file mode 100644 index 0000000..2513dcb --- /dev/null +++ b/salon-booking-api/app/Http/Controllers/CategoryController.php @@ -0,0 +1,34 @@ +json(Category::all()); + } + + // Tambah kategori (khusus pemilik/admin) + public function store(Request $request) + { + // Optional: Batasi hanya role 'pemilik' + if ($request->user()->role !== 'pemilik') { + return response()->json(['message' => 'Tidak diizinkan'], 403); + } + + $request->validate([ + 'name' => 'required|string|max:255' + ]); + + $category = Category::create([ + 'name' => $request->name + ]); + + return response()->json(['message' => 'Kategori berhasil ditambahkan', 'data' => $category], 201); + } +} diff --git a/salon-booking-api/app/Http/Controllers/Controller.php b/salon-booking-api/app/Http/Controllers/Controller.php new file mode 100644 index 0000000..77ec359 --- /dev/null +++ b/salon-booking-api/app/Http/Controllers/Controller.php @@ -0,0 +1,12 @@ +user()->role !== 'pemilik') { + return response()->json(['message' => 'Tidak diizinkan'], 403); + } + + $karyawan = User::where('role', 'karyawan')->get(); + return response()->json($karyawan); + } + + // Tambah Karyawan + public function store(Request $request) + { + if ($request->user()->role !== 'pemilik') { + return response()->json(['message' => 'Tidak diizinkan'], 403); + } + + $request->validate([ + 'name' => 'required|string', + 'email' => 'required|email|unique:users', + 'password' => 'required|string|min:6', + ]); + + $karyawan = User::create([ + 'name' => $request->name, + 'email' => $request->email, + 'password' => Hash::make($request->password), + 'role' => 'karyawan', + ]); + + return response()->json(['message' => 'Karyawan berhasil ditambahkan', 'karyawan' => $karyawan], 201); + } + + //status_approve + public function approve(Request $request, $id) + { + if ($request->user()->role !== 'pemilik') { + return response()->json(['message' => 'Tidak diizinkan'], 403); + } + + $karyawan = User::where('role', 'karyawan')->findOrFail($id); + $karyawan->is_approved = true; + $karyawan->save(); + + return response()->json(['message' => 'Karyawan berhasil disetujui']); + } + + + // Hapus Karyawan + public function destroy(Request $request, $id) + { + if ($request->user()->role !== 'pemilik') { + return response()->json(['message' => 'Tidak diizinkan'], 403); + } + + $karyawan = User::where('role', 'karyawan')->findOrFail($id); + $karyawan->delete(); + + return response()->json(['message' => 'Karyawan berhasil dihapus']); + } +} diff --git a/salon-booking-api/app/Http/Controllers/PelangganController.php b/salon-booking-api/app/Http/Controllers/PelangganController.php new file mode 100644 index 0000000..b6e9b3b --- /dev/null +++ b/salon-booking-api/app/Http/Controllers/PelangganController.php @@ -0,0 +1,35 @@ +where('role', 'pelanggan') + ->get() + ->map(function ($user) { + return [ + 'id' => $user->id, + 'name' => $user->name, + 'email' => $user->email, + 'bookings' => $user->bookings->map(function ($booking) { + return [ + 'id' => $booking->id, + 'service_name' => $booking->service->name ?? 'Layanan tidak diketahui', + 'tanggal_booking' => $booking->tanggal_booking, + 'status' => $booking->status, + ]; + }), + ]; + }); + + return response()->json($pelanggan); + } +} diff --git a/salon-booking-api/app/Http/Controllers/ServiceController.php b/salon-booking-api/app/Http/Controllers/ServiceController.php new file mode 100644 index 0000000..2639452 --- /dev/null +++ b/salon-booking-api/app/Http/Controllers/ServiceController.php @@ -0,0 +1,119 @@ +map(function ($service) { + $service->image_url = $service->image + ? url('storage/gambar/' . $service->image) + : null; + return $service; + }); + + return response()->json($services); + } + + // Get semua layanan (bisa filter by category_id) + public function index(Request $request) + { + $categoryId = $request->query('category_id'); + + $services = Service::query() + ->when($categoryId, fn($q) => $q->where('category_id', $categoryId)) + ->get() + ->map(function ($service) { + return [ + 'id' => $service->id, + 'name' => $service->name, + 'price' => $service->price, + 'description' => $service->description, + 'category_id' => $service->category_id, + 'image' => $service->image + ? url('storage/gambar/' . $service->image) + : null, + 'duration' => $service->duration, // ✅ Tambahkan ke response + ]; + }); + + return response()->json($services); + } + + // Tambah layanan baru dengan gambar + public function store(Request $request) + { + if ($request->user()->role !== 'pemilik') { + return response()->json(['message' => 'Tidak diizinkan'], 403); + } + + $request->validate([ + 'name' => 'required|string|max:255', + 'category_id' => 'required|exists:categories,id', + 'price' => 'required|numeric', + 'description' => 'nullable|string', + 'image' => 'nullable|image|mimes:jpg,jpeg,png|max:2048', + 'duration' => 'required|integer|min:1', // Validasi durasi + ]); + + // Upload gambar jika ada + $imageName = null; + if ($request->hasFile('image')) { + $image = $request->file('image'); + $imageName = time() . '_' . $image->getClientOriginalName(); + $image->move(public_path('storage/gambar'), $imageName); + } + + $service = Service::create([ + 'name' => $request->name, + 'category_id' => $request->category_id, + 'price' => $request->price, + 'description' => $request->description, + 'image' => $imageName, + 'duration' => $request->duration, // Simpan durasi + ]); + + return response()->json([ + 'message' => 'Layanan berhasil ditambahkan', + 'data' => [ + 'id' => $service->id, + 'name' => $service->name, + 'price' => $service->price, + 'description' => $service->description, + 'category_id' => $service->category_id, + 'image' => $service->image + ? url('storage/gambar/' . $service->image) + : null, + 'duration' => $service->duration, // Tambahkan ke response + ] + ], 201); + } + + // Hapus layanan + hapus gambar + public function destroy(Request $request, $id) + { + if ($request->user()->role !== 'pemilik') { + return response()->json(['message' => 'Tidak diizinkan'], 403); + } + + $service = Service::findOrFail($id); + + // Hapus gambar dari storage jika ada + if ($service->image) { + $imagePath = public_path('storage/gambar/' . $service->image); + if (file_exists($imagePath)) { + unlink($imagePath); + } + } + + $service->delete(); + + return response()->json(['message' => 'Layanan berhasil dihapus']); + } +} diff --git a/salon-booking-api/app/Http/Controllers/UploadController.php b/salon-booking-api/app/Http/Controllers/UploadController.php new file mode 100644 index 0000000..94ab21e --- /dev/null +++ b/salon-booking-api/app/Http/Controllers/UploadController.php @@ -0,0 +1,24 @@ +validate([ + 'gambar' => 'required|image|mimes:jpg,jpeg,png|max:2048', + ]); + + $path = $request->file('gambar')->store('public/gambar'); + $url = asset(Storage::url($path)); + + return response()->json([ + 'message' => 'Upload berhasil', + 'url' => $url + ]); + } +} diff --git a/salon-booking-api/app/Http/Kernel.php b/salon-booking-api/app/Http/Kernel.php new file mode 100644 index 0000000..494c050 --- /dev/null +++ b/salon-booking-api/app/Http/Kernel.php @@ -0,0 +1,68 @@ + + */ + protected $middleware = [ + // \App\Http\Middleware\TrustHosts::class, + \App\Http\Middleware\TrustProxies::class, + \Illuminate\Http\Middleware\HandleCors::class, + \App\Http\Middleware\PreventRequestsDuringMaintenance::class, + \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, + \App\Http\Middleware\TrimStrings::class, + \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, + ]; + + /** + * The application's route middleware groups. + * + * @var array+ Laravel has wonderful documentation covering every aspect of the framework. Whether you are a newcomer or have prior experience with Laravel, we recommend reading our documentation from beginning to end. +
++ Laracasts offers thousands of video tutorials on Laravel, PHP, and JavaScript development. Check them out, see for yourself, and massively level up your development skills in the process. +
++ Laravel News is a community driven portal and newsletter aggregating all of the latest and most important news in the Laravel ecosystem, including new package releases and tutorials. +
+