feat: implement barangay system phases 2-14
Complete adaptation from BukidBountyApp to Philippine barangay governance: - Barangay models: Resident, Household, HouseholdMember, Blotter, BlotterHearing, DocumentRequest, RequestPayment, RequestType, BarangayProject, BarangayBudget - Controllers: ResidentController, HouseholdController, BlotterController, BlotterHearingController, DocumentRequestController, RequestTypeController, ProjectController, BudgetController, QRPHController, AdminConsoleController, UserController, FileController, ChapterController, LoginController - Vue pages: Home, ManageResidents, ResidentProfile, ManageHouseholds, ManageBlotters, BlotterDetail, RequestDocument, ManageDocumentRequests, DocumentRequestDetail, ManageRequestTypes, ManageProjects, BudgetLedger, AdminConsole - Barangay roles: PunongBarangay, Kagawad, Secretary, Treasurer, SK, Tanod, BHW, Staff, Resident - UserPermissions matrix rewritten with barangay-specific permission mappings - VueRouteMap replaced with barangay SPA routes - UserActions enum references corrected across all controllers - Removed all market/cooperative/POS/subscription code and models
This commit is contained in:
188
resources/js/Pages/Barangay/DocumentRequestDetail.vue
Normal file
188
resources/js/Pages/Barangay/DocumentRequestDetail.vue
Normal file
@@ -0,0 +1,188 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, computed } from 'vue';
|
||||
import { usePageTitle } from '../../composables/Core/usePageTitle';
|
||||
import { executeRequest } from '../../utils/executeRequest.js';
|
||||
import { navigate } from '../../utils/navigate.js';
|
||||
import { useAuth } from '../../composables/Core/useAuth.js';
|
||||
|
||||
usePageTitle('Document Request Detail');
|
||||
|
||||
const { isBarangayStaff } = useAuth();
|
||||
|
||||
const request = ref(null);
|
||||
const loading = ref(false);
|
||||
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const target = urlParams.get('target');
|
||||
|
||||
const statusColors = {
|
||||
PENDING: 'bg-yellow-100 text-yellow-700',
|
||||
PAYMENT_PENDING: 'bg-orange-100 text-orange-700',
|
||||
PAID: 'bg-blue-100 text-blue-700',
|
||||
PROCESSING:'bg-indigo-100 text-indigo-700',
|
||||
READY: 'bg-teal-100 text-teal-700',
|
||||
CLAIMED: 'bg-green-100 text-green-700',
|
||||
CANCELLED: 'bg-red-100 text-red-700',
|
||||
};
|
||||
|
||||
const statusLabels = {
|
||||
PENDING: 'Pending',
|
||||
PAYMENT_PENDING: 'Awaiting Payment',
|
||||
PAID: 'Paid',
|
||||
PROCESSING:'Processing',
|
||||
READY: 'Ready for Pickup',
|
||||
CLAIMED: 'Claimed',
|
||||
CANCELLED: 'Cancelled',
|
||||
};
|
||||
|
||||
const getStatus = computed(() => request.value?.status?.value ?? request.value?.status ?? '');
|
||||
|
||||
const loadRequest = async () => {
|
||||
loading.value = true;
|
||||
const res = await executeRequest('/admin/documents/show', 'POST', { target });
|
||||
if (res.success) request.value = res.data;
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
const confirmPayment = async () => {
|
||||
if (!confirm('Confirm payment received?')) return;
|
||||
const res = await executeRequest('/admin/documents/confirm-payment', 'POST', {
|
||||
target,
|
||||
amount_paid: request.value?.base_fee ?? 0,
|
||||
payment_method: 'CASH',
|
||||
});
|
||||
if (res.success) loadRequest();
|
||||
};
|
||||
|
||||
const markReady = async () => {
|
||||
if (!confirm('Mark as ready for pickup?')) return;
|
||||
const res = await executeRequest('/admin/documents/mark-ready', 'POST', { target });
|
||||
if (res.success) loadRequest();
|
||||
};
|
||||
|
||||
const markClaimed = async () => {
|
||||
if (!confirm('Mark as claimed by resident?')) return;
|
||||
const res = await executeRequest('/admin/documents/mark-claimed', 'POST', { target });
|
||||
if (res.success) loadRequest();
|
||||
};
|
||||
|
||||
const cancel = async () => {
|
||||
if (!confirm('Cancel this request?')) return;
|
||||
const res = await executeRequest('/documents/cancel', 'POST', { target });
|
||||
if (res.success) loadRequest();
|
||||
};
|
||||
|
||||
onMounted(loadRequest);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="p-4 max-w-3xl mx-auto">
|
||||
<button @click="navigate(isBarangayStaff ? '/barangay/managedocumentrequests' : '/barangay/requestdocument')"
|
||||
class="text-sm text-blue-500 mb-4 inline-flex items-center gap-1">
|
||||
← Back
|
||||
</button>
|
||||
|
||||
<div v-if="loading" class="text-center py-8 text-gray-400">Loading...</div>
|
||||
|
||||
<div v-else-if="request">
|
||||
<!-- Header card -->
|
||||
<div class="bg-white rounded-xl shadow p-5 mb-4">
|
||||
<div class="flex justify-between items-start">
|
||||
<div>
|
||||
<div class="flex items-center gap-2 mb-1">
|
||||
<span class="font-mono font-semibold text-blue-600 text-lg">{{ request.request_no }}</span>
|
||||
<span :class="`text-xs px-2 py-0.5 rounded-full font-medium ${statusColors[getStatus] ?? 'bg-gray-100'}`">
|
||||
{{ statusLabels[getStatus] ?? getStatus }}
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-xl font-bold text-gray-800">{{ request.document_type?.name ?? request.request_type }}</p>
|
||||
<p class="text-sm text-gray-500 mt-1">Submitted: {{ request.created_at }}</p>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<div class="text-2xl font-bold text-green-600">
|
||||
{{ request.base_fee > 0 ? `₱${Number(request.base_fee).toFixed(2)}` : 'Free' }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-400">Fee</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Requester info -->
|
||||
<div class="bg-white rounded-xl shadow p-5 mb-4">
|
||||
<h2 class="font-semibold text-gray-700 mb-3 border-b pb-2">Requester Information</h2>
|
||||
<div class="grid grid-cols-2 gap-3 text-sm">
|
||||
<div><span class="text-gray-400">Name:</span> {{ request.resident?.fullname ?? request.requester_name ?? '—' }}</div>
|
||||
<div><span class="text-gray-400">Purok:</span> {{ request.resident?.purok ?? '—' }}</div>
|
||||
<div class="col-span-2"><span class="text-gray-400">Purpose:</span> {{ request.purpose ?? '—' }}</div>
|
||||
<div v-if="request.remarks" class="col-span-2">
|
||||
<span class="text-gray-400">Remarks:</span> {{ request.remarks }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Payment history -->
|
||||
<div v-if="request.payments && request.payments.length" class="bg-white rounded-xl shadow p-5 mb-4">
|
||||
<h2 class="font-semibold text-gray-700 mb-3 border-b pb-2">Payment Records</h2>
|
||||
<div v-for="p in request.payments" :key="p.id" class="flex justify-between text-sm py-1 border-b last:border-0">
|
||||
<span>{{ p.payment_method }} — {{ p.created_at }}</span>
|
||||
<span class="font-semibold text-green-600">₱{{ Number(p.amount_paid).toFixed(2) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Staff actions -->
|
||||
<div v-if="isBarangayStaff" class="bg-white rounded-xl shadow p-5">
|
||||
<h2 class="font-semibold text-gray-700 mb-3">Actions</h2>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<button
|
||||
v-if="['PENDING','PAYMENT_PENDING'].includes(getStatus)"
|
||||
@click="confirmPayment"
|
||||
class="btn-sm bg-green-500 text-white">
|
||||
Confirm Payment
|
||||
</button>
|
||||
<button
|
||||
v-if="['PAID','PROCESSING'].includes(getStatus)"
|
||||
@click="markReady"
|
||||
class="btn-sm bg-teal-500 text-white">
|
||||
Mark Ready for Pickup
|
||||
</button>
|
||||
<button
|
||||
v-if="getStatus === 'READY'"
|
||||
@click="markClaimed"
|
||||
class="btn-sm bg-blue-500 text-white">
|
||||
Mark Claimed
|
||||
</button>
|
||||
<button
|
||||
v-if="!['CLAIMED','CANCELLED'].includes(getStatus)"
|
||||
@click="cancel"
|
||||
class="btn-sm bg-red-100 text-red-600">
|
||||
Cancel Request
|
||||
</button>
|
||||
<p v-if="['CLAIMED','CANCELLED'].includes(getStatus)" class="text-sm text-gray-400">
|
||||
This request is {{ statusLabels[getStatus].toLowerCase() }}. No further actions available.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Resident view: status tracker -->
|
||||
<div v-else class="bg-white rounded-xl shadow p-5">
|
||||
<h2 class="font-semibold text-gray-700 mb-3">Request Progress</h2>
|
||||
<div class="flex items-center gap-1 text-xs">
|
||||
<template v-for="(step, i) in ['PENDING','PAID','PROCESSING','READY','CLAIMED']" :key="step">
|
||||
<div :class="`px-2 py-1 rounded font-medium ${['PENDING','PAYMENT_PENDING','PAID','PROCESSING','READY','CLAIMED'].indexOf(getStatus) >= i ? 'bg-blue-500 text-white' : 'bg-gray-100 text-gray-400'}`">
|
||||
{{ statusLabels[step] ?? step }}
|
||||
</div>
|
||||
<div v-if="i < 4" class="flex-1 h-0.5 bg-gray-200"></div>
|
||||
</template>
|
||||
</div>
|
||||
<p v-if="getStatus === 'READY'" class="mt-3 text-sm text-teal-600 font-medium">
|
||||
Your document is ready for pickup at the barangay hall.
|
||||
</p>
|
||||
<p v-else-if="getStatus === 'CANCELLED'" class="mt-3 text-sm text-red-500">
|
||||
This request has been cancelled.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p v-else class="text-center text-gray-400 py-8">Request not found.</p>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user