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
114 lines
5.1 KiB
Vue
114 lines
5.1 KiB
Vue
<script setup>
|
|
import { ref, onMounted } from 'vue';
|
|
import { usePageTitle } from '../../composables/Core/usePageTitle';
|
|
import { executeRequest } from '../../utils/executeRequest.js';
|
|
import { navigate } from '../../utils/navigate.js';
|
|
|
|
usePageTitle('Document Requests');
|
|
|
|
const requests = ref([]);
|
|
const loading = ref(false);
|
|
const statusFilter = ref('');
|
|
const search = ref('');
|
|
|
|
const statusLabels = {
|
|
DRAFT: 'Draft', PENDING_PAYMENT: 'Pending Payment', PAID: 'Paid',
|
|
PROCESSING: 'Processing', READY: 'Ready for Pickup', CLAIMED: 'Claimed', CANCELLED: 'Cancelled',
|
|
};
|
|
|
|
const statusColors = {
|
|
DRAFT: 'bg-gray-100 text-gray-700',
|
|
PENDING_PAYMENT: 'bg-yellow-100 text-yellow-700',
|
|
PAID: 'bg-blue-100 text-blue-700',
|
|
PROCESSING: 'bg-orange-100 text-orange-700',
|
|
READY: 'bg-teal-100 text-teal-700',
|
|
CLAIMED: 'bg-green-100 text-green-700',
|
|
CANCELLED: 'bg-red-100 text-red-700',
|
|
};
|
|
|
|
const loadRequests = async () => {
|
|
loading.value = true;
|
|
const params = new URLSearchParams();
|
|
if (statusFilter.value) params.append('status', statusFilter.value);
|
|
if (search.value) params.append('search', search.value);
|
|
const res = await executeRequest(`/admin/documents?${params}`);
|
|
if (res.success) requests.value = res.data.data ?? res.data;
|
|
loading.value = false;
|
|
};
|
|
|
|
const viewRequest = (item) => navigate(`/Barangay/DocumentRequestDetail?target=${item.hashkey}`);
|
|
|
|
const quickAction = async (hashkey, action) => {
|
|
const urlMap = {
|
|
confirm: '/admin/documents/confirm-payment',
|
|
ready: '/admin/documents/mark-ready',
|
|
claim: '/admin/documents/mark-claimed',
|
|
};
|
|
await executeRequest(urlMap[action], 'POST', { target: hashkey, method: 'CASH' });
|
|
loadRequests();
|
|
};
|
|
|
|
const getStatus = (item) => item.status?.value ?? item.status;
|
|
|
|
onMounted(loadRequests);
|
|
</script>
|
|
|
|
<template>
|
|
<div class="p-4 max-w-6xl mx-auto">
|
|
<div class="flex items-center justify-between mb-4">
|
|
<h1 class="text-2xl font-bold">Document Requests</h1>
|
|
<button @click="navigate('/Barangay/RequestDocument')" class="btn-primary">+ New Request</button>
|
|
</div>
|
|
|
|
<div class="flex gap-2 mb-4">
|
|
<input v-model="search" @input="loadRequests" placeholder="Search request no..." class="input flex-1" />
|
|
<select v-model="statusFilter" @change="loadRequests" class="input w-44">
|
|
<option value="">All Status</option>
|
|
<option v-for="(label, val) in statusLabels" :key="val" :value="val">{{ label }}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div v-if="loading" class="text-center py-8 text-gray-400">Loading...</div>
|
|
<div v-else>
|
|
<table class="w-full text-sm">
|
|
<thead class="bg-gray-100">
|
|
<tr>
|
|
<th class="text-left p-2">Request No.</th>
|
|
<th class="text-left p-2">Type</th>
|
|
<th class="text-left p-2">Resident</th>
|
|
<th class="text-left p-2">Fee</th>
|
|
<th class="text-left p-2">Payment</th>
|
|
<th class="text-left p-2">Status</th>
|
|
<th class="text-left p-2">Date</th>
|
|
<th class="p-2"></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="r in requests" :key="r.id" class="border-b hover:bg-gray-50">
|
|
<td class="p-2 font-mono text-xs">{{ r.request_no }}</td>
|
|
<td class="p-2">{{ r.request_type?.name }}</td>
|
|
<td class="p-2">{{ r.resident?.name }}</td>
|
|
<td class="p-2">₱{{ Number(r.fee_amount).toFixed(2) }}</td>
|
|
<td class="p-2 capitalize text-xs">
|
|
{{ (r.payment_status?.value ?? r.payment_status)?.toLowerCase() }}
|
|
</td>
|
|
<td class="p-2">
|
|
<span :class="`px-2 py-0.5 rounded-full text-xs font-medium ${statusColors[getStatus(r)] ?? 'bg-gray-100'}`">
|
|
{{ statusLabels[getStatus(r)] ?? getStatus(r) }}
|
|
</span>
|
|
</td>
|
|
<td class="p-2 text-xs text-gray-500">{{ r.created_at?.slice(0, 10) }}</td>
|
|
<td class="p-2 flex gap-1">
|
|
<button @click="viewRequest(r)" class="btn-sm">View</button>
|
|
<button v-if="getStatus(r) === 'PENDING_PAYMENT'" @click="quickAction(r.hashkey, 'confirm')" class="btn-sm bg-blue-500 text-white">Pay</button>
|
|
<button v-if="getStatus(r) === 'PROCESSING'" @click="quickAction(r.hashkey, 'ready')" class="btn-sm bg-teal-500 text-white">Ready</button>
|
|
<button v-if="getStatus(r) === 'READY'" @click="quickAction(r.hashkey, 'claim')" class="btn-sm bg-green-500 text-white">Claimed</button>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<p v-if="!requests.length" class="text-center text-gray-400 py-8">No document requests found.</p>
|
|
</div>
|
|
</div>
|
|
</template>
|