initial: bootstrap from BukidBountyApp base
This commit is contained in:
421
resources/js/Pages/UserInfoEdit.vue
Normal file
421
resources/js/Pages/UserInfoEdit.vue
Normal file
@@ -0,0 +1,421 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, watch, getCurrentInstance } 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 CardSimple from '../Components/Core/CardSimple.vue';
|
||||
import InputGroup from '../Components/Core/Forms/InputGroup.vue';
|
||||
import InputGroupSelect from '../Components/Core/Forms/InputGroupSelect.vue';
|
||||
import InputGroupButton from '../Components/Core/Forms/InputGroupButton.vue';
|
||||
import InputGroupTextarea from '../Components/Core/Forms/InputGroupTextarea.vue';
|
||||
|
||||
import { useUserStore } from '../stores/user';
|
||||
|
||||
const userStore = useUserStore();
|
||||
const props = defineProps({
|
||||
target: String
|
||||
});
|
||||
|
||||
usePageTitle('Member Profile');
|
||||
const { navigate } = useNavigate();
|
||||
const modal = useModal();
|
||||
|
||||
const form = ref({
|
||||
firstname: '',
|
||||
middlename: '',
|
||||
lastname: '',
|
||||
suffix: '',
|
||||
gender: '',
|
||||
dob: '',
|
||||
fullname: '',
|
||||
landline: '',
|
||||
mobile: '',
|
||||
email: '',
|
||||
alt_email: '',
|
||||
alt_landline: '',
|
||||
alt_mobile: '',
|
||||
facebook_url: '',
|
||||
messenger_id: '',
|
||||
viber_number: '',
|
||||
tiktok_username: '',
|
||||
region: '',
|
||||
province: '',
|
||||
city: '',
|
||||
barangay: '',
|
||||
civil_status: '',
|
||||
children_count: 0,
|
||||
education_level: '',
|
||||
course: '',
|
||||
school: '',
|
||||
year_last_attended: '',
|
||||
livelihood_source: '',
|
||||
last_company: '',
|
||||
last_position: '',
|
||||
last_employment_year: '',
|
||||
tin: '',
|
||||
philhealth_id: '',
|
||||
gov_id: '',
|
||||
emergency_contact_name: '',
|
||||
emergency_contact_address: '',
|
||||
emergency_contact_phone: '',
|
||||
emergency_contact_relation: '',
|
||||
emergency_contact_user_id: null,
|
||||
bank_details: { bank: '', account_number: '', account_name: '' },
|
||||
addresses: [],
|
||||
other_details: {}
|
||||
});
|
||||
|
||||
const loading = ref(true);
|
||||
const saving = ref(false);
|
||||
const emergencyResults = ref([]);
|
||||
const searchingEmergency = ref(false);
|
||||
const linkedUser = ref(null);
|
||||
|
||||
const fetchUserInfo = async () => {
|
||||
loading.value = true;
|
||||
let targetHashkey = props.target;
|
||||
|
||||
// If no target provided, try to use the current user's hashkey from the store
|
||||
if (!targetHashkey) {
|
||||
if (!userStore.user) {
|
||||
await userStore.fetchCurrentUser();
|
||||
}
|
||||
targetHashkey = userStore.user?.hashkey;
|
||||
}
|
||||
|
||||
if (!targetHashkey) {
|
||||
console.warn('[UserInfoEdit] No target hashkey found for profile fetch');
|
||||
loading.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post('/UserInfo/Get', { hashkey: targetHashkey });
|
||||
if (response.data.success) {
|
||||
const data = response.data.data;
|
||||
form.value = { ...form.value, ...data };
|
||||
if (!form.value.bank_details) form.value.bank_details = { bank: '', account_number: '', account_name: '' };
|
||||
if (!form.value.addresses) form.value.addresses = [];
|
||||
} else {
|
||||
console.error('[UserInfoEdit] Backend returned success=false:', response.data.message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[UserInfoEdit] Failed to fetch user info:', error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
saving.value = true;
|
||||
try {
|
||||
const response = await axios.post('/UserInfo/Update', {
|
||||
hashkey: props.target,
|
||||
...form.value
|
||||
});
|
||||
if (response.data.success) {
|
||||
modal.open({ title: 'Success', body: 'Member profile updated successfully!' });
|
||||
} else {
|
||||
modal.open({ title: 'Error', body: response.data.message || 'Failed to update user information' });
|
||||
}
|
||||
} catch (error) {
|
||||
modal.open({ title: 'Error', body: 'An error occurred while saving.' });
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const searchEmergency = async (val) => {
|
||||
if (val.length < 3) {
|
||||
emergencyResults.value = [];
|
||||
return;
|
||||
}
|
||||
searchingEmergency.value = true;
|
||||
try {
|
||||
const response = await axios.post('/UserInfo/SearchEmergencyContact', { q: val });
|
||||
if (response.data.success) {
|
||||
emergencyResults.value = response.data.data;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Search failed');
|
||||
} finally {
|
||||
searchingEmergency.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const selectEmergencyUser = (user) => {
|
||||
form.value.emergency_contact_name = user.fullname || user.name;
|
||||
form.value.emergency_contact_phone = user.mobile_number;
|
||||
form.value.emergency_contact_user_id = user.id;
|
||||
emergencyResults.value = [];
|
||||
};
|
||||
|
||||
onMounted(fetchUserInfo);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="user-info-edit pb-5">
|
||||
<div class="tf-container mt-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h3 class="fw_6 mb-0">Detailed Member Profile</h3>
|
||||
<InputGroupButton
|
||||
:loading="saving"
|
||||
:disabled="saving"
|
||||
text="Save Changes"
|
||||
variant="primary"
|
||||
@click="handleSave"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-if="loading" class="text-center py-5">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
<p class="text-muted mt-3">Loading profile...</p>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<CardSimple title="Personal Information" class="mb-4">
|
||||
<template #headerAction>
|
||||
<i class="fas fa-user-circle text-primary"></i>
|
||||
</template>
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<InputGroup v-model="form.firstname" label="First Name" id="firstname" placeholder="First Name" />
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<InputGroup v-model="form.middlename" label="Middle Name" id="middlename" placeholder="Middle Name" />
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<InputGroup v-model="form.lastname" label="Last Name" id="lastname" placeholder="Last Name" />
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<InputGroup v-model="form.suffix" label="Suffix" id="suffix" placeholder="e.g. Jr, III" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<InputGroupSelect
|
||||
v-model="form.gender"
|
||||
label="Gender"
|
||||
id="gender"
|
||||
:options="['Male', 'Female', 'Other']"
|
||||
placeholder="Select Gender"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<InputGroup v-model="form.dob" type="date" label="Date of Birth" id="dob" />
|
||||
</div>
|
||||
</div>
|
||||
<InputGroup v-model="form.fullname" label="Display/Full Name" id="fullname" placeholder="Juan Dela Cruz" />
|
||||
</CardSimple>
|
||||
|
||||
<CardSimple title="Contact & Social" class="mb-4">
|
||||
<template #headerAction>
|
||||
<i class="fas fa-phone-alt text-primary"></i>
|
||||
</template>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<InputGroup v-model="form.mobile" label="Primary Mobile" id="mobile" placeholder="09xxxxxxxxx" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<InputGroup v-model="form.email" type="email" label="Primary Email" id="email" placeholder="email@example.com" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<InputGroup v-model="form.facebook_url" label="Facebook Profile" id="facebook" placeholder="facebook.com/juan" icon="fas fa-facebook" />
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<InputGroup v-model="form.messenger_id" label="Messenger ID" id="messenger" placeholder="Messenger username" />
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<InputGroup v-model="form.viber_number" label="Viber Number" id="viber" placeholder="Viber number" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<InputGroup v-model="form.tiktok_username" label="TikTok Username" id="tiktok" placeholder="@username" />
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<InputGroup v-model="form.alt_mobile" label="Alt Mobile" id="alt_mobile" placeholder="Alternative number" />
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<InputGroup v-model="form.landline" label="Landline" id="landline" placeholder="02-8xxx-xxxx" />
|
||||
</div>
|
||||
</div>
|
||||
</CardSimple>
|
||||
|
||||
<CardSimple title="Address Information" class="mb-4">
|
||||
<template #headerAction>
|
||||
<i class="fas fa-map-marker-alt text-primary"></i>
|
||||
</template>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<InputGroup v-model="form.region" label="Region" id="region" placeholder="e.g. Region III" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<InputGroup v-model="form.province" label="Province" id="province" placeholder="e.g. Pampanga" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<InputGroup v-model="form.city" label="City / Municipality" id="city" placeholder="e.g. San Fernando" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<InputGroup v-model="form.barangay" label="Barangay" id="barangay" placeholder="e.g. Dolores" />
|
||||
</div>
|
||||
</div>
|
||||
</CardSimple>
|
||||
|
||||
<CardSimple title="Family & Education" class="mb-4">
|
||||
<template #headerAction>
|
||||
<i class="fas fa-users-cog text-primary"></i>
|
||||
</template>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<InputGroupSelect
|
||||
v-model="form.civil_status"
|
||||
label="Civil Status"
|
||||
id="civil_status"
|
||||
:options="['Single', 'Married', 'Widowed', 'Separated']"
|
||||
placeholder="Select Status"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<InputGroup v-model.number="form.children_count" type="number" label="Number of Children" id="children" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<InputGroup v-model="form.education_level" label="Education Level" id="education" placeholder="Highest attainment" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<InputGroup v-model="form.course" label="Course / Major" id="course" placeholder="Course taken" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<InputGroup v-model="form.school" label="School Attended" id="school" placeholder="Name of school" />
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<InputGroup v-model="form.year_last_attended" label="Year Graduated" id="grad_year" placeholder="YYYY" />
|
||||
</div>
|
||||
</div>
|
||||
</CardSimple>
|
||||
|
||||
<CardSimple title="Employment & Livelihood" class="mb-4">
|
||||
<template #headerAction>
|
||||
<i class="fas fa-briefcase text-primary"></i>
|
||||
</template>
|
||||
<InputGroup v-model="form.livelihood_source" label="Current Source of Livelihood" id="livelihood" placeholder="e.g. Farming, Business, Office Job" />
|
||||
<InputGroup v-model="form.last_company" label="Company Last Employed" id="last_company" placeholder="Name of company" />
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<InputGroup v-model="form.last_position" label="Position in the Company" id="last_position" placeholder="Job title" />
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<InputGroup v-model="form.last_employment_year" label="Year Last Employed" id="last_employment_year" placeholder="YYYY" />
|
||||
</div>
|
||||
</div>
|
||||
</CardSimple>
|
||||
|
||||
<CardSimple title="Government Information" class="mb-4">
|
||||
<template #headerAction>
|
||||
<i class="fas fa-id-card text-primary"></i>
|
||||
</template>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<InputGroup v-model="form.tin" label="TIN Number" id="tin" placeholder="xxx-xxx-xxx" />
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<InputGroup v-model="form.philhealth_id" label="PhilHealth ID" id="philhealth" placeholder="xxxx-xxxx-xxxx" />
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<InputGroup v-model="form.gov_id" label="Government ID" id="gov_id" placeholder="ID Number" />
|
||||
</div>
|
||||
</div>
|
||||
</CardSimple>
|
||||
|
||||
<CardSimple title="Emergency Contact Information" class="mb-4">
|
||||
<template #headerAction>
|
||||
<i class="fas fa-exclamation-triangle text-danger"></i>
|
||||
</template>
|
||||
|
||||
<div class="mb-4">
|
||||
<InputGroup
|
||||
id="emergency_search"
|
||||
label="Search Existing Member to Link"
|
||||
placeholder="Search by name or phone..."
|
||||
icon="fas fa-search"
|
||||
@input="searchEmergency($event.target.value)"
|
||||
:hint="searchingEmergency ? 'Searching...' : ''"
|
||||
/>
|
||||
<div v-if="emergencyResults.length > 0" class="list-group mt-2 border-0 shadow-sm rounded-15 overflow-hidden">
|
||||
<button v-for="user in emergencyResults" :key="user.id"
|
||||
type="button"
|
||||
class="list-group-item list-group-item-action border-0 py-3"
|
||||
@click="selectEmergencyUser(user)">
|
||||
<div class="fw-bold">{{ user.fullname || user.name }}</div>
|
||||
<div class="small text-muted">{{ user.mobile_number }}</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<InputGroup
|
||||
v-model="form.emergency_contact_name"
|
||||
label="Contact Name"
|
||||
id="emergency_name"
|
||||
placeholder="Juan Dela Cruz"
|
||||
:hint="form.emergency_contact_user_id ? 'Linked' : ''"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<InputGroup v-model="form.emergency_contact_relation" label="Relation" id="relation" placeholder="Spouse, Sibling, Friend" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<InputGroup v-model="form.emergency_contact_phone" label="Contact Number" id="emergency_phone" placeholder="09xxxxxxxxx" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<InputGroup v-model="form.emergency_contact_address" label="Address" id="emergency_address" placeholder="Full address" />
|
||||
</div>
|
||||
</div>
|
||||
</CardSimple>
|
||||
|
||||
<CardSimple title="Bank Details" class="mb-4">
|
||||
<template #headerAction>
|
||||
<i class="fas fa-university text-primary"></i>
|
||||
</template>
|
||||
<InputGroup v-model="form.bank_details.bank" label="Bank Name / E-Wallet" id="bank" placeholder="e.g. GCash, Maya, BDO" />
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<InputGroup v-model="form.bank_details.account_number" label="Account Number" id="account_number" placeholder="000123456789" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<InputGroup v-model="form.bank_details.account_name" label="Account Name" id="account_name" placeholder="Juan Dela Cruz" />
|
||||
</div>
|
||||
</div>
|
||||
</CardSimple>
|
||||
|
||||
<div class="mt-4 mb-5">
|
||||
<InputGroupButton
|
||||
:loading="saving"
|
||||
text="Update Entire Profile"
|
||||
variant="primary"
|
||||
size="lg"
|
||||
buttonStyle="width: 100%; py-3 fw-bold shadow-lg"
|
||||
@click="handleSave"
|
||||
/>
|
||||
<p class="text-center text-muted small mt-3">
|
||||
<i class="fas fa-shield-alt me-1"></i> Data is stored securely in compliance with the Data Privacy Act.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user