initial: bootstrap from BukidBountyApp base
This commit is contained in:
304
resources/js/Pages/EnrollFarmer.vue
Normal file
304
resources/js/Pages/EnrollFarmer.vue
Normal file
@@ -0,0 +1,304 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user