MIF_E31221305/TA_website/resources/views/admin/tailors/index.blade.php

977 lines
48 KiB
PHP

@extends('admin.layouts.app')
@section('title', 'Penjahit')
@section('content')
<div class="p-6" x-data="tailorFilters">
<!-- Header -->
<div class="flex flex-col md:flex-row md:justify-between md:items-center gap-4 mb-6">
<h2 class="text-2xl font-semibold text-gray-800">Daftar Penjahit</h2>
<!-- Filter Section -->
<div class="flex flex-wrap items-center gap-3">
<!-- Search -->
<div class="relative">
<input
type="text"
x-model="searchQuery"
@input="filterTailors()"
placeholder="Cari penjahit..."
class="w-full md:w-64 pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
>
<svg class="w-5 h-5 text-gray-400 absolute left-3 top-2.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
</svg>
</div>
<!-- Specialization Filter Dropdown -->
<div class="relative" x-data="{ open: false }">
<button
@click="open = !open"
class="flex items-center gap-2 px-4 py-2 border border-gray-300 rounded-lg hover:border-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500"
>
<span x-text="selectedFilters.length ? `${selectedFilters.length} Filter dipilih` : 'Filter Spesialisasi'"></span>
<svg class="w-5 h-5 text-gray-400" :class="{'rotate-180': open}" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
</svg>
</button>
<!-- Dropdown Menu -->
<div
x-show="open"
@click.away="open = false"
x-transition:enter="transition ease-out duration-100"
x-transition:enter-start="transform opacity-0 scale-95"
x-transition:enter-end="transform opacity-100 scale-100"
x-transition:leave="transition ease-in duration-75"
x-transition:leave-start="transform opacity-100 scale-100"
x-transition:leave-end="transform opacity-0 scale-95"
class="absolute right-0 mt-2 w-72 bg-white rounded-lg shadow-lg z-50"
>
<div class="p-4">
<!-- Categories -->
<template x-for="(specs, category) in specializations" :key="category">
<div class="mb-4">
<h4 class="text-sm font-medium text-gray-700 mb-2" x-text="category"></h4>
<div class="space-y-2">
<template x-for="spec in specs" :key="spec">
<label class="flex items-center">
<input
type="checkbox"
:value="spec"
x-model="selectedFilters"
@change="filterTailors()"
class="w-4 h-4 text-blue-600 rounded border-gray-300 focus:ring-blue-500"
>
<span class="ml-2 text-sm text-gray-600" x-text="spec"></span>
</label>
</template>
</div>
</div>
</template>
<!-- Actions -->
<div class="pt-3 border-t border-gray-200">
<button
@click="clearFilters()"
class="text-sm text-gray-600 hover:text-blue-600 flex items-center gap-1"
>
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
</svg>
Hapus semua filter
</button>
</div>
</div>
</div>
</div>
<button onclick="showAddModal()" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg flex items-center gap-2">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"/>
</svg>
Tambah Penjahit
</button>
</div>
</div>
@if(session('error'))
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative mb-4" role="alert">
<span class="block sm:inline">{{ session('error') }}</span>
</div>
@endif
@if(session('success'))
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded relative mb-4" role="alert">
<span class="block sm:inline">{{ session('success') }}</span>
</div>
@endif
<!-- Tailors Grid -->
<div id="tailorsGrid" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
@forelse($tailors as $tailor)
<div class="tailor-card bg-white rounded-xl shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300"
data-specializations='@json($tailor['specializations'])'
data-name="{{ strtolower($tailor['name']) }}">
<!-- Tailor Header -->
<div class="p-4 border-b">
<div class="flex items-center gap-4">
<div class="w-16 h-16 rounded-full bg-gray-200 flex items-center justify-center overflow-hidden">
@if(isset($tailor['profile']['photo']))
<img src="{{ env('API_URL') . $tailor['profile']['photo'] }}" alt="{{ $tailor['name'] }}" class="w-full h-full object-cover">
@else
<span class="text-2xl text-gray-600">{{ substr($tailor['name'], 0, 2) }}</span>
@endif
</div>
<div>
<h3 class="text-lg font-semibold text-gray-800">{{ $tailor['name'] }}</h3>
<div class="flex items-center gap-2 mt-1">
<div class="flex items-center">
@for($i = 1; $i <= 5; $i++)
@if($i <= $tailor['ratings']['average_rating'])
<svg class="w-4 h-4 text-yellow-400" fill="currentColor" viewBox="0 0 20 20">
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"/>
</svg>
@else
<svg class="w-4 h-4 text-gray-300" fill="currentColor" viewBox="0 0 20 20">
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"/>
</svg>
@endif
@endfor
<span class="ml-1 text-sm text-gray-600">({{ $tailor['ratings']['total_ratings'] }})</span>
</div>
</div>
<p class="text-sm text-gray-600 mt-1">{{ $tailor['phone'] }}</p>
</div>
</div>
</div>
<!-- Shop Info -->
<div class="p-4 border-b">
<h4 class="text-sm font-medium text-gray-700 mb-2">Informasi Toko</h4>
<p class="text-sm text-gray-600">{{ $tailor['profile']['shop_name'] }}</p>
<p class="text-sm text-gray-600">{{ $tailor['profile']['address'] }}</p>
<p class="text-sm text-gray-600 mt-2">{{ $tailor['profile']['description'] }}</p>
</div>
<!-- Specializations -->
@if(count($tailor['specializations']) > 0)
<div class="p-4 border-b">
<h4 class="text-sm font-medium text-gray-700 mb-2">Spesialisasi</h4>
<div class="flex flex-wrap gap-2">
@foreach($tailor['specializations'] as $specialization)
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
{{ $specialization['name'] }}
</span>
@endforeach
</div>
</div>
@endif
<!-- Latest Review -->
@if(isset($tailor['ratings']['reviews']) && count($tailor['ratings']['reviews']) > 0)
<div class="p-4 border-b">
<h4 class="text-sm font-medium text-gray-700 mb-2">Review Terbaru</h4>
<div class="text-sm text-gray-600">
<p class="italic">"{!! nl2br(e($tailor['ratings']['reviews'][0]['comment'])) !!}"</p>
<p class="text-xs text-gray-500 mt-1">{{ \Carbon\Carbon::parse($tailor['ratings']['reviews'][0]['created_at'])->diffForHumans() }}</p>
</div>
</div>
@endif
<!-- Actions -->
<div class="p-4 bg-gray-50 flex justify-end gap-2">
<button onclick="openGallery({{ $tailor['id'] }})" class="text-blue-600 hover:text-blue-800" title="Lihat Galeri">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"/>
</svg>
</button>
<button onclick="showEditModal({{ $tailor['id'] }})" class="text-gray-600 hover:text-gray-800" title="Edit">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"/>
</svg>
</button>
<button onclick="deleteTailor({{ $tailor['id'] }})" class="text-red-600 hover:text-red-800" title="Hapus">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
</svg>
</button>
</div>
</div>
@empty
<div class="col-span-3 text-center py-8">
<p class="text-gray-500">Tidak ada data penjahit</p>
</div>
@endforelse
</div>
</div>
<!-- Gallery Modal -->
<div id="galleryModal" class="fixed inset-0 bg-black bg-opacity-50 hidden z-50">
<div class="min-h-screen px-4 text-center">
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
</div>
<!-- Modal panel -->
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-4xl sm:w-full">
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="sm:flex sm:items-start">
<div class="w-full">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg leading-6 font-medium text-gray-900" id="modal-title">
Galeri Karya
</h3>
<button type="button" onclick="closeGallery()" class="text-gray-400 hover:text-gray-500">
<span class="sr-only">Close</span>
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<div id="galleryContent" class="grid grid-cols-2 md:grid-cols-3 gap-4">
<!-- Gallery items will be loaded here -->
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Add Tailor Modal -->
<div id="addTailorModal" class="fixed inset-0 bg-gray-600 bg-opacity-50 hidden z-50">
<div class="min-h-screen px-4 text-center">
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
</div>
<!-- Modal panel -->
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-2xl sm:w-full">
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="sm:flex sm:items-start">
<div class="w-full">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg leading-6 font-medium text-gray-900">
Tambah Penjahit Baru
</h3>
<button type="button" onclick="closeAddModal()" class="text-gray-400 hover:text-gray-500">
<span class="sr-only">Close</span>
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<form id="addTailorForm" onsubmit="addTailor(event)">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label for="name" class="block text-sm font-medium text-gray-700">Nama</label>
<input type="text" name="name" id="name" required
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm">
</div>
<div>
<label for="email" class="block text-sm font-medium text-gray-700">Email</label>
<input type="email" name="email" id="email" required
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm">
</div>
<div>
<label for="password" class="block text-sm font-medium text-gray-700">Password</label>
<input type="password" name="password" id="password" required
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm">
</div>
<div>
<label for="password_confirmation" class="block text-sm font-medium text-gray-700">Konfirmasi Password</label>
<input type="password" name="password_confirmation" id="password_confirmation" required
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm">
</div>
<div>
<label for="phone_number" class="block text-sm font-medium text-gray-700">Nomor Telepon</label>
<input type="text" name="phone_number" id="phone_number" required
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm">
</div>
<div>
<label for="address" class="block text-sm font-medium text-gray-700">Alamat</label>
<input type="text" name="address" id="address" required
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm">
</div>
<div>
<label for="latitude" class="block text-sm font-medium text-gray-700">Latitude</label>
<div class="flex gap-2">
<input type="number" step="any" name="latitude" id="latitude" required
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm">
<button type="button" onclick="getCurrentLocation()"
class="mt-1 inline-flex items-center px-3 py-2 border border-gray-300 shadow-sm text-sm leading-4 font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"/>
</svg>
Dapatkan Lokasi
</button>
</div>
</div>
<div>
<label for="longitude" class="block text-sm font-medium text-gray-700">Longitude</label>
<input type="number" step="any" name="longitude" id="longitude" required
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm">
</div>
<div class="md:col-span-2">
<label for="shop_description" class="block text-sm font-medium text-gray-700">Deskripsi Toko</label>
<textarea name="shop_description" id="shop_description" rows="3" required
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm"></textarea>
</div>
<div class="md:col-span-2">
<label class="block text-sm font-medium text-gray-700 mb-2">Spesialisasi</label>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4" id="specializationsContainer">
<!-- Specializations will be loaded here -->
</div>
</div>
</div>
<div class="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
<button type="submit"
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm">
Simpan
</button>
<button type="button" onclick="closeAddModal()"
class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:mt-0 sm:w-auto sm:text-sm">
Batal
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Edit Tailor Modal -->
<div id="editTailorModal" class="fixed inset-0 bg-gray-600 bg-opacity-50 hidden z-50">
<div class="min-h-screen px-4 text-center">
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
</div>
<!-- Modal panel -->
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-2xl sm:w-full">
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="sm:flex sm:items-start">
<div class="w-full">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg leading-6 font-medium text-gray-900">
Edit Penjahit
</h3>
<button type="button" onclick="closeEditModal()" class="text-gray-400 hover:text-gray-500">
<span class="sr-only">Close</span>
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<form id="editTailorForm" onsubmit="updateTailor(event)">
<input type="hidden" id="edit_tailor_id">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label for="edit_name" class="block text-sm font-medium text-gray-700">Nama</label>
<input type="text" name="name" id="edit_name" required
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm">
</div>
<div>
<label for="edit_email" class="block text-sm font-medium text-gray-700">Email</label>
<input type="email" name="email" id="edit_email" required
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm">
</div>
<div>
<label for="edit_phone_number" class="block text-sm font-medium text-gray-700">Nomor Telepon</label>
<input type="text" name="phone_number" id="edit_phone_number" required
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm">
</div>
<div>
<label for="edit_address" class="block text-sm font-medium text-gray-700">Alamat</label>
<input type="text" name="address" id="edit_address" required
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm">
</div>
<div class="md:col-span-2">
<label for="edit_shop_description" class="block text-sm font-medium text-gray-700">Deskripsi Toko</label>
<textarea name="shop_description" id="edit_shop_description" rows="3" required
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm"></textarea>
</div>
<div class="md:col-span-2">
<label class="block text-sm font-medium text-gray-700 mb-2">Spesialisasi</label>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4" id="editSpecializationsContainer">
<!-- Specializations will be loaded here -->
</div>
</div>
</div>
<div class="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
<button type="submit"
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm">
Simpan
</button>
<button type="button" onclick="closeEditModal()"
class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:mt-0 sm:w-auto sm:text-sm">
Batal
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
@push('styles')
<link href="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css" rel="stylesheet">
@endpush
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('tailorFilters', () => ({
searchQuery: '',
selectedFilters: [],
specializations: {
'Gaya Busana': ['Casual', 'Formal', 'Tradisional', 'Modern'],
'Jenis Layanan': ['Jahit Baru', 'Perbaikan', 'Design/Custom'],
'Hiasan Busana': ['Border', 'Payet', 'Sulam']
},
filterTailors() {
const cards = document.querySelectorAll('.tailor-card');
const searchLower = this.searchQuery.toLowerCase();
cards.forEach(card => {
const specializations = JSON.parse(card.dataset.specializations);
const name = card.dataset.name;
const matchesSearch = name.includes(searchLower);
const matchesFilters = this.selectedFilters.length === 0 ||
specializations.some(spec => this.selectedFilters.includes(spec.name));
card.style.display = (matchesSearch && matchesFilters) ? '' : 'none';
});
this.updateNoResults();
},
clearFilters() {
this.selectedFilters = [];
this.searchQuery = '';
this.filterTailors();
},
updateNoResults() {
const visibleCards = document.querySelectorAll('.tailor-card[style=""]').length;
const noResultsDiv = document.querySelector('.no-results');
if (visibleCards === 0) {
if (!noResultsDiv) {
const grid = document.getElementById('tailorsGrid');
const div = document.createElement('div');
div.className = 'no-results col-span-3 text-center py-8';
div.innerHTML = '<p class="text-gray-500">Tidak ada penjahit yang sesuai dengan filter</p>';
grid.appendChild(div);
}
} else if (noResultsDiv) {
noResultsDiv.remove();
}
}
}));
});
function openGallery(tailorId) {
const modal = document.getElementById('galleryModal');
const content = document.getElementById('galleryContent');
// Show modal and loading state
modal.classList.remove('hidden');
content.innerHTML = '<div class="col-span-3 text-center py-4">Loading...</div>';
// Get token from session (passed from backend)
const token = '{{ session('api_token') }}';
const tokenType = '{{ session('token_type') }}';
// Fetch gallery data
fetch(window.apiUrl(`api/tailors/${tailorId}/gallery`), {
headers: {
'Accept': 'application/json',
'Authorization': `${tokenType} ${token}`
}
})
.then(response => response.json())
.then(data => {
if (data.success && data.data.length > 0) {
content.innerHTML = data.data.map(item => `
<div class="relative group">
<img src="${window.assetUrl(item.photo)}"
alt="${item.title}"
class="w-full aspect-square object-cover rounded-lg">
<div class="absolute inset-0 bg-black bg-opacity-50 opacity-0 group-hover:opacity-100 transition-opacity duration-200 flex flex-col justify-end p-4 rounded-lg">
<h4 class="text-white font-medium">${item.title}</h4>
<p class="text-white text-sm">${item.description || ''}</p>
<span class="text-white text-xs mt-1">${item.category}</span>
</div>
</div>
`).join('');
} else {
content.innerHTML = '<div class="col-span-3 text-center py-4">Tidak ada foto dalam galeri</div>';
}
})
.catch(error => {
console.error('Error:', error);
content.innerHTML = '<div class="col-span-3 text-center py-4 text-red-600">Gagal memuat galeri</div>';
});
}
function closeGallery() {
const modal = document.getElementById('galleryModal');
modal.classList.add('hidden');
}
// Close modal when clicking outside
document.getElementById('galleryModal').addEventListener('click', function(e) {
if (e.target === this) {
closeGallery();
}
});
async function showAddModal() {
try {
// Get all specializations
const response = await fetch(window.apiUrl('api/specializations'), {
headers: {
'Authorization': '{{ session('token_type') }} {{ session('api_token') }}',
'Accept': 'application/json'
}
});
if (!response.ok) {
throw new Error('Failed to fetch specializations');
}
const data = await response.json();
const specializations = data.data;
// Group specializations by category
const groupedSpecializations = specializations.reduce((acc, spec) => {
if (!acc[spec.category]) {
acc[spec.category] = [];
}
acc[spec.category].push(spec);
return acc;
}, {});
// Render specialization checkboxes
const container = document.getElementById('specializationsContainer');
container.innerHTML = Object.entries(groupedSpecializations).map(([category, specs]) => `
<div class="space-y-2">
<h4 class="text-sm font-medium text-gray-700">${category}</h4>
<div class="space-y-2">
${specs.map(spec => `
<label class="flex items-center">
<input type="checkbox" name="specializations[]" value="${spec.id}"
class="h-4 w-4 text-blue-600 rounded border-gray-300 focus:ring-blue-500">
<span class="ml-2 text-sm text-gray-700">${spec.name}</span>
</label>
`).join('')}
</div>
</div>
`).join('');
// Show modal
document.getElementById('addTailorModal').classList.remove('hidden');
} catch (error) {
console.error('Error:', error);
Swal.fire({
title: 'Error!',
text: 'Gagal memuat data spesialisasi',
icon: 'error',
confirmButtonText: 'OK'
});
}
}
function closeAddModal() {
document.getElementById('addTailorModal').classList.add('hidden');
}
async function addTailor(event) {
event.preventDefault();
const form = event.target;
const formData = new FormData(form);
// Get selected specializations
const selectedSpecializations = Array.from(formData.getAll('specializations[]')).map(id => parseInt(id));
try {
const response = await fetch(window.apiUrl('api/penjahit/register'), {
method: 'POST',
headers: {
'Authorization': '{{ session('token_type') }} {{ session('api_token') }}',
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
name: formData.get('name'),
email: formData.get('email'),
password: formData.get('password'),
password_confirmation: formData.get('password_confirmation'),
phone_number: formData.get('phone_number'),
address: formData.get('address'),
latitude: parseFloat(formData.get('latitude')),
longitude: parseFloat(formData.get('longitude')),
shop_description: formData.get('shop_description'),
specializations: selectedSpecializations
})
});
const responseData = await response.json();
console.log('API Response:', responseData); // Debug log
if (!response.ok) {
// Handle validation errors
if (response.status === 422) {
let errorMessage = 'Gagal menambahkan penjahit:\n';
if (responseData.errors) {
Object.keys(responseData.errors).forEach(key => {
errorMessage += `- ${responseData.errors[key].join(', ')}\n`;
});
} else {
errorMessage += responseData.message || 'Terjadi kesalahan validasi';
}
Swal.fire({
title: 'Validasi Error',
text: errorMessage,
icon: 'error',
confirmButtonText: 'OK'
});
return;
}
throw new Error(responseData.message || 'Failed to add tailor');
}
if (responseData.success) {
Swal.fire({
title: 'Berhasil!',
text: responseData.message || 'Penjahit berhasil ditambahkan',
icon: 'success',
confirmButtonText: 'OK'
}).then((result) => {
if (result.isConfirmed) {
closeAddModal();
window.location.reload();
}
});
} else {
throw new Error(responseData.message || 'Failed to add tailor');
}
} catch (error) {
console.error('Error:', error);
Swal.fire({
title: 'Gagal!',
text: error.message || 'Gagal menambahkan penjahit. Silakan coba lagi nanti.',
icon: 'error',
confirmButtonText: 'OK'
});
}
}
function getCurrentLocation() {
if (!navigator.geolocation) {
Swal.fire({
title: 'Error!',
text: 'Geolocation tidak didukung oleh browser Anda',
icon: 'error',
confirmButtonText: 'OK'
});
return;
}
// Show loading state
Swal.fire({
title: 'Mendapatkan lokasi...',
text: 'Mohon tunggu sebentar',
allowOutsideClick: false,
didOpen: () => {
Swal.showLoading();
}
});
navigator.geolocation.getCurrentPosition(
(position) => {
const latitude = position.coords.latitude;
const longitude = position.coords.longitude;
// Update form fields
document.getElementById('latitude').value = latitude;
document.getElementById('longitude').value = longitude;
// Close loading
Swal.close();
// Show success message
Swal.fire({
title: 'Berhasil!',
text: 'Lokasi berhasil didapatkan',
icon: 'success',
confirmButtonText: 'OK'
});
},
(error) => {
let errorMessage = 'Gagal mendapatkan lokasi: ';
switch (error.code) {
case error.PERMISSION_DENIED:
errorMessage += 'Akses lokasi ditolak';
break;
case error.POSITION_UNAVAILABLE:
errorMessage += 'Informasi lokasi tidak tersedia';
break;
case error.TIMEOUT:
errorMessage += 'Permintaan lokasi timeout';
break;
default:
errorMessage += 'Terjadi kesalahan yang tidak diketahui';
break;
}
Swal.fire({
title: 'Error!',
text: errorMessage,
icon: 'error',
confirmButtonText: 'OK'
});
},
{
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 0
}
);
}
async function deleteTailor(id) {
Swal.fire({
title: 'Apakah Anda yakin?',
text: "Data penjahit akan dihapus secara permanen!",
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#4F46E5',
cancelButtonColor: '#6B7280',
confirmButtonText: 'Ya, hapus!',
cancelButtonText: 'Batal'
}).then(async (result) => {
if (result.isConfirmed) {
try {
const response = await fetch(window.apiUrl(`api/tailors/${id}`), {
method: 'DELETE',
headers: {
'Authorization': '{{ session('token_type') }} {{ session('api_token') }}',
'Accept': 'application/json'
}
});
const responseData = await response.json();
if (!response.ok) {
throw new Error(responseData.message || 'Gagal menghapus penjahit');
}
if (responseData.status === 'success') {
Swal.fire({
title: 'Berhasil!',
text: responseData.message || 'Penjahit berhasil dihapus',
icon: 'success',
confirmButtonText: 'OK',
confirmButtonColor: '#4F46E5'
}).then((result) => {
if (result.isConfirmed) {
window.location.reload();
}
});
} else {
throw new Error(responseData.message || 'Gagal menghapus penjahit');
}
} catch (error) {
console.error('Error:', error);
Swal.fire({
title: 'Gagal!',
text: error.message || 'Gagal menghapus penjahit. Silakan coba lagi nanti.',
icon: 'error',
confirmButtonText: 'OK',
confirmButtonColor: '#4F46E5'
});
}
}
});
}
async function showEditModal(id) {
try {
// Get tailor data
const response = await fetch(window.apiUrl(`api/tailors/${id}`), {
headers: {
'Authorization': '{{ session('token_type') }} {{ session('api_token') }}',
'Accept': 'application/json'
}
});
if (!response.ok) {
throw new Error('Failed to fetch tailor data');
}
const responseData = await response.json();
console.log('Tailor Data:', responseData); // Debug log
if (!responseData.data) {
throw new Error('Invalid response format');
}
const tailor = responseData.data;
// Get all specializations
const specializationsResponse = await fetch(window.apiUrl('api/specializations'), {
headers: {
'Authorization': '{{ session('token_type') }} {{ session('api_token') }}',
'Accept': 'application/json'
}
});
if (!specializationsResponse.ok) {
throw new Error('Failed to fetch specializations');
}
const specializationsData = await specializationsResponse.json();
const specializations = specializationsData.data;
// Fill form with tailor data
document.getElementById('edit_tailor_id').value = tailor.id;
document.getElementById('edit_name').value = tailor.name || '';
document.getElementById('edit_email').value = tailor.email || '';
document.getElementById('edit_phone_number').value = tailor.phone_number || tailor.phone || '';
document.getElementById('edit_address').value = tailor.address || (tailor.profile ? tailor.profile.address : '') || '';
document.getElementById('edit_shop_description').value = tailor.shop_description || (tailor.profile ? tailor.profile.description : '') || '';
// Group specializations by category
const groupedSpecializations = specializations.reduce((acc, spec) => {
if (!acc[spec.category]) {
acc[spec.category] = [];
}
acc[spec.category].push(spec);
return acc;
}, {});
// Get current specialization IDs
const currentSpecializations = tailor.specializations || tailor.specialization_ids || [];
const currentSpecializationIds = currentSpecializations.map(spec =>
typeof spec === 'object' ? spec.id : spec
);
// Render specialization checkboxes
const container = document.getElementById('editSpecializationsContainer');
container.innerHTML = Object.entries(groupedSpecializations).map(([category, specs]) => `
<div class="space-y-2">
<h4 class="text-sm font-medium text-gray-700">${category}</h4>
<div class="space-y-2">
${specs.map(spec => `
<label class="flex items-center">
<input type="checkbox" name="specializations[]" value="${spec.id}"
${currentSpecializationIds.includes(spec.id) ? 'checked' : ''}
class="h-4 w-4 text-blue-600 rounded border-gray-300 focus:ring-blue-500">
<span class="ml-2 text-sm text-gray-700">${spec.name}</span>
</label>
`).join('')}
</div>
</div>
`).join('');
// Show modal
document.getElementById('editTailorModal').classList.remove('hidden');
} catch (error) {
console.error('Error:', error);
Swal.fire({
title: 'Error!',
text: 'Gagal memuat data penjahit: ' + error.message,
icon: 'error',
confirmButtonText: 'OK',
confirmButtonColor: '#4F46E5'
});
}
}
function closeEditModal() {
document.getElementById('editTailorModal').classList.add('hidden');
}
async function updateTailor(event) {
event.preventDefault();
const form = event.target;
const formData = new FormData(form);
const tailorId = document.getElementById('edit_tailor_id').value;
// Get selected specializations
const selectedSpecializations = Array.from(formData.getAll('specializations[]')).map(id => parseInt(id));
try {
const response = await fetch(window.apiUrl(`api/tailors/${tailorId}`), {
method: 'PUT',
headers: {
'Authorization': '{{ session('token_type') }} {{ session('api_token') }}',
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
name: formData.get('name'),
email: formData.get('email'),
phone_number: formData.get('phone_number'),
address: formData.get('address'),
shop_description: formData.get('shop_description'),
specialization_ids: selectedSpecializations
})
});
const responseData = await response.json();
if (!response.ok) {
throw new Error(responseData.message || 'Gagal mengupdate penjahit');
}
Swal.fire({
title: 'Berhasil!',
text: 'Data penjahit berhasil diperbarui',
icon: 'success',
confirmButtonText: 'OK',
confirmButtonColor: '#4F46E5'
}).then((result) => {
if (result.isConfirmed) {
closeEditModal();
window.location.reload();
}
});
} catch (error) {
console.error('Error:', error);
Swal.fire({
title: 'Gagal!',
text: error.message || 'Gagal mengupdate penjahit. Silakan coba lagi nanti.',
icon: 'error',
confirmButtonText: 'OK',
confirmButtonColor: '#4F46E5'
});
}
}
// Update the edit button to use showEditModal
document.querySelectorAll('[data-edit-tailor]').forEach(button => {
button.addEventListener('click', (e) => {
e.preventDefault();
const tailorId = button.dataset.editTailor;
showEditModal(tailorId);
});
});
</script>
@endpush