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

305 lines
8.3 KiB
Vue

<script setup>
import { ref, onMounted, computed } from "vue";
import axios from "axios";
import { usePageTitle } from "../composables/Core/usePageTitle";
import { useNavigate } from "../composables/Core/useNavigate";
import { useModal } from "../composables/Core/useModal";
usePageTitle("Enroll Farmer");
const { navigate } = useNavigate();
const modal = useModal();
const props = defineProps({
target: String,
});
// Cooperative
const cooperative = ref(null);
const loadingCoop = ref(true);
// Search
const searchQuery = ref("");
const searchResults = ref([]);
const isSearching = ref(false);
// Selected User
const selectedUser = ref(null);
// Form
const form = ref({
farm_name: "",
farm_location: "",
main_crops: [],
});
const cropInput = ref("");
const isSaving = ref(false);
const fetchCooperative = async () => {
if (!props.target) return;
loadingCoop.value = true;
try {
const response = await axios.post("/Cooperatives/Get", {
hashkey: props.target,
});
if (response.data.success) {
cooperative.value = response.data.data;
}
} catch (error) {
console.error("Failed to fetch cooperative", error);
} finally {
loadingCoop.value = false;
}
};
const searchUsers = async () => {
if (searchQuery.value.length < 2) {
searchResults.value = [];
return;
}
isSearching.value = true;
try {
const response = await axios.post("/Farmers/List", {
search: searchQuery.value,
});
if (response.data.success) {
searchResults.value = response.data.data;
}
} catch (error) {
console.error("Search failed", error);
} finally {
isSearching.value = false;
}
};
const selectUser = (user) => {
selectedUser.value = user;
searchResults.value = [];
searchQuery.value = "";
};
const clearSelection = () => {
selectedUser.value = null;
};
const addCrop = () => {
if (
cropInput.value &&
!form.value.main_crops.includes(cropInput.value)
) {
form.value.main_crops.push(cropInput.value);
cropInput.value = "";
}
};
const removeCrop = (crop) => {
form.value.main_crops = form.value.main_crops.filter((c) => c !== crop);
};
const enrollFarmer = async () => {
if (!selectedUser.value) {
modal.open({ title: "Error", body: "Please select a user first." });
return;
}
isSaving.value = true;
try {
const response = await axios.post("/Farmers/Register", {
...form.value,
organization_hash: props.target,
});
if (response.data.success) {
modal.open({
title: "Success",
body: "Farmer enrolled successfully!",
onClose: () =>
navigate({ page: "CooperativeDetail", target: props.target }),
});
} else {
modal.open({
title: "Error",
body: response.data.message || "Enrollment failed. Please try again.",
});
}
} catch (error) {
console.error("Enrollment failed", error);
modal.open({
title: "Error",
body:
error.response?.data?.message ||
"Failed to enroll farmer. Please try again.",
});
} finally {
isSaving.value = false;
}
};
onMounted(fetchCooperative);
</script>
<template>
<div class="enroll-farmer pb-5">
<div class="tf-container mt-4">
<div class="mb-4">
<button
@click="navigate({ page: 'CooperativeDetail', target: target })"
class="btn btn-link text-decoration-none p-0 mb-2 text-primary"
>
<i class="fas fa-arrow-left me-1"></i> Back to Cooperative
</button>
<h3 class="fw_6 mb-0">Enroll Farmer</h3>
<p v-if="cooperative" class="text-muted">
Adding farmer to <strong>{{ cooperative.name }}</strong>
</p>
</div>
<div v-if="loadingCoop" class="text-center py-5">
<i class="fas fa-spinner fa-spin fa-2x text-primary"></i>
</div>
<div v-else>
<!-- User Selection -->
<div class="card border-0 shadow-sm rounded-20 p-4 mb-4">
<h5 class="fw_6 mb-3">1. Select User</h5>
<div v-if="!selectedUser">
<div class="position-relative">
<input
v-model="searchQuery"
@input="searchUsers"
type="text"
class="form-control rounded-pill"
placeholder="Search user by name or mobile..."
/>
<div
v-if="searchResults.length > 0"
class="search-results card border-0 shadow-sm mt-2 position-absolute w-100 z-1"
>
<div
v-for="user in searchResults"
:key="user.hashkey"
@click="selectUser(user)"
class="p-3 border-bottom cursor-pointer hover-bg"
>
<div class="fw-bold">
{{ user.fullname || user.firstname + " " + user.lastname }}
</div>
<small class="text-muted">{{ user.mobile }}</small>
</div>
</div>
<div v-if="isSearching" class="text-center mt-2">
<small class="text-muted"
><i class="fas fa-spinner fa-spin me-1"></i> Searching...</small
>
</div>
</div>
</div>
<div v-else class="d-flex align-items-center justify-content-between bg-light p-3 rounded-15">
<div>
<div class="fw-bold">
{{
selectedUser.fullname ||
selectedUser.firstname + " " + selectedUser.lastname
}}
</div>
<small class="text-muted">{{ selectedUser.mobile }}</small>
</div>
<button @click="clearSelection" class="btn btn-sm btn-outline-danger rounded-pill">
Change
</button>
</div>
</div>
<!-- Farmer Details -->
<div class="card border-0 shadow-sm rounded-20 p-4">
<h5 class="fw_6 mb-3">2. Farm Details</h5>
<div class="mb-3">
<label class="form-label small fw-bold">Farm Name</label>
<input
v-model="form.farm_name"
type="text"
class="form-control rounded-pill"
placeholder="Enter farm name"
/>
</div>
<div class="mb-3">
<label class="form-label small fw-bold">Farm Location</label>
<textarea
v-model="form.farm_location"
class="form-control rounded-15"
rows="2"
placeholder="Barangay, Municipality, Province"
></textarea>
</div>
<div class="mb-4">
<label class="form-label small fw-bold">Main Crops</label>
<div class="d-flex gap-2 mb-2">
<input
v-model="cropInput"
@keyup.enter="addCrop"
type="text"
class="form-control rounded-pill"
placeholder="e.g. Rice, Corn"
/>
<button
@click="addCrop"
type="button"
class="btn btn-primary rounded-pill px-4"
>
Add
</button>
</div>
<div class="d-flex flex-wrap gap-2">
<span
v-for="crop in form.main_crops"
:key="crop"
class="badge bg-light text-dark rounded-pill border px-3 py-2"
>
{{ crop }}
<i
@click="removeCrop(crop)"
class="fas fa-times ms-2 cursor-pointer text-danger"
></i>
</span>
</div>
</div>
<button
:disabled="isSaving || !selectedUser"
@click="enrollFarmer"
class="btn btn-primary w-100 rounded-pill py-3 fw-bold"
>
<span v-if="isSaving"
><i class="fas fa-spinner fa-spin me-2"></i> Enrolling...</span
>
<span v-else>Enroll Farmer</span>
</button>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.rounded-20 {
border-radius: 20px;
}
.rounded-15 {
border-radius: 15px;
}
.cursor-pointer {
cursor: pointer;
}
.hover-bg:hover {
background-color: #f8f9fa;
}
.search-results {
max-height: 200px;
overflow-y: auto;
}
</style>