Files
Verde-Web/resources/views/store/profile.blade.php

350 lines
21 KiB
PHP

@extends('store.layouts.app', ['pageTitle' => 'Store Profile', 'pageSubtitle' => 'Your business and account details'])
@push('styles')
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<style>
#store-map { height: 280px; width: 100%; border-radius: 0.75rem; z-index: 1; }
</style>
@endpush
@section('page')
<div class="space-y-8 max-w-5xl">
{{-- Main Profile Overview Card --}}
<div class="rounded-2xl border border-neutral-200 bg-white p-8 shadow-sm">
<div class="flex flex-col md:flex-row md:items-center justify-between gap-6 pb-6 border-b border-neutral-100">
<div class="flex items-center gap-4">
<div class="flex h-16 w-16 items-center justify-center rounded-2xl bg-verde-50 text-verde-600">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="h-8 w-8 lucide lucide-store"><path d="m2 7 4.41-4.41A2 2 0 0 1 7.83 2h8.34a2 2 0 0 1 1.42.59L22 7"/><path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"/><path d="M15 22v-4a2 2 0 0 0-2-2h-2a2 2 0 0 0-2 2v4"/><path d="M2 7h20"/><path d="M22 7v3a2 2 0 0 1-2 2v0a2.7 2.7 0 0 1-1.59-.63.7.7 0 0 0-.82 0A2.7 2.7 0 0 1 16 12a2.7 2.7 0 0 1-1.59-.63.7.7 0 0 0-.82 0A2.7 2.7 0 0 1 12 12a2.7 2.7 0 0 1-1.59-.63.7.7 0 0 0-.82 0A2.7 2.7 0 0 1 8 12a2.7 2.7 0 0 1-1.59-.63.7.7 0 0 0-.82 0A2.7 2.7 0 0 1 4 12v0a2 2 0 0 1-2-2V7"/></svg>
</div>
<div>
<h3 id="profile-business-name" class="text-xl font-bold text-neutral-900">Loading store...</h3>
<div class="mt-1 flex items-center gap-2">
<span id="profile-status-badge" class="rounded-full px-2 py-0.5 text-[10px] font-bold uppercase tracking-tight bg-neutral-100 text-neutral-600">Pending</span>
<span class="text-xs text-neutral-400">·</span>
<span id="profile-permit" class="text-xs text-neutral-500 font-mono">Permit: -</span>
</div>
</div>
</div>
<div>
<button type="button" id="btn-edit-profile" class="flex items-center gap-2 rounded-xl bg-neutral-900 hover:bg-neutral-800 active:scale-95 px-5 py-2.5 text-xs font-bold text-white shadow-lg shadow-neutral-950/10 transition-all">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-pencil"><path d="M12 20h9"/><path d="M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4Z"/></svg>
<span>Edit Profile</span>
</button>
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8 pt-6">
{{-- Left column: Details --}}
<div class="lg:col-span-2 space-y-8">
{{-- Business Details --}}
<div class="space-y-4">
<h4 class="text-xs font-bold uppercase tracking-widest text-neutral-400">Business Registration</h4>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="flex justify-between py-2 border-b border-neutral-50 text-sm">
<span class="text-neutral-500">Business Name</span>
<span id="detail-business-name" class="font-semibold text-neutral-900"></span>
</div>
<div class="flex justify-between py-2 border-b border-neutral-50 text-sm">
<span class="text-neutral-500">Business Permit</span>
<span id="detail-permit" class="font-semibold text-neutral-900 font-mono"></span>
</div>
<div class="flex justify-between py-2 border-b border-neutral-50 text-sm">
<span class="text-neutral-500">Commission Rate</span>
<span id="detail-commission" class="font-semibold text-neutral-900"></span>
</div>
<div class="flex justify-between py-2 border-b border-neutral-50 text-sm">
<span class="text-neutral-500">Operating Hours</span>
<span id="detail-hours" class="font-semibold text-neutral-900"></span>
</div>
</div>
</div>
{{-- Location Details --}}
<div class="space-y-4">
<h4 class="text-xs font-bold uppercase tracking-widest text-neutral-400">Address & Location</h4>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="flex justify-between py-2 border-b border-neutral-50 text-sm">
<span class="text-neutral-500">Address Line</span>
<span id="detail-address" class="font-semibold text-neutral-900 truncate max-w-[200px]" title=""></span>
</div>
<div class="flex justify-between py-2 border-b border-neutral-50 text-sm">
<span class="text-neutral-500">Barangay</span>
<span id="detail-barangay" class="font-semibold text-neutral-900"></span>
</div>
<div class="flex justify-between py-2 border-b border-neutral-50 text-sm">
<span class="text-neutral-500">LGU</span>
<span id="detail-lgu" class="font-semibold text-neutral-900"></span>
</div>
<div class="flex justify-between py-2 border-b border-neutral-50 text-sm">
<span class="text-neutral-500">Coordinates</span>
<span id="detail-coords" class="font-semibold text-neutral-900 font-mono"></span>
</div>
</div>
</div>
</div>
{{-- Right column: Pinned Location Map --}}
<div class="lg:col-span-1 space-y-4">
<h4 class="text-xs font-bold uppercase tracking-widest text-neutral-400">Store Pinned Location</h4>
<div class="rounded-xl border border-neutral-200 overflow-hidden bg-neutral-50 shadow-sm relative">
<div id="store-map"></div>
</div>
</div>
</div>
</div>
{{-- Owner Details Card --}}
<div class="rounded-2xl border border-neutral-200 bg-white p-8 shadow-sm">
<h4 class="text-xs font-bold uppercase tracking-widest text-neutral-400 mb-4">Owner Profile</h4>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="p-4 rounded-xl bg-neutral-50/50 border border-neutral-100 flex items-center gap-3">
<div class="flex h-10 w-10 items-center justify-center rounded-lg bg-blue-50 text-blue-600">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-user"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
</div>
<div>
<p class="text-[10px] font-bold uppercase text-neutral-400 tracking-wider">Full Name</p>
<p id="owner-name" class="text-sm font-semibold text-neutral-900"></p>
</div>
</div>
<div class="p-4 rounded-xl bg-neutral-50/50 border border-neutral-100 flex items-center gap-3">
<div class="flex h-10 w-10 items-center justify-center rounded-lg bg-orange-50 text-orange-600">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-mail"><rect width="20" height="16" x="2" y="4" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/></svg>
</div>
<div>
<p class="text-[10px] font-bold uppercase text-neutral-400 tracking-wider">Email Address</p>
<p id="owner-email" class="text-sm font-semibold text-neutral-900 truncate max-w-[180px]" title=""></p>
</div>
</div>
<div class="p-4 rounded-xl bg-neutral-50/50 border border-neutral-100 flex items-center gap-3">
<div class="flex h-10 w-10 items-center justify-center rounded-lg bg-emerald-50 text-emerald-600">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-phone"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"/></svg>
</div>
<div>
<p class="text-[10px] font-bold uppercase text-neutral-400 tracking-wider">Phone Number</p>
<p id="owner-phone" class="text-sm font-semibold text-neutral-900"></p>
</div>
</div>
</div>
</div>
</div>
{{-- Edit Profile Modal --}}
<div id="edit-profile-modal" class="fixed inset-0 z-50 hidden flex items-center justify-center bg-neutral-900/50 backdrop-blur-sm">
<div class="w-full max-w-lg rounded-2xl bg-white p-8 shadow-2xl ring-1 ring-neutral-200 animate-in fade-in zoom-in-95 duration-200">
<div class="flex items-center justify-between border-b border-neutral-100 pb-4 mb-6">
<h3 class="text-lg font-bold text-neutral-900">Edit Store Profile</h3>
<button type="button" id="btn-close-modal" class="text-neutral-400 hover:text-neutral-600 transition-colors">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
</button>
</div>
<form id="edit-profile-form" class="space-y-4">
<div>
<label class="block text-xs font-bold uppercase tracking-wider text-neutral-400 mb-1">Business Name</label>
<input type="text" id="edit-business-name" name="business_name" required class="w-full rounded-xl border-neutral-200 focus:ring-verde-500 focus:border-verde-500 py-2.5 px-4 text-sm font-medium">
</div>
<div>
<label class="block text-xs font-bold uppercase tracking-wider text-neutral-400 mb-1">Business Permit Number</label>
<input type="text" id="edit-business-permit" name="business_permit_number" class="w-full rounded-xl border-neutral-200 focus:ring-verde-500 focus:border-verde-500 py-2.5 px-4 text-sm font-medium font-mono">
</div>
<div>
<label class="block text-xs font-bold uppercase tracking-wider text-neutral-400 mb-1">Address Line</label>
<input type="text" id="edit-address-line" name="address_line" required class="w-full rounded-xl border-neutral-200 focus:ring-verde-500 focus:border-verde-500 py-2.5 px-4 text-sm font-medium">
</div>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-xs font-bold uppercase tracking-wider text-neutral-400 mb-1">Open Hours</label>
<input type="text" placeholder="e.g. 08:00 AM" id="edit-hours-open" name="hours_open" class="w-full rounded-xl border-neutral-200 focus:ring-verde-500 focus:border-verde-500 py-2.5 px-4 text-sm font-medium">
</div>
<div>
<label class="block text-xs font-bold uppercase tracking-wider text-neutral-400 mb-1">Close Hours</label>
<input type="text" placeholder="e.g. 09:00 PM" id="edit-hours-close" name="hours_close" class="w-full rounded-xl border-neutral-200 focus:ring-verde-500 focus:border-verde-500 py-2.5 px-4 text-sm font-medium">
</div>
</div>
<div>
<label class="block text-xs font-bold uppercase tracking-wider text-neutral-400 mb-1">Owner Phone Number</label>
<input type="text" id="edit-owner-phone" name="owner_phone" class="w-full rounded-xl border-neutral-200 focus:ring-verde-500 focus:border-verde-500 py-2.5 px-4 text-sm font-medium">
</div>
<div class="pt-4 flex justify-end gap-3">
<button type="button" id="btn-cancel-edit" class="rounded-xl border border-neutral-200 bg-white hover:bg-neutral-50 px-6 py-2.5 text-sm font-bold text-neutral-600 transition-colors">Cancel</button>
<button type="submit" class="rounded-xl bg-verde-600 hover:bg-verde-700 active:scale-95 px-6 py-2.5 text-sm font-bold text-white shadow-lg shadow-verde-600/20 transition-all">Save Changes</button>
</div>
</form>
</div>
</div>
@endsection
@push('scripts')
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script type="module">
document.addEventListener('DOMContentLoaded', () => {
let storeData = null;
let map = null;
let marker = null;
let boundaryLayer = null;
const modal = document.getElementById('edit-profile-modal');
const openBtn = document.getElementById('btn-edit-profile');
const closeBtn = document.getElementById('btn-close-modal');
const cancelBtn = document.getElementById('btn-cancel-edit');
const form = document.getElementById('edit-profile-form');
// Initialize Map
const initMap = (lat, lng) => {
if (!map) {
map = L.map('store-map', {
zoomControl: false,
attributionControl: false
}).setView([lat, lng], 16);
L.tileLayer('https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png', {
maxZoom: 19
}).addTo(map);
L.control.zoom({ position: 'bottomright' }).addTo(map);
} else {
map.setView([lat, lng], 16);
}
// Draw/Update Marker
if (marker) {
marker.setLatLng([lat, lng]);
} else {
marker = L.marker([lat, lng]).addTo(map);
}
};
const openModal = () => {
if (!storeData) return;
document.getElementById('edit-business-name').value = storeData.business_name || '';
document.getElementById('edit-business-permit').value = storeData.business_permit_number || '';
document.getElementById('edit-address-line').value = storeData.address_line || '';
document.getElementById('edit-hours-open').value = storeData.operating_hours?.open || '';
document.getElementById('edit-hours-close').value = storeData.operating_hours?.close || '';
document.getElementById('edit-owner-phone').value = storeData.owner?.phone || '';
modal.classList.remove('hidden');
};
const closeModal = () => {
modal.classList.add('hidden');
};
openBtn.addEventListener('click', openModal);
closeBtn.addEventListener('click', closeModal);
cancelBtn.addEventListener('click', closeModal);
const loadProfile = async () => {
try {
const res = await window.Verde.apiFetch('/api/v1/store/profile');
const data = res.body?.data;
if (!data) return;
storeData = data;
// Overview header
document.getElementById('profile-business-name').textContent = data.business_name;
document.getElementById('profile-permit').textContent = `Permit: ${data.business_permit_number || 'N/A'}`;
const badge = document.getElementById('profile-status-badge');
badge.textContent = data.status;
if (data.status === 'active') {
badge.className = 'rounded-full px-2 py-0.5 text-[10px] font-bold uppercase tracking-tight bg-emerald-50 text-emerald-700';
} else if (data.status === 'suspended') {
badge.className = 'rounded-full px-2 py-0.5 text-[10px] font-bold uppercase tracking-tight bg-red-50 text-red-700';
} else {
badge.className = 'rounded-full px-2 py-0.5 text-[10px] font-bold uppercase tracking-tight bg-orange-50 text-orange-700';
}
// Registration details
document.getElementById('detail-business-name').textContent = data.business_name;
document.getElementById('detail-permit').textContent = data.business_permit_number || 'N/A';
document.getElementById('detail-commission').textContent = `${data.commission_rate_percent}%`;
let hoursText = 'Not Set';
if (data.operating_hours) {
if (typeof data.operating_hours === 'object') {
hoursText = data.operating_hours.open && data.operating_hours.close
? `${data.operating_hours.open} - ${data.operating_hours.close}`
: JSON.stringify(data.operating_hours);
} else {
hoursText = data.operating_hours;
}
}
document.getElementById('detail-hours').textContent = hoursText;
// Location details
const addrEl = document.getElementById('detail-address');
addrEl.textContent = data.address_line || 'N/A';
addrEl.title = data.address_line || '';
document.getElementById('detail-barangay').textContent = data.barangay_name;
document.getElementById('detail-lgu').textContent = data.lgu_name;
document.getElementById('detail-coords').textContent = data.coordinates
? `${data.coordinates.lat.toFixed(5)}, ${data.coordinates.lng.toFixed(5)}`
: 'N/A';
// Owner info
document.getElementById('owner-name').textContent = data.owner?.name || 'N/A';
const emailEl = document.getElementById('owner-email');
emailEl.textContent = data.owner?.email || 'N/A';
emailEl.title = data.owner?.email || '';
document.getElementById('owner-phone').textContent = data.owner?.phone || 'N/A';
// Map rendering
if (data.coordinates) {
initMap(data.coordinates.lat, data.coordinates.lng);
}
} catch (e) {
console.error('Store profile load failed', e);
window.Verde.toast('Failed to load store profile details.', 'error');
}
};
form.addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(form);
const payload = {
business_name: formData.get('business_name'),
business_permit_number: formData.get('business_permit_number'),
address_line: formData.get('address_line'),
operating_hours: {
open: formData.get('hours_open'),
close: formData.get('hours_close')
},
owner_phone: formData.get('owner_phone')
};
try {
const res = await window.Verde.apiFetch('/api/v1/store/profile', {
method: 'PUT',
body: payload
});
if (res.ok) {
window.Verde.toast('Store profile updated successfully!', 'success');
closeModal();
await loadProfile();
} else {
const errorMsg = res.body?.message || 'Failed to update store profile.';
window.Verde.toast(errorMsg, 'error');
}
} catch (err) {
console.error('Failed to update store profile', err);
window.Verde.toast('Failed to update store profile.', 'error');
}
});
loadProfile();
});
</script>
@endpush