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

207 lines
8.4 KiB
Vue

<script setup>
import { ref, onMounted } from 'vue'
import axios from 'axios'
import { useNavigate } from '../composables/Core/useNavigate'
import { useModal } from '../composables/Core/useModal'
import { usePageTitle } from '../composables/Core/usePageTitle'
import BackButton from '../Components/Core/BackButton.vue'
import LoadingSpinner from '../Components/LoadingSpinner.vue'
const props = defineProps({
target: String
})
const { navigate } = useNavigate()
const modal = useModal()
usePageTitle('Batch Add Cooperative Members')
const ROLES = ['MEMBER', 'OFFICER', 'ADMIN']
const MEMBERSHIP_TYPES = ['REGULAR', 'ASSOCIATE', 'LABORATORY']
const blankRow = () => ({
username: '',
name: '',
mobile_number: '',
password: 'Password123!',
role: 'MEMBER',
membership_type: '',
})
const cooperative = ref(null)
const loadingCoop = ref(true)
const members = ref([blankRow()])
const saving = ref(false)
const addRow = () => {
members.value.push(blankRow())
}
const removeRow = (index) => {
if (members.value.length > 1) {
members.value.splice(index, 1)
}
}
const fetchCooperative = async () => {
if (!props.target) {
loadingCoop.value = false
return
}
try {
const response = await axios.post('/Cooperatives/Get', { hashkey: props.target })
if (response.data?.success) {
cooperative.value = response.data.data
}
} catch (err) {
console.error('Failed to load cooperative', err)
} finally {
loadingCoop.value = false
}
}
const saveMembers = async () => {
if (!props.target) {
modal.open({ title: 'Error', body: 'Cooperative not specified.' })
return
}
const invalid = members.value.some(m => !m.username || !m.name || !m.mobile_number || !m.password)
if (invalid) {
modal.open({
title: 'Validation Error',
body: 'Please fill in Username, Name, Mobile, and Password for all rows.'
})
return
}
saving.value = true
try {
const response = await axios.post('/admin/batch/cooperative-members', {
cooperative_hash: props.target,
members: members.value
})
if (response.data?.success) {
modal.open({
title: 'Success',
body: `Successfully registered ${response.data.count} members with new user accounts.`,
onClose: () => navigate({ page: 'CooperativeDetail', props: { target: props.target } })
})
}
} catch (err) {
console.error('Error saving batch members:', err)
const errorMessage = err.response?.data?.errors
? err.response.data.errors.join('<br>')
: (err.response?.data?.message || 'Failed to save members.')
modal.open({ title: 'Error', body: errorMessage })
} finally {
saving.value = false
}
}
onMounted(fetchCooperative)
</script>
<template>
<div class="batch-add-page pb-5">
<div class="tf-container mt-4">
<div class="mb-3">
<BackButton :to="{ page: 'CooperativeDetail', props: { target: props.target } }" />
</div>
<div class="mb-4">
<h3 class="fw_6 mb-1">Batch Add Cooperative Members</h3>
<p class="text-muted small mb-0">
<span v-if="loadingCoop">Loading cooperative...</span>
<span v-else-if="cooperative">
Adding members to <strong>{{ cooperative.name }}</strong>. Each row creates a new user account and enrolls them as a member. Default password: <code>Password123!</code>
</span>
<span v-else class="text-danger">Cooperative not found.</span>
</p>
</div>
<div class="d-grid mb-3">
<button @click="addRow" class="btn btn-outline-primary rounded-pill">
<i class="fas fa-plus-circle me-2"></i> Add Member
</button>
</div>
<div class="row g-4">
<div v-for="(m, index) in members" :key="index" class="col-md-6 col-lg-4">
<div class="leaf-card p-3 bg-white rounded-3 border position-relative h-100">
<div class="d-flex justify-content-between align-items-center mb-3 pb-2 border-bottom">
<span class="badge bg-primary rounded-pill">#{{ index + 1 }}</span>
<button @click="removeRow(index)" class="btn btn-link text-danger p-0 border-0"
:disabled="members.length <= 1"><i class="fas fa-times-circle"></i></button>
</div>
<div class="row g-2 mb-2">
<div class="col-6">
<label class="form-label small fw-bold text-muted mb-1">Username *</label>
<input v-model="m.username" type="text" class="form-control form-control-sm" placeholder="Unique username">
</div>
<div class="col-6">
<label class="form-label small fw-bold text-muted mb-1">Full Name *</label>
<input v-model="m.name" type="text" class="form-control form-control-sm" placeholder="Member's full name">
</div>
</div>
<div class="row g-2 mb-2">
<div class="col-6">
<label class="form-label small fw-bold text-muted mb-1">Mobile *</label>
<input v-model="m.mobile_number" type="text" class="form-control form-control-sm" placeholder="09xxxxxxxxx">
</div>
<div class="col-6">
<label class="form-label small fw-bold text-muted mb-1">Password *</label>
<input v-model="m.password" type="text" class="form-control form-control-sm" placeholder="Password">
</div>
</div>
<div class="row g-2 mb-2">
<div class="col-6">
<label class="form-label small fw-bold text-muted mb-1">Role</label>
<select v-model="m.role" class="form-select form-select-sm">
<option v-for="r in ROLES" :key="r" :value="r">{{ r }}</option>
</select>
</div>
<div class="col-6">
<label class="form-label small fw-bold text-muted mb-1">Membership Type</label>
<select v-model="m.membership_type" class="form-select form-select-sm">
<option value=""></option>
<option v-for="t in MEMBERSHIP_TYPES" :key="t" :value="t">{{ t }}</option>
</select>
</div>
</div>
</div>
</div>
</div>
<div class="d-grid mt-4">
<button @click="addRow" class="btn btn-outline-primary rounded-pill px-4 fw-semibold">
<i class="fas fa-plus-circle me-2"></i> Add Another Member
</button>
</div>
<div class="d-grid mt-3 pt-3 border-top">
<button @click="saveMembers" :disabled="saving || !cooperative" class="btn btn-primary rounded-pill px-4 fw-semibold">
<i class="fas fa-save me-2"></i>
{{ saving ? 'Saving...' : 'Save All Members' }}
</button>
</div>
</div>
</div>
</template>
<style scoped>
.batch-add-page {
background: var(--bg-primary);
min-height: 100vh;
}
.leaf-card { transition: box-shadow 0.15s ease, transform 0.15s ease; }
.leaf-card:hover { box-shadow: 0 4px 12px rgba(13,110,253,0.08); transform: translateY(-2px); }
:global(.dark-mode) .leaf-card { background-color: var(--bg-secondary) !important; border-color: var(--border-color) !important; }
:global(.dark-mode) .form-control, :global(.dark-mode) .form-select {
background-color: var(--bg-secondary) !important;
color: var(--text-primary);
border-color: var(--border-color);
}
</style>