Files
BarangaySystem/resources/js/Pages/CooperativeList.vue
2026-06-06 18:43:00 +08:00

217 lines
8.6 KiB
Vue

<script setup>
import { ref, onMounted } from 'vue';
import axios from 'axios';
import { usePageTitle } from '../composables/Core/usePageTitle';
import { useNavigate } from '../composables/Core/useNavigate';
import { useModal } from '../composables/Core/useModal';
import { useAuth } from '../composables/Core/useAuth';
import TransactionListSkeleton from '../Components/Core/Skeleton/TransactionListSkeleton.vue';
usePageTitle('Cooperatives');
const { navigate } = useNavigate();
const modal = useModal();
const { isUltimate, isSuperOperator } = useAuth();
const cooperatives = ref([]);
const loading = ref(true);
const error = ref(null);
const showCreateModal = ref(false);
const isSaving = ref(false);
const createForm = ref({
name: '',
address: ''
});
const fetchCooperatives = async () => {
loading.value = true;
error.value = null;
try {
const response = await axios.post('/Cooperatives/List');
if (response.data.success) {
cooperatives.value = response.data.data;
} else {
error.value = response.data.message || 'Failed to load cooperatives.';
}
} catch (err) {
console.error('Failed to fetch cooperatives:', err);
error.value = err.response?.data?.message || 'A server error occurred. Please try again later.';
} finally {
loading.value = false;
}
};
const openCreateModal = () => {
createForm.value = { name: '', address: '' };
showCreateModal.value = true;
};
const closeCreateModal = () => {
showCreateModal.value = false;
createForm.value = { name: '', address: '' };
};
const handleCreate = async () => {
if (!createForm.value.name.trim()) {
modal.open({ title: 'Error', body: 'Cooperative name is required.' });
return;
}
isSaving.value = true;
try {
const response = await axios.post('/Cooperatives/Create', createForm.value);
if (response.data.success) {
showCreateModal.value = false;
createForm.value = { name: '', address: '' };
await fetchCooperatives();
modal.open({
title: 'Success',
body: 'Cooperative created successfully!',
});
} else {
modal.open({
title: 'Error',
body: response.data.message || 'Failed to create cooperative. Please try again.'
});
}
} catch (error) {
console.error('Failed to create cooperative:', error);
modal.open({
title: 'Error',
body: error.response?.data?.message || 'Failed to create cooperative. Please try again.'
});
} finally {
isSaving.value = false;
}
};
const viewDetails = (hashkey) => {
navigate({ page: 'CooperativeDetail', props: { target: hashkey } });
};
const goToCreate = () => {
navigate({ page: 'CreateCooperative' });
};
onMounted(fetchCooperatives);
</script>
<template>
<div class="cooperative-list pb-5">
<div class="tf-container mt-4">
<div class="mb-4">
<h3 class="fw_6 mb-3">Cooperatives</h3>
<div class="d-flex gap-2">
<button v-if="isUltimate || isSuperOperator" @click="navigate({ page: 'BatchAddCooperatives' })" class="btn btn-outline-primary rounded-pill px-4 py-2 d-flex align-items-center gap-2 flex-grow-1 justify-content-center">
<i class="fas fa-layer-group"></i> Batch Add
</button>
<button @click="goToCreate" class="btn btn-primary rounded-pill px-4 py-2 d-flex align-items-center gap-2 flex-grow-1 justify-content-center">
<i class="fas fa-plus"></i> New Cooperative
</button>
</div>
</div>
<div v-if="loading" class="mt-2 text-center">
<TransactionListSkeleton :count="6" />
</div>
<div v-else-if="error" class="text-center py-5 px-4 animate-fade-in">
<div class="bg-soft-danger text-danger rounded-circle mx-auto mb-4 p-3 d-flex align-items-center justify-content-center" style="width: 80px; height: 80px;">
<i class="fas fa-exclamation-triangle fa-3x"></i>
</div>
<h4 class="fw-bold mb-2 text-dark">Data Not Loaded</h4>
<p class="text-muted mb-4 px-lg-5">{{ error }}</p>
<button @click="fetchCooperatives" class="btn btn-outline-primary rounded-pill px-4">
<i class="fas fa-sync-alt me-2"></i> Try Again
</button>
</div>
<div v-else-if="cooperatives.length === 0" class="text-center py-5">
<i class="fas fa-users-slash fa-3x text-muted opacity-2 mb-3"></i>
<p class="text-muted">No cooperatives found.</p>
<button @click="goToCreate" class="btn btn-primary mt-3 rounded-pill px-4">
<i class="fas fa-plus me-2"></i> Create First Cooperative
</button>
</div>
<div v-else class="row g-3">
<div v-for="coop in cooperatives" :key="coop.hashkey" class="col-12 col-md-6">
<div @click="viewDetails(coop.hashkey)" class="card border-0 shadow-sm rounded-20 p-3 cursor-pointer hover-card">
<div class="d-flex align-items-center gap-3">
<div class="bg-primary-subtle rounded-circle p-3 text-primary">
<i class="fas fa-users fa-lg"></i>
</div>
<div class="flex-grow-1">
<h5 class="fw_6 mb-1 text-truncate">{{ coop.name }}</h5>
<p class="text-muted small mb-0"><i class="fas fa-map-marker-alt me-1"></i> {{ coop.address || 'No address' }}</p>
</div>
<div class="text-end">
<span class="badge bg-light text-dark rounded-pill border">{{ coop.members_count || 0 }} Members</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Create Cooperative Modal -->
<div v-if="showCreateModal" class="modal-backdrop-custom" @click.self="closeCreateModal">
<div class="modal-card">
<div class="d-flex justify-content-between align-items-center mb-4">
<h5 class="fw_6 mb-0">Create Cooperative</h5>
<button @click="closeCreateModal" class="btn-close"></button>
</div>
<form @submit.prevent="handleCreate">
<div class="mb-3">
<label class="form-label small fw-bold">Cooperative Name *</label>
<input v-model="createForm.name" type="text" class="form-control rounded-pill" required placeholder="Enter cooperative name">
</div>
<div class="mb-4">
<label class="form-label small fw-bold">Address</label>
<textarea v-model="createForm.address" class="form-control rounded-15" rows="2" placeholder="Enter address (optional)"></textarea>
</div>
<div class="d-flex gap-2">
<button type="button" @click="closeCreateModal" class="btn btn-light rounded-pill flex-grow-1 py-2">Cancel</button>
<button :disabled="isSaving" type="submit" class="btn btn-primary rounded-pill flex-grow-1 py-2">
<span v-if="isSaving"><i class="fas fa-spinner fa-spin me-1"></i> Creating...</span>
<span v-else>Create</span>
</button>
</div>
</form>
</div>
</div>
</div>
</template>
<style scoped>
.rounded-20 { border-radius: 20px; }
.rounded-15 { border-radius: 15px; }
.cursor-pointer { cursor: pointer; }
.hover-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(0,0,0,0.1) !important;
}
.bg-soft-danger { background-color: rgba(220, 53, 69, 0.1); }
.bg-primary-subtle { background-color: rgba(13, 110, 253, 0.1); }
.modal-backdrop-custom {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
z-index: 1050;
display: flex;
align-items: center;
justify-content: center;
padding: 1rem;
}
.modal-card {
background: white;
border-radius: 20px;
padding: 1.5rem;
width: 100%;
max-width: 450px;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
}
</style>