422 lines
19 KiB
Vue
422 lines
19 KiB
Vue
<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>
|