Commit 64b8d478 authored by Administrator's avatar Administrator

Update 6 files via Son of Anton

parent f7d90c56
......@@ -9,38 +9,76 @@ use App\Core\Response;
use App\Core\App;
use App\Core\EventBus;
use App\Modules\Members\Models\Member;
use App\Modules\Members\Models\MemberNote;
use App\Modules\Members\Services\MemberSearchService;
use App\Modules\Members\Services\NationalIdParser;
use App\Modules\Pricing\Services\PricingEngine;
use App\Modules\Workflow\Services\WorkflowEngine;
use App\Modules\Members\Services\FormNumberGenerator;
use App\Modules\Members\Services\MemberSearchService;
class MemberController extends Controller
{
public function index(Request $request): Response
{
$db = App::getInstance()->db();
$filters = [
'q' => trim((string) $request->get('q', '')),
'status' => $request->get('status', ''),
'branch_id' => $request->get('branch_id', ''),
'membership_type' => $request->get('membership_type', ''),
'date_from' => $request->get('date_from', ''),
'date_to' => $request->get('date_to', ''),
];
$page = max(1, (int) $request->get('page', 1));
$result = MemberSearchService::search($filters, 25, $page);
$perPage = 25;
$db = App::getInstance()->db();
$where = 'm.is_archived = 0';
$params = [];
if ($filters['q'] !== '') {
$where .= ' AND (m.full_name_ar LIKE ? OR m.national_id LIKE ? OR m.membership_number LIKE ? OR m.phone_mobile LIKE ? OR m.form_number LIKE ?)';
$s = '%' . $filters['q'] . '%';
$params = array_merge($params, [$s, $s, $s, $s, $s]);
}
if ($filters['status'] !== '') {
$where .= ' AND m.status = ?';
$params[] = $filters['status'];
}
if ($filters['branch_id'] !== '') {
$where .= ' AND m.branch_id = ?';
$params[] = (int) $filters['branch_id'];
}
if ($filters['date_from'] !== '') {
$where .= ' AND m.created_at >= ?';
$params[] = $filters['date_from'] . ' 00:00:00';
}
if ($filters['date_to'] !== '') {
$where .= ' AND m.created_at <= ?';
$params[] = $filters['date_to'] . ' 23:59:59';
}
$countRow = $db->selectOne("SELECT COUNT(*) as cnt FROM members m WHERE {$where}", $params);
$total = (int) ($countRow['cnt'] ?? 0);
$offset = ($page - 1) * $perPage;
$members = $db->select(
"SELECT m.*, b.name_ar as branch_name
FROM members m
LEFT JOIN branches b ON b.id = m.branch_id
WHERE {$where}
ORDER BY m.id DESC
LIMIT {$perPage} OFFSET {$offset}",
$params
);
$pagination = \App\Core\Pagination::paginate($total, $perPage, $page);
$branches = $db->select("SELECT id, name_ar FROM branches WHERE is_active = 1 ORDER BY name_ar");
$statuses = Member::getStatusOptions();
return $this->view('Members.Views.index', [
'members' => $result['data'],
'pagination' => $result['pagination'],
'filters' => $filters,
'members' => $members,
'branches' => $branches,
'statuses' => Member::getStatusOptions(),
'types' => Member::getMembershipTypes(),
'statuses' => $statuses,
'filters' => $filters,
'pagination' => $pagination,
]);
}
......@@ -48,260 +86,312 @@ class MemberController extends Controller
{
$db = App::getInstance()->db();
$branches = $db->select("SELECT id, name_ar FROM branches WHERE is_active = 1 ORDER BY name_ar");
$qualifications = $db->select("SELECT id, name_ar FROM qualifications WHERE is_active = 1 ORDER BY sort_order");
$governorates = $db->select("SELECT code, name_ar FROM governorates WHERE is_active = 1 ORDER BY name_ar");
$countries = $db->select("SELECT nationality_ar FROM countries WHERE is_active = 1 ORDER BY name_ar");
$nextFormNumber = FormNumberGenerator::next();
$formFee = FormNumberGenerator::getFormFee();
$needsStartNumber = ($nextFormNumber === null);
return $this->view('Members.Views.create', [
'branches' => $branches,
'qualifications' => $qualifications,
'governorates' => $governorates,
'countries' => $countries,
'nextFormNumber' => $nextFormNumber,
'formFee' => $formFee,
'needsStartNumber' => $needsStartNumber,
]);
}
public function store(Request $request): Response
{
$data = $request->all();
unset($data['_csrf_token']);
$db = App::getInstance()->db();
$employee = App::getInstance()->currentEmployee();
// If super admin is setting the starting form number
$startNumber = $request->post('form_start_number');
if ($startNumber !== null && $startNumber !== '') {
$startNum = (int) $startNumber;
if ($startNum <= 0) {
return $this->redirect('/members/create')->withError('رقم بداية الاستمارات يجب أن يكون أكبر من صفر');
}
FormNumberGenerator::setStartNumber($startNum);
}
// Validate basic fields
$fullNameAr = trim((string) $request->post('full_name_ar', ''));
$nationalId = trim((string) $request->post('national_id', ''));
$phoneMobile = trim((string) $request->post('phone_mobile', ''));
$branchId = (int) $request->post('branch_id', 0);
// Validate required fields
$errors = [];
if (empty(trim($data['full_name_ar'] ?? ''))) $errors[] = 'الاسم بالعربي مطلوب';
if (empty($data['branch_id'] ?? '')) $errors[] = 'الفرع مطلوب';
if (empty($data['date_of_birth'] ?? '')) $errors[] = 'تاريخ الميلاد مطلوب';
if (empty($data['gender'] ?? '')) $errors[] = 'النوع مطلوب';
if (empty($data['phone_mobile'] ?? '')) $errors[] = 'رقم المحمول مطلوب';
$idType = $data['id_type'] ?? 'national_id';
// Parse and validate NID if Egyptian
if ($idType === 'national_id') {
$nid = trim($data['national_id'] ?? '');
if (empty($nid)) {
$errors[] = 'الرقم القومي مطلوب';
if ($fullNameAr === '' || mb_strlen($fullNameAr) < 5) {
$errors[] = 'الاسم بالعربي مطلوب (5 أحرف على الأقل)';
}
if ($phoneMobile === '' || !preg_match('/^01[0125]\d{8}$/', $phoneMobile)) {
$errors[] = 'رقم المحمول غير صالح (01XXXXXXXXX)';
}
if ($branchId <= 0) {
$errors[] = 'الفرع مطلوب';
}
// Parse NID
$dob = null;
$ageYears = null;
$ageMonths = null;
$gender = null;
$govCode = null;
$idType = 'national_id';
if ($nationalId !== '') {
if (strlen($nationalId) !== 14 || !ctype_digit($nationalId)) {
$errors[] = 'الرقم القومي يجب أن يكون 14 رقم';
} else {
$parsed = NationalIdParser::parse($nid);
$parsed = NationalIdParser::parse($nationalId);
if (!$parsed['is_valid']) {
$errors = array_merge($errors, $parsed['errors']);
$errors = array_merge($errors, $parsed['errors'] ?? ['الرقم القومي غير صالح']);
} else {
// Override with parsed data
$data['date_of_birth'] = $parsed['dob'];
$data['age_years'] = $parsed['age_years'];
$data['age_months'] = $parsed['age_months'];
$data['gender'] = $parsed['gender'];
$data['governorate_code'] = $parsed['governorate_code'];
// Check minimum working age
if ($parsed['age_years'] < 21) {
$errors[] = 'يجب أن يكون عمر العضو العامل 21 سنة على الأقل';
}
// Check for duplicate NID
$dup = NationalIdParser::checkDuplicate($nid);
$dob = $parsed['dob'];
$ageYears = $parsed['age_years'];
$ageMonths = $parsed['age_months'];
$gender = $parsed['gender'];
$govCode = $parsed['governorate_code'];
// Check duplicate
$dup = $db->selectOne(
"SELECT id, full_name_ar, membership_number FROM members WHERE national_id = ? AND is_archived = 0",
[$nationalId]
);
if ($dup) {
if (!$dup['is_archived']) {
$errors[] = 'الرقم القومي مسجل بالفعل للعضو: ' . $dup['full_name_ar'] . ' (' . ($dup['membership_number'] ?: 'بدون رقم') . ')';
}
// If archived, allow but we could show a warning — for now allow
$errors[] = 'الرقم القومي مسجل بالفعل: ' . $dup['full_name_ar'] . ' (' . ($dup['membership_number'] ?? 'بدون رقم') . ')';
}
}
}
} elseif ($idType === 'passport') {
if (empty(trim($data['passport_number'] ?? ''))) {
$errors[] = 'رقم جواز السفر مطلوب';
} else {
// Without NID, we need DOB and gender manually
$dob = $request->post('date_of_birth', '');
$gender = $request->post('gender', '');
if ($dob === '') $errors[] = 'تاريخ الميلاد مطلوب';
if ($gender === '') $errors[] = 'النوع مطلوب';
if ($dob) {
$age = age_from_dob($dob);
$ageYears = $age['years'];
$ageMonths = $age['months'];
}
$idType = 'passport';
}
if (!empty($errors)) {
$session = App::getInstance()->session();
$session->flash('_alerts', array_map(fn($e) => ['type' => 'error', 'message' => $e], $errors));
$session->flash('_old_input', $data);
$session->flash('_old_input', $request->all());
return $this->redirect('/members/create');
}
// Calculate age if not already set
if (!empty($data['date_of_birth']) && empty($data['age_years'])) {
$age = age_from_dob($data['date_of_birth']);
$data['age_years'] = $age['years'];
$data['age_months'] = $age['months'];
}
// Get membership price
$branchId = (int) $data['branch_id'];
$qualCode = null;
if (!empty($data['qualification_id'])) {
$db = App::getInstance()->db();
$qualRow = $db->selectOne("SELECT code FROM qualifications WHERE id = ?", [(int) $data['qualification_id']]);
$qualCode = $qualRow['code'] ?? 'high';
// Generate form number
$formNumber = FormNumberGenerator::next();
if ($formNumber === null) {
return $this->redirect('/members/create')->withError('يجب تحديد رقم بداية الاستمارات أولاً');
}
$priceInfo = $qualCode ? PricingEngine::getMembershipPrice($branchId, $qualCode) : ['price' => '0.00'];
$membershipValue = $priceInfo['price'] ?? '0.00';
$employee = App::getInstance()->currentEmployee();
// Create the member
// Create member with basic info
$member = Member::create([
'full_name_ar' => trim($data['full_name_ar']),
'full_name_en' => trim($data['full_name_en'] ?? ''),
'national_id' => $data['national_id'] ?? null,
'passport_number' => $data['passport_number'] ?? null,
'full_name_ar' => $fullNameAr,
'national_id' => $nationalId ?: null,
'passport_number' => $request->post('passport_number') ?: null,
'id_type' => $idType,
'id_issue_date' => $data['id_issue_date'] ?: null,
'id_expiry_date' => $data['id_expiry_date'] ?: null,
'date_of_birth' => $data['date_of_birth'],
'age_years' => (int) ($data['age_years'] ?? 0),
'age_months' => (int) ($data['age_months'] ?? 0),
'gender' => $data['gender'],
'place_of_birth' => $data['place_of_birth'] ?? null,
'nationality' => $data['nationality'] ?? 'مصري',
'governorate_code' => $data['governorate_code'] ?? null,
'religion' => $data['religion'] ?? null,
'qualification_id' => !empty($data['qualification_id']) ? (int) $data['qualification_id'] : null,
'marital_status' => $data['marital_status'] ?? null,
'date_of_birth' => $dob,
'age_years' => $ageYears,
'age_months' => $ageMonths,
'gender' => $gender,
'governorate_code' => $govCode,
'phone_mobile' => $phoneMobile,
'branch_id' => $branchId,
'membership_type' => $data['membership_type'] ?? 'working',
'member_category' => 'working_member',
'nationality' => 'مصري',
'form_number' => (string) $formNumber,
'form_date' => date('Y-m-d'),
'status' => 'potential',
'phone_home' => $data['phone_home'] ?? null,
'phone_mobile' => $data['phone_mobile'],
'phone_international' => $data['phone_international'] ?? null,
'email' => $data['email'] ?? null,
'emergency_name' => $data['emergency_name'] ?? null,
'emergency_phone' => $data['emergency_phone'] ?? null,
'residence_type' => $data['residence_type'] ?? null,
'residence_address' => $data['residence_address'] ?? null,
'landmark' => $data['landmark'] ?? null,
'floor' => $data['floor'] ?? null,
'apartment' => $data['apartment'] ?? null,
'area' => $data['area'] ?? null,
'governorate' => $data['governorate'] ?? null,
'correspondence_address'=> $data['correspondence_address'] ?? null,
'employment_type' => $data['employment_type'] ?? null,
'occupation' => $data['occupation'] ?? null,
'job_title' => $data['job_title'] ?? null,
'employment_date' => !empty($data['employment_date']) ? $data['employment_date'] : null,
'business_address' => $data['business_address'] ?? null,
'office_phone' => $data['office_phone'] ?? null,
'office_fax' => $data['office_fax'] ?? null,
'business_activity' => $data['business_activity'] ?? null,
'membership_value' => $membershipValue,
'referral_source' => $data['referral_source'] ?? null,
'membership_type' => 'working',
'member_category' => 'working_member',
]);
// Create workflow instance
try {
$wfInstance = WorkflowEngine::createInstance(
'new_membership',
'members',
(int) $member->id
);
$db = App::getInstance()->db();
$db->update('members', [
'workflow_instance_id' => (int) $wfInstance->id,
], '`id` = ?', [$member->id]);
} catch (\Throwable $e) {
// Workflow creation failed — log but don't block member creation
\App\Core\Logger::warning("Failed to create workflow for member #{$member->id}: " . $e->getMessage());
}
EventBus::dispatch('member.created', [
'member_id' => (int) $member->id,
'branch_id' => $branchId,
'created_by' => $employee ? (int) $employee->id : null,
'form_number' => (string) $formNumber,
]);
return $this->redirect('/members/' . $member->id)->withSuccess('تم إنشاء العضو بنجاح — الحالة: عضوية محتملة');
$formFee = FormNumberGenerator::getFormFee();
return $this->redirect('/members/' . $member->id)
->withSuccess('تم تسجيل العضو — استمارة رقم: ' . $formNumber . ' — رسوم الاستمارة: ' . money($formFee));
}
public function show(Request $request, string $id): Response
{
$db = App::getInstance()->db();
$member = Member::find((int) $id);
if (!$member) {
if (!$member || $member->is_archived) {
return $this->redirect('/members')->withError('العضو غير موجود');
}
$db = App::getInstance()->db();
$branch = $db->selectOne("SELECT name_ar FROM branches WHERE id = ?", [(int) $member->branch_id]);
$qualification = $member->qualification_id
? $db->selectOne("SELECT name_ar FROM qualifications WHERE id = ?", [(int) $member->qualification_id])
: null;
// Build profile data — future phases enrich this via events
$profileData = [
'member' => $member,
'spouses' => [],
'children' => [],
'temporary' => [],
'payments' => [],
'installments' => [],
'subscriptions'=> [],
'documents' => [],
'violations' => [],
'fines' => [],
];
// Load family data
$spouses = [];
$children = [];
try {
$spouses = $db->select(
"SELECT * FROM spouses WHERE member_id = ? AND is_archived = 0 ORDER BY spouse_order",
[(int) $id]
);
} catch (\Throwable $e) {}
// Load data from tables that exist (future phases add data via event)
$tablesToCheck = [
'spouses' => "SELECT * FROM spouses WHERE member_id = ? AND is_archived = 0 ORDER BY spouse_order",
'children' => "SELECT * FROM children WHERE member_id = ? AND is_archived = 0 ORDER BY child_order",
'temporary_members' => "SELECT * FROM temporary_members WHERE member_id = ? AND is_archived = 0 ORDER BY created_at",
];
try {
$children = $db->select(
"SELECT * FROM children WHERE member_id = ? AND is_archived = 0 ORDER BY child_order",
[(int) $id]
);
} catch (\Throwable $e) {}
foreach ($tablesToCheck as $key => $sql) {
// Load payments
$payments = [];
$totalPaid = '0.00';
try {
if ($db->tableExists($key)) {
$mapKey = $key === 'temporary_members' ? 'temporary' : $key;
$profileData[$mapKey] = $db->select($sql, [(int) $member->id]);
$payments = $db->select(
"SELECT p.*, r.receipt_number FROM payments p LEFT JOIN receipts r ON r.id = p.receipt_id
WHERE p.member_id = ? AND p.is_voided = 0 ORDER BY p.payment_date DESC LIMIT 10",
[(int) $id]
);
$totalPaidRow = $db->selectOne(
"SELECT COALESCE(SUM(amount), 0) as total FROM payments WHERE member_id = ? AND is_voided = 0",
[(int) $id]
);
$totalPaid = $totalPaidRow['total'] ?? '0.00';
} catch (\Throwable $e) {}
// Load notes
$notes = $member->getNotes();
$formFee = FormNumberGenerator::getFormFee();
// Check if form is filled (has qualification = minimum indicator of filled form)
$formFilled = ($member->qualification_id !== null && $member->qualification_id > 0);
return $this->view('Members.Views.show', [
'member' => $member,
'branchName' => $branch['name_ar'] ?? '—',
'qualificationName' => $qualification['name_ar'] ?? '—',
'spouses' => $spouses,
'children' => $children,
'payments' => $payments,
'totalPaid' => $totalPaid,
'notes' => $notes,
'formFee' => $formFee,
'formFilled' => $formFilled,
]);
}
} catch (\Throwable $e) {
// Table doesn't exist yet — that's fine
public function fillForm(Request $request, string $id): Response
{
$db = App::getInstance()->db();
$member = Member::find((int) $id);
if (!$member || $member->is_archived) {
return $this->redirect('/members')->withError('العضو غير موجود');
}
$branches = $db->select("SELECT id, name_ar FROM branches WHERE is_active = 1 ORDER BY name_ar");
$qualifications = $db->select("SELECT id, name_ar FROM qualifications WHERE is_active = 1 ORDER BY sort_order");
$governorates = $db->select("SELECT code, name_ar FROM governorates WHERE is_active = 1 ORDER BY name_ar");
$countries = $db->select("SELECT nationality_ar FROM countries WHERE is_active = 1 ORDER BY name_ar");
return $this->view('Members.Views.fill-form', [
'member' => $member,
'branches' => $branches,
'qualifications' => $qualifications,
'governorates' => $governorates,
'countries' => $countries,
]);
}
// Let other modules enrich profile data
EventBus::dispatch('member.profile_data', $profileData);
public function saveFillForm(Request $request, string $id): Response
{
$db = App::getInstance()->db();
$member = Member::find((int) $id);
if (!$member || $member->is_archived) {
return $this->redirect('/members')->withError('العضو غير موجود');
}
// Get notes
$notes = $member->getNotes();
$data = $request->all();
unset($data['_csrf_token']);
// Get workflow info
$workflowInstance = null;
if ($member->workflow_instance_id) {
try {
$workflowInstance = $db->selectOne(
"SELECT wi.*, wd.name_ar as workflow_name FROM workflow_instances wi JOIN workflow_definitions wd ON wd.id = wi.workflow_definition_id WHERE wi.id = ?",
[(int) $member->workflow_instance_id]
);
} catch (\Throwable $e) {
// ignore
// Build update array — only update non-empty fields
$updateFields = [
'full_name_en', 'place_of_birth', 'nationality', 'religion',
'qualification_id', 'marital_status', 'id_issue_date', 'id_expiry_date',
'phone_home', 'phone_international', 'email',
'emergency_name', 'emergency_phone',
'residence_type', 'residence_address', 'landmark', 'floor', 'apartment',
'area', 'governorate', 'correspondence_address',
'employment_type', 'occupation', 'job_title', 'employment_date',
'business_address', 'office_phone', 'office_fax', 'business_activity',
'referral_source',
];
$update = [];
foreach ($updateFields as $field) {
if (array_key_exists($field, $data)) {
$val = trim((string) ($data[$field] ?? ''));
$update[$field] = ($val === '') ? null : $val;
}
}
// Get audit history (last 20 actions)
$auditHistory = [];
// Handle qualification_id as integer
if (isset($update['qualification_id'])) {
$update['qualification_id'] = $update['qualification_id'] ? (int) $update['qualification_id'] : null;
}
// Calculate membership value based on branch + qualification
if (!empty($update['qualification_id'])) {
try {
$auditHistory = $db->select(
"SELECT * FROM audit_trail WHERE entity_type = 'members' AND entity_id = ? ORDER BY created_at DESC LIMIT 20",
[(int) $member->id]
$pricing = $db->selectOne(
"SELECT price FROM pricing_configs
WHERE branch_id = ? AND qualification_id = ? AND membership_type = 'working'
AND is_active = 1 AND effective_from <= CURDATE()
AND (effective_to IS NULL OR effective_to >= CURDATE())
ORDER BY effective_from DESC LIMIT 1",
[(int) $member->branch_id, (int) $update['qualification_id']]
);
} catch (\Throwable $e) {
// ignore
if ($pricing) {
$update['membership_value'] = $pricing['price'];
}
} catch (\Throwable $e) {}
}
return $this->view('Members.Views.show', array_merge($profileData, [
'notes' => $notes,
'workflowInstance' => $workflowInstance,
'auditHistory' => $auditHistory,
]));
// Update status to under_review if still potential
if ($member->status === 'potential') {
$update['status'] = 'under_review';
}
if (!empty($update)) {
$member->update($update);
}
EventBus::dispatch('member.form_filled', [
'member_id' => (int) $id,
'form_number' => $member->form_number,
]);
return $this->redirect('/members/' . $id)
->withSuccess('تم ملء الاستمارة بنجاح — الحالة: تحت المراجعة');
}
public function edit(Request $request, string $id): Response
{
$db = App::getInstance()->db();
$member = Member::find((int) $id);
if (!$member) {
if (!$member || $member->is_archived) {
return $this->redirect('/members')->withError('العضو غير موجود');
}
$db = App::getInstance()->db();
$branches = $db->select("SELECT id, name_ar FROM branches WHERE is_active = 1 ORDER BY name_ar");
$qualifications = $db->select("SELECT id, name_ar FROM qualifications WHERE is_active = 1 ORDER BY sort_order");
$governorates = $db->select("SELECT code, name_ar FROM governorates WHERE is_active = 1 ORDER BY name_ar");
......@@ -319,65 +409,76 @@ class MemberController extends Controller
public function update(Request $request, string $id): Response
{
$member = Member::find((int) $id);
if (!$member) {
if (!$member || $member->is_archived) {
return $this->redirect('/members')->withError('العضو غير موجود');
}
$data = $request->all();
unset($data['_csrf_token']);
// Recalculate age
if (!empty($data['date_of_birth'])) {
$age = age_from_dob($data['date_of_birth']);
$data['age_years'] = $age['years'];
$data['age_months'] = $age['months'];
}
$updateFields = [
'full_name_en', 'religion', 'marital_status',
'phone_home', 'phone_mobile', 'phone_international', 'email',
'emergency_name', 'emergency_phone',
$allowedFields = [
'full_name_en', 'phone_home', 'phone_mobile', 'phone_international',
'email', 'emergency_name', 'emergency_phone',
'residence_type', 'residence_address', 'landmark', 'floor', 'apartment',
'area', 'governorate', 'correspondence_address',
'employment_type', 'occupation', 'job_title', 'employment_date',
'business_address', 'office_phone', 'office_fax', 'business_activity',
'referral_source',
'referral_source', 'religion', 'marital_status',
];
$updateData = [];
foreach ($updateFields as $field) {
$update = [];
foreach ($allowedFields as $field) {
if (array_key_exists($field, $data)) {
$val = $data[$field];
$updateData[$field] = ($val === '' || $val === null) ? null : $val;
$val = trim((string) ($data[$field] ?? ''));
$update[$field] = ($val === '') ? null : $val;
}
}
if (!empty($updateData)) {
$member->update($updateData);
if (!empty($update)) {
$member->update($update);
}
// Handle notes
$noteText = trim($data['new_note'] ?? '');
if ($noteText !== '') {
$employee = App::getInstance()->currentEmployee();
MemberNote::create([
'member_id' => (int) $member->id,
'note_text' => $noteText,
]);
return $this->redirect('/members/' . $id)->withSuccess('تم تحديث بيانات العضو');
}
public function changeStatus(Request $request, string $id): Response
{
$member = Member::find((int) $id);
if (!$member) {
return $this->redirect('/members')->withError('العضو غير موجود');
}
return $this->redirect('/members/' . $member->id)->withSuccess('تم تحديث بيانات العضو بنجاح');
$newStatus = trim((string) $request->post('status', ''));
$validStatuses = array_keys(Member::getStatusOptions());
if (!in_array($newStatus, $validStatuses)) {
return $this->redirect('/members/' . $id)->withError('حالة غير صالحة');
}
$oldStatus = $member->status;
$member->update(['status' => $newStatus]);
EventBus::dispatch('member.status_changed', [
'member_id' => (int) $id,
'old_status' => $oldStatus,
'new_status' => $newStatus,
]);
return $this->redirect('/members/' . $id)
->withSuccess('تم تغيير الحالة إلى: ' . (Member::getStatusOptions()[$newStatus] ?? $newStatus));
}
public function search(Request $request): Response
{
$db = App::getInstance()->db();
$branches = $db->select("SELECT id, name_ar FROM branches WHERE is_active = 1 ORDER BY name_ar");
$q = trim((string) $request->get('q', ''));
$results = [];
if ($q !== '' && mb_strlen($q) >= 2) {
$results = MemberSearchService::search($q, 50);
}
return $this->view('Members.Views.search', [
'branches' => $branches,
'statuses' => Member::getStatusOptions(),
'results' => null,
'query' => $q,
'results' => $results,
]);
}
}
\ No newline at end of file
......@@ -2,15 +2,21 @@
declare(strict_types=1);
return [
// Member CRUD
['GET', '/members', 'Members\Controllers\MemberController@index', ['auth'], 'member.view'],
['GET', '/members/search', 'Members\Controllers\MemberController@search', ['auth'], 'member.search'],
['GET', '/members/create', 'Members\Controllers\MemberController@create', ['auth'], 'member.create'],
['POST', '/members', 'Members\Controllers\MemberController@store', ['auth'], 'member.create'],
['GET', '/members/search', 'Members\Controllers\MemberController@search', ['auth'], 'member.view'],
['GET', '/members/{id}', 'Members\Controllers\MemberController@show', ['auth'], 'member.view'],
['GET', '/members/{id}/edit', 'Members\Controllers\MemberController@edit', ['auth'], 'member.edit'],
['POST', '/members/{id}', 'Members\Controllers\MemberController@update', ['auth'], 'member.edit'],
['POST', '/members/{id}/status', 'Members\Controllers\MemberController@changeStatus',['auth'], 'member.change_status'],
// Fill Form (استمارة)
['GET', '/members/{id}/fill-form', 'Members\Controllers\MemberController@fillForm', ['auth'], 'member.edit'],
['POST', '/members/{id}/fill-form', 'Members\Controllers\MemberController@saveFillForm',['auth'], 'member.edit'],
// API endpoints
['POST', '/api/members/parse-nid', 'Members\Controllers\MemberApiController@parseNid', ['auth'], 'member.create'],
['POST', '/api/members/quick-search', 'Members\Controllers\MemberApiController@quickSearch', ['auth'], 'member.search'],
['POST', '/api/members/check-duplicate', 'Members\Controllers\MemberApiController@checkDuplicate', ['auth'], 'member.create'],
['POST', '/api/members/search', 'Members\Controllers\MemberApiController@search', ['auth'], 'member.view'],
];
\ No newline at end of file
<?php
declare(strict_types=1);
namespace App\Modules\Members\Services;
use App\Core\App;
final class FormNumberGenerator
{
/**
* Get the next form number.
* Returns null if not configured (super admin must set starting number).
*/
public static function next(): ?int
{
$db = App::getInstance()->db();
// Check existing form numbers in members table
$last = $db->selectOne(
"SELECT MAX(CAST(form_number AS UNSIGNED)) as last_num
FROM members
WHERE form_number IS NOT NULL
AND form_number REGEXP '^[0-9]+$'"
);
$lastNum = (int) ($last['last_num'] ?? 0);
if ($lastNum > 0) {
return $lastNum + 1;
}
// No existing forms — check system config for starting number
$config = $db->selectOne(
"SELECT config_value FROM system_config WHERE config_key = ?",
['membership.form_start_number']
);
if ($config && is_numeric($config['config_value']) && (int) $config['config_value'] > 0) {
return (int) $config['config_value'];
}
// Not configured
return null;
}
/**
* Set the starting form number (called once by super admin).
*/
public static function setStartNumber(int $number): void
{
$db = App::getInstance()->db();
$existing = $db->selectOne(
"SELECT id FROM system_config WHERE config_key = ?",
['membership.form_start_number']
);
if ($existing) {
$db->update(
'system_config',
[
'config_value' => (string) $number,
'updated_at' => date('Y-m-d H:i:s'),
],
'`id` = ?',
[(int) $existing['id']]
);
} else {
$db->insert('system_config', [
'config_key' => 'membership.form_start_number',
'config_value' => (string) $number,
'config_type' => 'integer',
'group_name' => 'membership',
'description_ar' => 'رقم بداية الاستمارات',
'description_en' => 'Starting Form Number',
'is_editable' => 1,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
}
}
/**
* Check if the form number system is configured.
*/
public static function isConfigured(): bool
{
return self::next() !== null;
}
/**
* Get the form fee amount from service catalog or default.
*/
public static function getFormFee(): string
{
try {
$db = App::getInstance()->db();
$fee = $db->selectOne(
"SELECT base_amount FROM service_catalog WHERE service_code = 'FORM_NEW_MEMBERSHIP' AND is_active = 1 LIMIT 1"
);
if ($fee && $fee['base_amount']) {
return $fee['base_amount'];
}
} catch (\Throwable $e) {
}
return '505.00';
}
}
\ No newline at end of file
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>تسجيل عضو جديد<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<form method="POST" action="/members" id="member-form">
<?= csrf_field() ?>
<!-- Personal Data -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;"><h3 style="margin:0;color:#0D7377;">البيانات الشخصية</h3></div>
<div style="padding:20px;display:grid;grid-template-columns:1fr 1fr;gap:15px;">
<div class="form-group" style="grid-column:1/-1;">
<label class="form-label">الاسم بالكامل (عربي) <span style="color:#DC2626;">*</span></label>
<input type="text" name="full_name_ar" value="<?= e(old('full_name_ar')) ?>" class="form-input" required minlength="10" maxlength="200">
</div>
<div class="form-group">
<label class="form-label">الاسم بالكامل (إنجليزي)</label>
<input type="text" name="full_name_en" value="<?= e(old('full_name_en')) ?>" class="form-input">
</div>
<div class="form-group">
<label class="form-label">نوع إثبات الشخصية <span style="color:#DC2626;">*</span></label>
<select name="id_type" id="id_type" class="form-select" required>
<option value="national_id" <?= old('id_type', 'national_id') === 'national_id' ? 'selected' : '' ?>>رقم قومي</option>
<option value="passport" <?= old('id_type') === 'passport' ? 'selected' : '' ?>>جواز سفر</option>
</select>
</div>
<div class="form-group" id="nid-group">
<label class="form-label">الرقم القومي <span style="color:#DC2626;">*</span></label>
<input type="text" name="national_id" id="national_id" value="<?= e(old('national_id')) ?>" class="form-input" maxlength="14" pattern="\d{14}" style="direction:ltr;text-align:left;" placeholder="أدخل 14 رقم">
<div id="nid-feedback" style="margin-top:5px;font-size:12px;"></div>
</div>
<div class="form-group" id="passport-group" style="display:none;">
<label class="form-label">رقم جواز السفر</label>
<input type="text" name="passport_number" value="<?= e(old('passport_number')) ?>" class="form-input" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">تاريخ الميلاد <span style="color:#DC2626;">*</span></label>
<input type="date" name="date_of_birth" id="date_of_birth" value="<?= e(old('date_of_birth')) ?>" class="form-input" required style="background:#F3F4F6;" readonly>
</div>
<div class="form-group">
<label class="form-label">السن</label>
<input type="text" name="age_display" id="age_display" class="form-input" style="background:#F3F4F6;" readonly>
<input type="hidden" name="age_years" id="age_years" value="<?= e(old('age_years')) ?>">
<input type="hidden" name="age_months" id="age_months" value="<?= e(old('age_months')) ?>">
</div>
<div class="form-group">
<label class="form-label">النوع <span style="color:#DC2626;">*</span></label>
<select name="gender" id="gender" class="form-select" required style="background:#F3F4F6;" disabled>
<option value=""></option>
<option value="male" <?= old('gender') === 'male' ? 'selected' : '' ?>>ذكر</option>
<option value="female" <?= old('gender') === 'female' ? 'selected' : '' ?>>أنثى</option>
</select>
<input type="hidden" name="gender" id="gender_hidden" value="<?= e(old('gender')) ?>">
<?php if ($needsStartNumber): ?>
<!-- First time: Super admin must set starting form number -->
<div class="card" style="margin-bottom:20px;padding:20px;border-right:4px solid #D97706;">
<h3 style="color:#D97706;margin:0 0 10px;">⚠ تحديد رقم بداية الاستمارات</h3>
<p style="color:#6B7280;font-size:14px;margin-bottom:15px;">هذه المرة الأولى — يجب تحديد رقم أول استمارة في النظام</p>
<form method="POST" action="/members" id="member-form">
<?= csrf_field() ?>
<div style="display:flex;gap:15px;align-items:end;margin-bottom:20px;">
<div class="form-group" style="flex:0 0 200px;">
<label class="form-label">رقم أول استمارة <span style="color:#DC2626;">*</span></label>
<input type="number" name="form_start_number" class="form-input" required min="1" style="direction:ltr;text-align:left;font-size:18px;font-weight:700;" placeholder="مثال: 15001">
</div>
<div class="form-group">
<label class="form-label">محافظة الميلاد</label>
<input type="text" id="gov_display" class="form-input" style="background:#F3F4F6;" readonly>
<input type="hidden" name="governorate_code" id="governorate_code" value="<?= e(old('governorate_code')) ?>">
</div>
<div class="form-group">
<label class="form-label">الجنسية <span style="color:#DC2626;">*</span></label>
<select name="nationality" class="form-select">
<option value="مصري" selected>مصري</option>
<?php foreach ($countries as $c): ?>
<?php if ($c['nationality_ar'] !== 'مصري'): ?>
<option value="<?= e($c['nationality_ar']) ?>" <?= old('nationality') === $c['nationality_ar'] ? 'selected' : '' ?>><?= e($c['nationality_ar']) ?></option>
<?php else: ?>
<form method="POST" action="/members" id="member-form">
<?= csrf_field() ?>
<?php endif; ?>
<!-- Form Info Banner -->
<div class="card" style="margin-bottom:20px;padding:20px;background:#EFF6FF;border:1px solid #BFDBFE;">
<div style="display:flex;justify-content:space-between;align-items:center;">
<div>
<strong style="color:#0284C7;font-size:16px;">📋 استمارة عضوية جديدة</strong>
<p style="color:#6B7280;font-size:13px;margin:5px 0 0;">أدخل البيانات الأساسية فقط — باقي البيانات تُملأ في الاستمارة بعد دفع الرسوم</p>
</div>
<div style="text-align:left;">
<?php if (!$needsStartNumber && $nextFormNumber): ?>
<div style="font-size:12px;color:#6B7280;">رقم الاستمارة التالي</div>
<div style="font-size:28px;font-weight:700;color:#0D7377;direction:ltr;"><?= (int) $nextFormNumber ?></div>
<?php endif; ?>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label class="form-label">الديانة</label>
<select name="religion" class="form-select">
<option value="">-- اختر --</option>
<option value="muslim" <?= old('religion') === 'muslim' ? 'selected' : '' ?>>مسلم</option>
<option value="christian" <?= old('religion') === 'christian' ? 'selected' : '' ?>>مسيحي</option>
<option value="other" <?= old('religion') === 'other' ? 'selected' : '' ?>>أخرى</option>
</select>
</div>
<div class="form-group">
<label class="form-label">المؤهل الدراسي <span style="color:#DC2626;">*</span></label>
<select name="qualification_id" class="form-select" required>
<option value="">-- اختر --</option>
<?php foreach ($qualifications as $q): ?>
<option value="<?= (int) $q['id'] ?>" <?= old('qualification_id') == $q['id'] ? 'selected' : '' ?>><?= e($q['name_ar']) ?></option>
<?php endforeach; ?>
</select>
<small style="color:#6B7280;">المؤهل يؤثر على قيمة العضوية</small>
</div>
<div class="form-group">
<label class="form-label">الحالة الاجتماعية</label>
<select name="marital_status" class="form-select">
<option value="">-- اختر --</option>
<option value="single" <?= old('marital_status') === 'single' ? 'selected' : '' ?>>أعزب</option>
<option value="married" <?= old('marital_status') === 'married' ? 'selected' : '' ?>>متزوج</option>
<option value="divorced" <?= old('marital_status') === 'divorced' ? 'selected' : '' ?>>مطلق</option>
<option value="widowed" <?= old('marital_status') === 'widowed' ? 'selected' : '' ?>>أرمل</option>
</select>
</div>
<div class="form-group">
<label class="form-label">تاريخ إصدار إثبات الشخصية</label>
<input type="date" name="id_issue_date" value="<?= e(old('id_issue_date')) ?>" class="form-input">
</div>
<div class="form-group">
<label class="form-label">تاريخ انتهاء إثبات الشخصية</label>
<input type="date" name="id_expiry_date" value="<?= e(old('id_expiry_date')) ?>" class="form-input">
<div style="font-size:13px;color:#D97706;font-weight:600;margin-top:5px;">رسوم الاستمارة: <?= money($formFee) ?></div>
</div>
</div>
</div>
<!-- Contact -->
<!-- Basic Info Card -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;"><h3 style="margin:0;color:#0D7377;">بيانات الاتصال</h3></div>
<div style="padding:20px;display:grid;grid-template-columns:1fr 1fr;gap:15px;">
<div class="form-group">
<label class="form-label">رقم المحمول <span style="color:#DC2626;">*</span></label>
<input type="tel" name="phone_mobile" value="<?= e(old('phone_mobile')) ?>" class="form-input" required maxlength="11" style="direction:ltr;text-align:left;" placeholder="01XXXXXXXXX">
</div>
<div class="form-group">
<label class="form-label">تليفون المنزل</label>
<input type="tel" name="phone_home" value="<?= e(old('phone_home')) ?>" class="form-input" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">رقم تليفون خارج مصر</label>
<input type="tel" name="phone_international" value="<?= e(old('phone_international')) ?>" class="form-input" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">البريد الإلكتروني</label>
<input type="email" name="email" value="<?= e(old('email')) ?>" class="form-input" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">شخص للطوارئ — الاسم</label>
<input type="text" name="emergency_name" value="<?= e(old('emergency_name')) ?>" class="form-input">
</div>
<div class="form-group">
<label class="form-label">شخص للطوارئ — المحمول</label>
<input type="tel" name="emergency_phone" value="<?= e(old('emergency_phone')) ?>" class="form-input" style="direction:ltr;text-align:left;">
</div>
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;color:#0D7377;">البيانات الأساسية</h3>
</div>
<div style="padding:20px;display:grid;grid-template-columns:1fr 1fr;gap:20px;">
<!-- Full Name Arabic -->
<div class="form-group" style="grid-column:1/-1;">
<label class="form-label">الاسم بالكامل (عربي) <span style="color:#DC2626;">*</span></label>
<input type="text" name="full_name_ar" value="<?= e(old('full_name_ar')) ?>" class="form-input" required minlength="5" maxlength="200" placeholder="الاسم رباعي بالعربي" style="font-size:16px;">
</div>
<!-- Residence -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;"><h3 style="margin:0;color:#0D7377;">بيانات السكن</h3></div>
<div style="padding:20px;display:grid;grid-template-columns:1fr 1fr;gap:15px;">
<!-- National ID -->
<div class="form-group">
<label class="form-label">نوع السكن</label>
<select name="residence_type" class="form-select">
<option value="">-- اختر --</option>
<option value="rented" <?= old('residence_type') === 'rented' ? 'selected' : '' ?>>إيجار</option>
<option value="owned" <?= old('residence_type') === 'owned' ? 'selected' : '' ?>>ملك</option>
<option value="other" <?= old('residence_type') === 'other' ? 'selected' : '' ?>>أخرى</option>
</select>
<label class="form-label">الرقم القومي <span style="color:#DC2626;">*</span></label>
<input type="text" name="national_id" id="national_id" value="<?= e(old('national_id')) ?>" class="form-input" maxlength="14" style="direction:ltr;text-align:left;font-size:18px;letter-spacing:2px;" placeholder="أدخل 14 رقم">
<div id="nid-feedback" style="margin-top:5px;font-size:12px;"></div>
</div>
<!-- Phone -->
<div class="form-group">
<label class="form-label">المنطقة</label>
<input type="text" name="area" value="<?= e(old('area')) ?>" class="form-input">
</div>
<div class="form-group" style="grid-column:1/-1;">
<label class="form-label">عنوان السكن</label>
<textarea name="residence_address" class="form-textarea" rows="2"><?= e(old('residence_address')) ?></textarea>
<label class="form-label">رقم المحمول <span style="color:#DC2626;">*</span></label>
<input type="tel" name="phone_mobile" value="<?= e(old('phone_mobile')) ?>" class="form-input" required maxlength="11" style="direction:ltr;text-align:left;font-size:16px;" placeholder="01XXXXXXXXX">
</div>
<!-- Branch -->
<div class="form-group">
<label class="form-label">المحافظة</label>
<select name="governorate" class="form-select">
<option value="">-- اختر --</option>
<?php foreach ($governorates as $g): ?>
<option value="<?= e($g['name_ar']) ?>" <?= old('governorate') === $g['name_ar'] ? 'selected' : '' ?>><?= e($g['name_ar']) ?></option>
<label class="form-label">الفرع <span style="color:#DC2626;">*</span></label>
<select name="branch_id" class="form-select" required style="font-size:16px;">
<option value="">-- اختر الفرع --</option>
<?php foreach ($branches as $b): ?>
<option value="<?= (int) $b['id'] ?>" <?= old('branch_id') == $b['id'] ? 'selected' : '' ?>><?= e($b['name_ar']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label class="form-label">عنوان المراسلات</label>
<select name="correspondence_address" class="form-select">
<option value="">-- اختر --</option>
<option value="work" <?= old('correspondence_address') === 'work' ? 'selected' : '' ?>>العمل</option>
<option value="residence" <?= old('correspondence_address') === 'residence' ? 'selected' : '' ?>>السكن</option>
<option value="other" <?= old('correspondence_address') === 'other' ? 'selected' : '' ?>>أخرى</option>
</select>
</div>
</div>
</div>
<!-- Work -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;"><h3 style="margin:0;color:#0D7377;">بيانات العمل</h3></div>
<div style="padding:20px;display:grid;grid-template-columns:1fr 1fr;gap:15px;">
<div class="form-group">
<label class="form-label">نوع التوظيف</label>
<select name="employment_type" class="form-select">
<option value="">-- اختر --</option>
<option value="employed" <?= old('employment_type') === 'employed' ? 'selected' : '' ?>>موظف</option>
<option value="self_employed" <?= old('employment_type') === 'self_employed' ? 'selected' : '' ?>>أعمال حرة</option>
<option value="professions" <?= old('employment_type') === 'professions' ? 'selected' : '' ?>>مهن حرة</option>
<option value="other" <?= old('employment_type') === 'other' ? 'selected' : '' ?>>أخرى</option>
</select>
</div>
<div class="form-group">
<label class="form-label">المهنة</label>
<input type="text" name="occupation" value="<?= e(old('occupation')) ?>" class="form-input">
</div>
<div class="form-group">
<label class="form-label">المركز الوظيفي</label>
<input type="text" name="job_title" value="<?= e(old('job_title')) ?>" class="form-input">
</div>
<!-- Auto-parsed fields (read-only) -->
<div class="form-group">
<label class="form-label">تاريخ الالتحاق بالعمل</label>
<input type="date" name="employment_date" value="<?= e(old('employment_date')) ?>" class="form-input">
</div>
<div class="form-group" style="grid-column:1/-1;">
<label class="form-label">عنوان العمل</label>
<textarea name="business_address" class="form-textarea" rows="2"><?= e(old('business_address')) ?></textarea>
</div>
<div class="form-group">
<label class="form-label">تليفون العمل</label>
<input type="tel" name="office_phone" value="<?= e(old('office_phone')) ?>" class="form-input" style="direction:ltr;text-align:left;">
<label class="form-label">تاريخ الميلاد</label>
<input type="date" name="date_of_birth" id="date_of_birth" value="<?= e(old('date_of_birth')) ?>" class="form-input" style="background:#F3F4F6;" readonly>
</div>
<div class="form-group">
<label class="form-label">نشاط العمل</label>
<input type="text" name="business_activity" value="<?= e(old('business_activity')) ?>" class="form-input">
</div>
</div>
<label class="form-label">السن</label>
<input type="text" id="age_display" class="form-input" style="background:#F3F4F6;" readonly>
<input type="hidden" name="age_years" id="age_years">
<input type="hidden" name="age_months" id="age_months">
</div>
<!-- Membership -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;"><h3 style="margin:0;color:#0D7377;">بيانات العضوية</h3></div>
<div style="padding:20px;display:grid;grid-template-columns:1fr 1fr;gap:15px;">
<div class="form-group">
<label class="form-label">الفرع <span style="color:#DC2626;">*</span></label>
<select name="branch_id" class="form-select" required>
<option value="">-- اختر --</option>
<?php foreach ($branches as $b): ?>
<option value="<?= (int) $b['id'] ?>" <?= old('branch_id') == $b['id'] ? 'selected' : '' ?>><?= e($b['name_ar']) ?></option>
<?php endforeach; ?>
</select>
<label class="form-label">النوع</label>
<input type="text" id="gender_display" class="form-input" style="background:#F3F4F6;" readonly>
<input type="hidden" name="gender" id="gender_hidden" value="<?= e(old('gender')) ?>">
</div>
<div class="form-group">
<label class="form-label">كيف عرفت النادي</label>
<select name="referral_source" class="form-select">
<option value="">-- اختر --</option>
<option value="social_media" <?= old('referral_source') === 'social_media' ? 'selected' : '' ?>>مواقع التواصل الاجتماعي</option>
<option value="tv" <?= old('referral_source') === 'tv' ? 'selected' : '' ?>>إعلان التليفزيون</option>
<option value="friend" <?= old('referral_source') === 'friend' ? 'selected' : '' ?>>من خلال صديق</option>
<option value="radio" <?= old('referral_source') === 'radio' ? 'selected' : '' ?>>إعلان الراديو</option>
<option value="outdoor" <?= old('referral_source') === 'outdoor' ? 'selected' : '' ?>>إعلانات الطريق</option>
</select>
<label class="form-label">محافظة الميلاد</label>
<input type="text" id="gov_display" class="form-input" style="background:#F3F4F6;" readonly>
<input type="hidden" name="governorate_code" id="governorate_code">
</div>
</div>
</div>
<div style="display:flex;gap:10px;">
<button type="submit" class="btn btn-primary">تسجيل العضو</button>
<a href="/members" class="btn btn-outline">إلغاء</a>
<button type="submit" class="btn btn-primary" style="padding:12px 30px;font-size:16px;">📋 تسجيل وإنشاء الاستمارة</button>
<a href="/members" class="btn btn-outline" style="padding:12px 20px;">إلغاء</a>
</div>
</form>
......@@ -259,60 +111,24 @@
<?php $__template->section('scripts'); ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
var idTypeSelect = document.getElementById('id_type');
var nidGroup = document.getElementById('nid-group');
var passportGroup = document.getElementById('passport-group');
var nidInput = document.getElementById('national_id');
var dobInput = document.getElementById('date_of_birth');
var ageDisplay = document.getElementById('age_display');
var ageYears = document.getElementById('age_years');
var ageMonths = document.getElementById('age_months');
var genderSelect = document.getElementById('gender');
var genderDisplay = document.getElementById('gender_display');
var genderHidden = document.getElementById('gender_hidden');
var govDisplay = document.getElementById('gov_display');
var govCode = document.getElementById('governorate_code');
var feedback = document.getElementById('nid-feedback');
function toggleIdType() {
if (idTypeSelect.value === 'passport') {
nidGroup.style.display = 'none';
passportGroup.style.display = 'block';
dobInput.removeAttribute('readonly');
dobInput.style.background = '';
genderSelect.removeAttribute('disabled');
genderSelect.style.background = '';
} else {
nidGroup.style.display = 'block';
passportGroup.style.display = 'none';
dobInput.setAttribute('readonly', 'readonly');
dobInput.style.background = '#F3F4F6';
}
}
idTypeSelect.addEventListener('change', toggleIdType);
toggleIdType();
nidInput.addEventListener('input', function() {
var val = this.value.replace(/\D/g, '');
this.value = val;
if (val.length === 14) {
parseNid(val);
} else {
feedback.innerHTML = '';
dobInput.value = '';
ageDisplay.value = '';
ageYears.value = '';
ageMonths.value = '';
genderSelect.value = '';
genderHidden.value = '';
govDisplay.value = '';
govCode.value = '';
}
});
function parseNid(nid) {
feedback.innerHTML = '<span style="color:#0284C7;">جاري التحقق...</span>';
var formData = new FormData();
formData.append('national_id', nid);
formData.append('national_id', val);
var csrfToken = document.querySelector('input[name="_csrf_token"]');
if (csrfToken) formData.append('_csrf_token', csrfToken.value);
......@@ -325,25 +141,27 @@ document.addEventListener('DOMContentLoaded', function() {
ageDisplay.value = p.age_years + ' سنة و ' + p.age_months + ' شهر';
ageYears.value = p.age_years;
ageMonths.value = p.age_months;
genderSelect.value = p.gender;
genderDisplay.value = p.gender === 'male' ? 'ذكر' : 'أنثى';
genderHidden.value = p.gender;
govDisplay.value = p.governorate_name_ar || '';
govCode.value = p.governorate_code || '';
feedback.innerHTML = '<span style="color:#059669;">✓ الرقم القومي صالح</span>';
if (data.duplicate) {
var d = data.duplicate;
var msg = d.is_archived ? '⚠ هذا الرقم مسجل في الأرشيف: ' : '✖ هذا الرقم مسجل بالفعل: ';
feedback.innerHTML += '<br><span style="color:' + (d.is_archived ? '#D97706' : '#DC2626') + ';">' + msg + d.full_name_ar + ' (' + (d.membership_number || 'بدون رقم') + ')</span>';
feedback.innerHTML += '<br><span style="color:#DC2626;">✖ مسجل بالفعل: ' + data.duplicate.full_name_ar + '</span>';
}
} else {
feedback.innerHTML = '<span style="color:#DC2626;">✖ ' + (p.errors ? p.errors.join(' | ') : 'رقم غير صالح') + '</span>';
feedback.innerHTML = '<span style="color:#DC2626;">✖ ' + (p.errors ? p.errors.join(' | ') : 'غير صالح') + '</span>';
}
})
.catch(function() {
feedback.innerHTML = '<span style="color:#DC2626;">خطأ في الاتصال</span>';
});
.catch(function() { feedback.innerHTML = '<span style="color:#DC2626;">خطأ</span>'; });
} else {
feedback.innerHTML = '';
dobInput.value = '';
ageDisplay.value = '';
genderDisplay.value = '';
govDisplay.value = '';
}
});
});
</script>
<?php $__template->endSection(); ?>
\ No newline at end of file
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>ملء الاستمارة — <?= e($member->full_name_ar) ?><?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<!-- Header -->
<div class="card" style="margin-bottom:20px;padding:20px;background:#FFF7ED;border:1px solid #FED7AA;">
<div style="display:flex;justify-content:space-between;align-items:center;">
<div>
<h2 style="margin:0 0 5px;color:#D97706;">📋 استمارة رقم <?= e($member->form_number ?? '—') ?></h2>
<p style="margin:0;color:#6B7280;font-size:14px;">
<strong><?= e($member->full_name_ar) ?></strong>
— الرقم القومي: <?= e($member->national_id ?? '—') ?>
— تاريخ الاستمارة: <?= e($member->form_date ?? date('Y-m-d')) ?>
</p>
</div>
<a href="/members/<?= (int) $member->id ?>" class="btn btn-outline">← العودة للعضو</a>
</div>
</div>
<form method="POST" action="/members/<?= (int) $member->id ?>/fill-form">
<?= csrf_field() ?>
<!-- Section 1: Personal Details -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;color:#0D7377;">1. البيانات الشخصية التكميلية</h3>
</div>
<div style="padding:20px;display:grid;grid-template-columns:1fr 1fr;gap:15px;">
<div class="form-group">
<label class="form-label">الاسم بالإنجليزي</label>
<input type="text" name="full_name_en" value="<?= e($member->full_name_en ?? '') ?>" class="form-input" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">محل الميلاد</label>
<input type="text" name="place_of_birth" value="<?= e($member->place_of_birth ?? '') ?>" class="form-input">
</div>
<div class="form-group">
<label class="form-label">الجنسية</label>
<select name="nationality" class="form-select">
<option value="مصري" <?= ($member->nationality ?? '') === 'مصري' ? 'selected' : '' ?>>مصري</option>
<?php foreach ($countries as $c): ?>
<?php if ($c['nationality_ar'] !== 'مصري'): ?>
<option value="<?= e($c['nationality_ar']) ?>" <?= ($member->nationality ?? '') === $c['nationality_ar'] ? 'selected' : '' ?>><?= e($c['nationality_ar']) ?></option>
<?php endif; ?>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label class="form-label">الديانة</label>
<select name="religion" class="form-select">
<option value="">-- اختر --</option>
<option value="muslim" <?= ($member->religion ?? '') === 'muslim' ? 'selected' : '' ?>>مسلم</option>
<option value="christian" <?= ($member->religion ?? '') === 'christian' ? 'selected' : '' ?>>مسيحي</option>
<option value="other" <?= ($member->religion ?? '') === 'other' ? 'selected' : '' ?>>أخرى</option>
</select>
</div>
<div class="form-group">
<label class="form-label">المؤهل الدراسي <span style="color:#DC2626;">*</span></label>
<select name="qualification_id" class="form-select" required>
<option value="">-- اختر المؤهل --</option>
<?php foreach ($qualifications as $q): ?>
<option value="<?= (int) $q['id'] ?>" <?= ($member->qualification_id ?? 0) == $q['id'] ? 'selected' : '' ?>><?= e($q['name_ar']) ?></option>
<?php endforeach; ?>
</select>
<small style="color:#D97706;">المؤهل يؤثر على قيمة العضوية</small>
</div>
<div class="form-group">
<label class="form-label">الحالة الاجتماعية</label>
<select name="marital_status" class="form-select">
<option value="">-- اختر --</option>
<option value="single" <?= ($member->marital_status ?? '') === 'single' ? 'selected' : '' ?>>أعزب</option>
<option value="married" <?= ($member->marital_status ?? '') === 'married' ? 'selected' : '' ?>>متزوج</option>
<option value="divorced" <?= ($member->marital_status ?? '') === 'divorced' ? 'selected' : '' ?>>مطلق</option>
<option value="widowed" <?= ($member->marital_status ?? '') === 'widowed' ? 'selected' : '' ?>>أرمل</option>
</select>
</div>
<div class="form-group">
<label class="form-label">تاريخ إصدار إثبات الشخصية</label>
<input type="date" name="id_issue_date" value="<?= e($member->id_issue_date ?? '') ?>" class="form-input">
</div>
<div class="form-group">
<label class="form-label">تاريخ انتهاء إثبات الشخصية</label>
<input type="date" name="id_expiry_date" value="<?= e($member->id_expiry_date ?? '') ?>" class="form-input">
</div>
</div>
</div>
<!-- Section 2: Contact -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;color:#0D7377;">2. بيانات الاتصال</h3>
</div>
<div style="padding:20px;display:grid;grid-template-columns:1fr 1fr;gap:15px;">
<div class="form-group">
<label class="form-label">تليفون المنزل</label>
<input type="tel" name="phone_home" value="<?= e($member->phone_home ?? '') ?>" class="form-input" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">رقم تليفون خارج مصر</label>
<input type="tel" name="phone_international" value="<?= e($member->phone_international ?? '') ?>" class="form-input" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">البريد الإلكتروني</label>
<input type="email" name="email" value="<?= e($member->email ?? '') ?>" class="form-input" style="direction:ltr;text-align:left;">
</div>
<div class="form-group"></div>
<div class="form-group">
<label class="form-label">شخص للطوارئ — الاسم</label>
<input type="text" name="emergency_name" value="<?= e($member->emergency_name ?? '') ?>" class="form-input">
</div>
<div class="form-group">
<label class="form-label">شخص للطوارئ — المحمول</label>
<input type="tel" name="emergency_phone" value="<?= e($member->emergency_phone ?? '') ?>" class="form-input" style="direction:ltr;text-align:left;">
</div>
</div>
</div>
<!-- Section 3: Residence -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;color:#0D7377;">3. بيانات السكن</h3>
</div>
<div style="padding:20px;display:grid;grid-template-columns:1fr 1fr;gap:15px;">
<div class="form-group">
<label class="form-label">نوع السكن</label>
<select name="residence_type" class="form-select">
<option value="">-- اختر --</option>
<option value="owned" <?= ($member->residence_type ?? '') === 'owned' ? 'selected' : '' ?>>ملك</option>
<option value="rented" <?= ($member->residence_type ?? '') === 'rented' ? 'selected' : '' ?>>إيجار</option>
<option value="other" <?= ($member->residence_type ?? '') === 'other' ? 'selected' : '' ?>>أخرى</option>
</select>
</div>
<div class="form-group">
<label class="form-label">المنطقة</label>
<input type="text" name="area" value="<?= e($member->area ?? '') ?>" class="form-input">
</div>
<div class="form-group" style="grid-column:1/-1;">
<label class="form-label">عنوان السكن</label>
<textarea name="residence_address" class="form-textarea" rows="2"><?= e($member->residence_address ?? '') ?></textarea>
</div>
<div class="form-group">
<label class="form-label">علامة مميزة</label>
<input type="text" name="landmark" value="<?= e($member->landmark ?? '') ?>" class="form-input">
</div>
<div class="form-group">
<label class="form-label">الدور</label>
<input type="text" name="floor" value="<?= e($member->floor ?? '') ?>" class="form-input">
</div>
<div class="form-group">
<label class="form-label">الشقة</label>
<input type="text" name="apartment" value="<?= e($member->apartment ?? '') ?>" class="form-input">
</div>
<div class="form-group">
<label class="form-label">المحافظة</label>
<select name="governorate" class="form-select">
<option value="">-- اختر --</option>
<?php foreach ($governorates as $g): ?>
<option value="<?= e($g['name_ar']) ?>" <?= ($member->governorate ?? '') === $g['name_ar'] ? 'selected' : '' ?>><?= e($g['name_ar']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label class="form-label">عنوان المراسلات</label>
<select name="correspondence_address" class="form-select">
<option value="">-- اختر --</option>
<option value="residence" <?= ($member->correspondence_address ?? '') === 'residence' ? 'selected' : '' ?>>السكن</option>
<option value="work" <?= ($member->correspondence_address ?? '') === 'work' ? 'selected' : '' ?>>العمل</option>
<option value="other" <?= ($member->correspondence_address ?? '') === 'other' ? 'selected' : '' ?>>أخرى</option>
</select>
</div>
</div>
</div>
<!-- Section 4: Employment -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;color:#0D7377;">4. بيانات العمل</h3>
</div>
<div style="padding:20px;display:grid;grid-template-columns:1fr 1fr;gap:15px;">
<div class="form-group">
<label class="form-label">نوع التوظيف</label>
<select name="employment_type" class="form-select">
<option value="">-- اختر --</option>
<option value="employed" <?= ($member->employment_type ?? '') === 'employed' ? 'selected' : '' ?>>موظف</option>
<option value="self_employed" <?= ($member->employment_type ?? '') === 'self_employed' ? 'selected' : '' ?>>أعمال حرة</option>
<option value="professions" <?= ($member->employment_type ?? '') === 'professions' ? 'selected' : '' ?>>مهن حرة</option>
<option value="retired" <?= ($member->employment_type ?? '') === 'retired' ? 'selected' : '' ?>>متقاعد</option>
<option value="other" <?= ($member->employment_type ?? '') === 'other' ? 'selected' : '' ?>>أخرى</option>
</select>
</div>
<div class="form-group">
<label class="form-label">المهنة</label>
<input type="text" name="occupation" value="<?= e($member->occupation ?? '') ?>" class="form-input">
</div>
<div class="form-group">
<label class="form-label">المركز الوظيفي</label>
<input type="text" name="job_title" value="<?= e($member->job_title ?? '') ?>" class="form-input">
</div>
<div class="form-group">
<label class="form-label">تاريخ الالتحاق بالعمل</label>
<input type="date" name="employment_date" value="<?= e($member->employment_date ?? '') ?>" class="form-input">
</div>
<div class="form-group" style="grid-column:1/-1;">
<label class="form-label">عنوان العمل</label>
<textarea name="business_address" class="form-textarea" rows="2"><?= e($member->business_address ?? '') ?></textarea>
</div>
<div class="form-group">
<label class="form-label">تليفون العمل</label>
<input type="tel" name="office_phone" value="<?= e($member->office_phone ?? '') ?>" class="form-input" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">فاكس العمل</label>
<input type="tel" name="office_fax" value="<?= e($member->office_fax ?? '') ?>" class="form-input" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">نشاط العمل</label>
<input type="text" name="business_activity" value="<?= e($member->business_activity ?? '') ?>" class="form-input">
</div>
</div>
</div>
<!-- Section 5: How did you hear -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;color:#0D7377;">5. معلومات إضافية</h3>
</div>
<div style="padding:20px;display:grid;grid-template-columns:1fr 1fr;gap:15px;">
<div class="form-group">
<label class="form-label">كيف عرفت النادي</label>
<select name="referral_source" class="form-select">
<option value="">-- اختر --</option>
<option value="social_media" <?= ($member->referral_source ?? '') === 'social_media' ? 'selected' : '' ?>>مواقع التواصل الاجتماعي</option>
<option value="tv" <?= ($member->referral_source ?? '') === 'tv' ? 'selected' : '' ?>>إعلان تليفزيون</option>
<option value="friend" <?= ($member->referral_source ?? '') === 'friend' ? 'selected' : '' ?>>من خلال صديق</option>
<option value="radio" <?= ($member->referral_source ?? '') === 'radio' ? 'selected' : '' ?>>إعلان راديو</option>
<option value="outdoor" <?= ($member->referral_source ?? '') === 'outdoor' ? 'selected' : '' ?>>إعلانات الطريق</option>
<option value="member" <?= ($member->referral_source ?? '') === 'member' ? 'selected' : '' ?>>عضو بالنادي</option>
<option value="other" <?= ($member->referral_source ?? '') === 'other' ? 'selected' : '' ?>>أخرى</option>
</select>
</div>
</div>
</div>
<div style="display:flex;gap:10px;">
<button type="submit" class="btn btn-primary" style="padding:12px 30px;font-size:16px;">✓ حفظ الاستمارة</button>
<a href="/members/<?= (int) $member->id ?>" class="btn btn-outline" style="padding:12px 20px;">إلغاء</a>
</div>
</form>
<?php $__template->endSection(); ?>
\ No newline at end of file
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>العضو: <?= e($member->full_name_ar) ?><?php $__template->endSection(); ?>
<?php $__template->section('title'); ?><?= e($member->full_name_ar) ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/members/<?= (int) $member->id ?>/edit" class="btn btn-outline">تعديل</a>
<a href="/members" class="btn btn-outline">قائمة الأعضاء</a>
<a href="/members/<?= (int) $member->id ?>/edit" class="btn btn-outline">✏️ تعديل</a>
<a href="/members" class="btn btn-outline">العودة</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<!-- Profile Header -->
<div class="card" style="margin-bottom:20px;padding:20px;display:flex;justify-content:space-between;align-items:center;">
<?php
$statusLabel = $member->getStatusLabel();
$statusColor = $member->getStatusColor();
?>
<!-- Form Not Filled Banner -->
<?php if (!$formFilled && in_array($member->status, ['potential'])): ?>
<div class="card" style="margin-bottom:20px;padding:25px;background:linear-gradient(135deg, #FFF7ED, #FEF3C7);border:2px solid #F59E0B;">
<div style="display:flex;justify-content:space-between;align-items:center;">
<div>
<h3 style="color:#D97706;margin:0 0 8px;">📋 الاستمارة لم تُملأ بعد</h3>
<p style="color:#92400E;margin:0;font-size:14px;">
استمارة رقم <strong style="font-size:18px;"><?= e($member->form_number) ?></strong>
— رسوم الاستمارة: <strong><?= money($formFee) ?></strong>
</p>
<p style="color:#6B7280;margin:8px 0 0;font-size:13px;">يجب دفع رسوم الاستمارة ثم ملء باقي البيانات</p>
</div>
<a href="/members/<?= (int) $member->id ?>/fill-form" class="btn btn-primary" style="padding:15px 30px;font-size:16px;background:#D97706;border-color:#D97706;">
📝 ملء الاستمارة
</a>
</div>
</div>
<?php endif; ?>
<!-- Member Header Card -->
<div class="card" style="margin-bottom:20px;overflow:visible;">
<div style="padding:25px;">
<div style="display:flex;justify-content:space-between;align-items:start;">
<div>
<h2 style="margin:0;color:#1A1A2E;"><?= e($member->full_name_ar) ?></h2>
<?php if ($member->full_name_en): ?><div style="color:#6B7280;margin-top:4px;"><?= e($member->full_name_en) ?></div><?php endif; ?>
<div style="margin-top:8px;display:flex;gap:15px;font-size:13px;color:#6B7280;">
<span>رقم العضوية: <strong style="color:#0D7377;"><?= e($member->membership_number ?: 'لم يُحدد بعد') ?></strong></span>
<span>الفرع: <strong><?= e($member->getBranchName()) ?></strong></span>
<span>النوع: <strong><?= e($member->getGenderLabel()) ?></strong></span>
<h2 style="margin:0 0 5px;color:#1A1A2E;font-size:22px;"><?= e($member->full_name_ar) ?></h2>
<?php if ($member->full_name_en): ?>
<div style="color:#6B7280;font-size:14px;margin-bottom:8px;"><?= e($member->full_name_en) ?></div>
<?php endif; ?>
<div style="display:flex;gap:15px;flex-wrap:wrap;font-size:13px;color:#6B7280;margin-top:10px;">
<?php if ($member->membership_number): ?>
<span>🪪 رقم العضوية: <strong style="color:#0D7377;font-size:16px;"><?= e($member->membership_number) ?></strong></span>
<?php endif; ?>
<span>📋 استمارة: <strong style="color:#D97706;"><?= e($member->form_number ?? '—') ?></strong></span>
<span>🏢 <?= e($branchName) ?></span>
<span><?= $member->gender === 'male' ? '👨' : '👩' ?> <?= $member->getGenderLabel() ?></span>
<?php if ($member->age_years): ?>
<span>🎂 <?= (int) $member->age_years ?> سنة</span>
<?php endif; ?>
</div>
</div>
<div style="text-align:left;">
<span style="display:inline-block;padding:6px 16px;border-radius:20px;font-weight:700;font-size:14px;color:#fff;background:<?= $member->getStatusColor() ?>;">
<?= e($member->getStatusLabel()) ?>
<span style="display:inline-block;padding:6px 16px;border-radius:20px;font-weight:700;font-size:14px;color:#fff;background:<?= $statusColor ?>;">
<?= e($statusLabel) ?>
</span>
<div style="margin-top:10px;font-size:12px;color:#9CA3AF;">
تاريخ التسجيل: <?= e(substr($member->created_at, 0, 10)) ?>
</div>
</div>
</div>
</div>
</div>
<!-- Tabs -->
<div style="display:flex;gap:0;margin-bottom:20px;border-bottom:2px solid #E5E7EB;">
<?php
$tabs = [
'personal' => 'البيانات الشخصية',
'family' => 'الأسرة',
'financial' => 'المالية',
'documents' => 'المستندات',
'activity' => 'النشاط',
];
?>
<?php foreach ($tabs as $key => $label): ?>
<button class="tab-btn" data-tab="<?= $key ?>" style="padding:10px 20px;border:none;background:transparent;cursor:pointer;font-size:14px;font-weight:600;color:#6B7280;border-bottom:3px solid transparent;<?= $key === 'personal' ? 'color:#0D7377;border-bottom-color:#0D7377;' : '' ?>" onclick="switchTab('<?= $key ?>')"><?= e($label) ?></button>
<?php endforeach; ?>
</div>
<!-- Info Grid -->
<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px;margin-bottom:20px;">
<!-- Tab: Personal -->
<div id="tab-personal" class="tab-content">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px;">
<div class="card" style="padding:20px;">
<h4 style="color:#0D7377;margin-bottom:15px;">البيانات الشخصية</h4>
<table style="width:100%;font-size:14px;">
<tr><td style="padding:6px 0;color:#6B7280;width:40%;">الرقم القومي</td><td style="padding:6px 0;direction:ltr;text-align:right;font-weight:600;"><?= e($member->national_id ?: '—') ?></td></tr>
<tr><td style="padding:6px 0;color:#6B7280;">جواز السفر</td><td style="padding:6px 0;"><?= e($member->passport_number ?: '—') ?></td></tr>
<tr><td style="padding:6px 0;color:#6B7280;">تاريخ الميلاد</td><td style="padding:6px 0;"><?= e($member->date_of_birth) ?></td></tr>
<tr><td style="padding:6px 0;color:#6B7280;">السن</td><td style="padding:6px 0;"><?= (int) $member->age_years ?> سنة <?= $member->age_months ? 'و ' . (int) $member->age_months . ' شهر' : '' ?></td></tr>
<tr><td style="padding:6px 0;color:#6B7280;">الجنسية</td><td style="padding:6px 0;"><?= e($member->nationality ?: '—') ?></td></tr>
<tr><td style="padding:6px 0;color:#6B7280;">الديانة</td><td style="padding:6px 0;"><?= e($member->religion ?: '—') ?></td></tr>
<tr><td style="padding:6px 0;color:#6B7280;">المؤهل</td><td style="padding:6px 0;"><?= e($member->getQualificationName()) ?></td></tr>
<tr><td style="padding:6px 0;color:#6B7280;">الحالة الاجتماعية</td><td style="padding:6px 0;"><?= e($member->marital_status ?: '—') ?></td></tr>
<tr><td style="padding:6px 0;color:#6B7280;">قيمة العضوية</td><td style="padding:6px 0;font-weight:700;color:#0D7377;"><?= $member->membership_value ? money($member->membership_value) : '—' ?></td></tr>
</table>
</div>
<div class="card" style="padding:20px;">
<h4 style="color:#0D7377;margin-bottom:15px;">بيانات الاتصال</h4>
<table style="width:100%;font-size:14px;">
<tr><td style="padding:6px 0;color:#6B7280;width:40%;">المحمول</td><td style="padding:6px 0;direction:ltr;text-align:right;"><?= e($member->phone_mobile) ?></td></tr>
<tr><td style="padding:6px 0;color:#6B7280;">المنزل</td><td style="padding:6px 0;direction:ltr;text-align:right;"><?= e($member->phone_home ?: '—') ?></td></tr>
<tr><td style="padding:6px 0;color:#6B7280;">البريد</td><td style="padding:6px 0;direction:ltr;text-align:right;"><?= e($member->email ?: '—') ?></td></tr>
<tr><td style="padding:6px 0;color:#6B7280;">طوارئ</td><td style="padding:6px 0;"><?= e($member->emergency_name ?: '—') ?><?= e($member->emergency_phone ?: '') ?></td></tr>
</table>
<h4 style="color:#0D7377;margin:20px 0 15px;">العمل</h4>
<table style="width:100%;font-size:14px;">
<tr><td style="padding:6px 0;color:#6B7280;width:40%;">المهنة</td><td style="padding:6px 0;"><?= e($member->occupation ?: '—') ?></td></tr>
<tr><td style="padding:6px 0;color:#6B7280;">جهة العمل</td><td style="padding:6px 0;"><?= e($member->business_address ?: '—') ?></td></tr>
<!-- Personal Info -->
<div class="card">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;color:#0D7377;font-size:15px;">👤 البيانات الشخصية</h3>
</div>
<div style="padding:15px 20px;">
<table style="width:100%;font-size:13px;">
<tr><td style="padding:5px 0;color:#6B7280;width:40%;">الرقم القومي</td><td style="padding:5px 0;direction:ltr;text-align:right;font-weight:600;"><?= e($member->national_id ?? '—') ?></td></tr>
<tr><td style="padding:5px 0;color:#6B7280;">تاريخ الميلاد</td><td style="padding:5px 0;"><?= e($member->date_of_birth ?? '—') ?></td></tr>
<tr><td style="padding:5px 0;color:#6B7280;">المحمول</td><td style="padding:5px 0;direction:ltr;text-align:right;"><?= e($member->phone_mobile ?? '—') ?></td></tr>
<tr><td style="padding:5px 0;color:#6B7280;">الجنسية</td><td style="padding:5px 0;"><?= e($member->nationality ?? '—') ?></td></tr>
<tr><td style="padding:5px 0;color:#6B7280;">المؤهل</td><td style="padding:5px 0;"><?= e($qualificationName) ?></td></tr>
<tr><td style="padding:5px 0;color:#6B7280;">الحالة الاجتماعية</td><td style="padding:5px 0;"><?= e($member->marital_status ?? '—') ?></td></tr>
<tr><td style="padding:5px 0;color:#6B7280;">الديانة</td><td style="padding:5px 0;"><?= e($member->religion ?? '—') ?></td></tr>
<?php if ($member->email): ?>
<tr><td style="padding:5px 0;color:#6B7280;">البريد</td><td style="padding:5px 0;direction:ltr;text-align:right;"><?= e($member->email) ?></td></tr>
<?php endif; ?>
</table>
</div>
</div>
<!-- Notes -->
<div class="card" style="margin-top:20px;padding:20px;">
<h4 style="color:#0D7377;margin-bottom:15px;">الملاحظات</h4>
<?php if (!empty($notes)): ?>
<?php foreach ($notes as $note): ?>
<div style="padding:10px 0;border-bottom:1px solid #F3F4F6;font-size:13px;">
<div style="color:#1A1A2E;"><?= nl2br(e($note['note_text'])) ?></div>
<div style="color:#9CA3AF;font-size:11px;margin-top:4px;"><?= e($note['employee_name'] ?? 'النظام') ?><?= e($note['created_at']) ?></div>
<!-- Financial Summary -->
<div class="card">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;color:#0D7377;font-size:15px;">💰 الملخص المالي</h3>
</div>
<?php endforeach; ?>
<?php else: ?>
<p style="color:#6B7280;">لا توجد ملاحظات</p>
<div style="padding:15px 20px;">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:15px;margin-bottom:15px;">
<div style="background:#F0FDF4;padding:15px;border-radius:8px;text-align:center;">
<div style="font-size:22px;font-weight:700;color:#059669;"><?= money($member->membership_value ?? '0') ?></div>
<div style="color:#6B7280;font-size:12px;">قيمة العضوية</div>
</div>
<div style="background:#EFF6FF;padding:15px;border-radius:8px;text-align:center;">
<div style="font-size:22px;font-weight:700;color:#0284C7;"><?= money($totalPaid) ?></div>
<div style="color:#6B7280;font-size:12px;">إجمالي المدفوع</div>
</div>
</div>
<a href="/payments/member/<?= (int) $member->id ?>" class="btn btn-outline" style="width:100%;text-align:center;">عرض كل المدفوعات</a>
</div>
</div>
</div>
<!-- Quick Actions -->
<div class="card" style="margin-bottom:20px;padding:15px;">
<div style="display:flex;gap:10px;flex-wrap:wrap;">
<?php if (!$formFilled && $member->status === 'potential'): ?>
<a href="/members/<?= (int) $member->id ?>/fill-form" class="btn btn-primary">📝 ملء الاستمارة</a>
<?php endif; ?>
<a href="/members/<?= (int) $member->id ?>/spouses/create" class="btn btn-outline">💍 إضافة زوج/ة</a>
<a href="/members/<?= (int) $member->id ?>/children/create" class="btn btn-outline">👶 إضافة ابن/ة</a>
<a href="/payments/process/<?= (int) $member->id ?>" class="btn btn-outline">💰 تسجيل دفعة</a>
<a href="/documents/upload/<?= (int) $member->id ?>" class="btn btn-outline">📁 رفع مستندات</a>
<a href="/interviews/schedule/<?= (int) $member->id ?>" class="btn btn-outline">🗓️ تحديد مقابلة</a>
<a href="/installments/create/<?= (int) $member->id ?>" class="btn btn-outline">📅 خطة تقسيط</a>
<?php if ($member->status === 'active'): ?>
<a href="/carnets/issue/<?= (int) $member->id ?>" class="btn btn-outline">🪪 إصدار كارنيه</a>
<?php endif; ?>
</div>
</div>
<!-- Tab: Family -->
<div id="tab-family" class="tab-content" style="display:none;">
<div class="card" style="padding:20px;margin-bottom:20px;">
<h4 style="color:#0D7377;margin-bottom:15px;">الزوجات</h4>
<?php if (!empty($spouses)): ?>
<table class="data-table"><thead><tr><th>#</th><th>الاسم</th><th>الرقم القومي</th><th>تاريخ الميلاد</th><th>السن</th><th>التصنيف</th><th>الحالة</th></tr></thead><tbody>
<!-- Family: Spouses -->
<?php if (!empty($spouses)): ?>
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;justify-content:space-between;align-items:center;">
<h3 style="margin:0;color:#0D7377;font-size:15px;">💍 الزوجات (<?= count($spouses) ?>)</h3>
<a href="/members/<?= (int) $member->id ?>/spouses/create" class="btn btn-sm btn-outline">+ إضافة</a>
</div>
<div class="table-responsive">
<table class="data-table">
<thead><tr><th>#</th><th>الاسم</th><th>الرقم القومي</th><th>تاريخ الميلاد</th><th>الحالة</th><th>الإجراءات</th></tr></thead>
<tbody>
<?php foreach ($spouses as $s): ?>
<tr><td><?= (int) $s['spouse_order'] ?></td><td><?= e($s['full_name_ar']) ?></td><td style="direction:ltr;text-align:right;"><?= e($s['national_id'] ?? '—') ?></td><td><?= e($s['date_of_birth']) ?></td><td><?= (int) ($s['age_years'] ?? 0) ?></td><td><?= e($s['classification'] ?? '—') ?></td><td><?= e($s['status']) ?></td></tr>
<tr>
<td><?= (int) $s['spouse_order'] ?></td>
<td style="font-weight:600;"><?= e($s['full_name_ar']) ?></td>
<td style="direction:ltr;text-align:right;font-size:12px;"><?= e($s['national_id'] ?? '—') ?></td>
<td style="font-size:13px;"><?= e($s['date_of_birth']) ?></td>
<td><span style="color:<?= $s['status'] === 'active' ? '#059669' : '#DC2626' ?>;font-weight:600;"><?= $s['status'] === 'active' ? 'نشط' : $s['status'] ?></span></td>
<td><a href="/members/<?= (int) $member->id ?>/spouses/<?= (int) $s['id'] ?>" class="btn btn-sm btn-outline">عرض</a></td>
</tr>
<?php endforeach; ?>
</tbody></table>
<?php else: ?><p style="color:#6B7280;">لا توجد زوجات مسجلة</p><?php endif; ?>
</tbody>
</table>
</div>
<div class="card" style="padding:20px;margin-bottom:20px;">
<h4 style="color:#0D7377;margin-bottom:15px;">الأبناء</h4>
<?php if (!empty($children)): ?>
<table class="data-table"><thead><tr><th>#</th><th>الاسم</th><th>النوع</th><th>تاريخ الميلاد</th><th>السن</th><th>التصنيف</th><th>الحالة</th></tr></thead><tbody>
</div>
<?php endif; ?>
<!-- Family: Children -->
<?php if (!empty($children)): ?>
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;justify-content:space-between;align-items:center;">
<h3 style="margin:0;color:#0D7377;font-size:15px;">👨‍👩‍👧‍👦 الأبناء (<?= count($children) ?>)</h3>
<a href="/members/<?= (int) $member->id ?>/children/create" class="btn btn-sm btn-outline">+ إضافة</a>
</div>
<div class="table-responsive">
<table class="data-table">
<thead><tr><th>#</th><th>الاسم</th><th>النوع</th><th>تاريخ الميلاد</th><th>السن</th><th>التصنيف</th><th>الحالة</th><th>الإجراءات</th></tr></thead>
<tbody>
<?php foreach ($children as $c): ?>
<tr><td><?= (int) $c['child_order'] ?></td><td><?= e($c['full_name_ar']) ?></td><td><?= $c['gender'] === 'male' ? 'ذكر' : 'أنثى' ?></td><td><?= e($c['date_of_birth']) ?></td><td><?= (int) ($c['age_years'] ?? 0) ?></td><td><?= e($c['classification'] ?? '—') ?></td><td><?= e($c['status']) ?></td></tr>
<?php endforeach; ?>
</tbody></table>
<?php else: ?><p style="color:#6B7280;">لا يوجد أبناء مسجلون</p><?php endif; ?>
</div>
<div class="card" style="padding:20px;">
<h4 style="color:#0D7377;margin-bottom:15px;">الأعضاء المؤقتون</h4>
<?php if (!empty($temporary)): ?>
<table class="data-table"><thead><tr><th>الاسم</th><th>الفئة</th><th>السن</th><th>الحالة</th></tr></thead><tbody>
<?php foreach ($temporary as $t): ?>
<tr><td><?= e($t['full_name_ar']) ?></td><td><?= e($t['category']) ?></td><td><?= (int) ($t['age_years'] ?? 0) ?></td><td><?= e($t['status']) ?></td></tr>
<tr>
<td><?= (int) $c['child_order'] ?></td>
<td style="font-weight:600;"><?= e($c['full_name_ar']) ?></td>
<td><?= $c['gender'] === 'male' ? 'ذكر' : 'أنثى' ?></td>
<td style="font-size:13px;"><?= e($c['date_of_birth']) ?></td>
<td><?= (int) ($c['age_years'] ?? 0) ?></td>
<td style="font-size:12px;"><?= e($c['classification'] ?? '—') ?></td>
<td><span style="color:<?= $c['status'] === 'active' ? '#059669' : '#DC2626' ?>;font-weight:600;"><?= $c['status'] === 'active' ? 'نشط' : $c['status'] ?></span></td>
<td><a href="/members/<?= (int) $member->id ?>/children/<?= (int) $c['id'] ?>" class="btn btn-sm btn-outline">عرض</a></td>
</tr>
<?php endforeach; ?>
</tbody></table>
<?php else: ?><p style="color:#6B7280;">لا يوجد أعضاء مؤقتون</p><?php endif; ?>
</tbody>
</table>
</div>
</div>
<?php endif; ?>
<!-- Tab: Financial -->
<div id="tab-financial" class="tab-content" style="display:none;">
<div class="card" style="padding:40px;text-align:center;color:#6B7280;">
<p>البيانات المالية ستكون متاحة بعد تفعيل وحدة المدفوعات</p>
<!-- Recent Payments -->
<?php if (!empty($payments)): ?>
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;color:#0D7377;font-size:15px;">💳 آخر المدفوعات</h3>
</div>
<div class="table-responsive">
<table class="data-table">
<thead><tr><th>التاريخ</th><th>النوع</th><th>المبلغ</th><th>الإيصال</th></tr></thead>
<tbody>
<?php foreach ($payments as $p): ?>
<tr>
<td style="font-size:13px;"><?= e($p['payment_date']) ?></td>
<td style="font-size:13px;"><?= e(\App\Modules\Payments\Models\Payment::getPaymentTypeLabel($p['payment_type'])) ?></td>
<td style="font-weight:600;"><?= money($p['amount']) ?></td>
<td style="font-size:12px;direction:ltr;text-align:right;"><?= e($p['receipt_number'] ?? '—') ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<?php endif; ?>
<!-- Tab: Documents -->
<div id="tab-documents" class="tab-content" style="display:none;">
<div class="card" style="padding:40px;text-align:center;color:#6B7280;">
<p>المستندات ستكون متاحة بعد تفعيل وحدة إدارة المستندات</p>
<!-- Status Change -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;color:#0D7377;font-size:15px;">🔄 تغيير الحالة</h3>
</div>
<div style="padding:15px 20px;">
<form method="POST" action="/members/<?= (int) $member->id ?>/status" style="display:flex;gap:10px;align-items:center;">
<?= csrf_field() ?>
<select name="status" class="form-select" style="width:auto;">
<?php foreach (\App\Modules\Members\Models\Member::getStatusOptions() as $val => $label): ?>
<option value="<?= e($val) ?>" <?= $member->status === $val ? 'selected' : '' ?>><?= e($label) ?></option>
<?php endforeach; ?>
</select>
<button type="submit" class="btn btn-outline" onclick="return confirm('تغيير حالة العضو؟')">تحديث الحالة</button>
</form>
</div>
</div>
<!-- Tab: Activity -->
<div id="tab-activity" class="tab-content" style="display:none;">
<div class="card" style="padding:20px;">
<h4 style="color:#0D7377;margin-bottom:15px;">سجل النشاط</h4>
<?php if (!empty($auditHistory)): ?>
<?php foreach ($auditHistory as $i => $entry): ?>
<div style="display:flex;gap:15px;padding:12px 0;<?= $i < count($auditHistory) - 1 ? 'border-bottom:1px solid #F3F4F6;' : '' ?>">
<div style="flex-shrink:0;width:10px;height:10px;border-radius:50%;background:#0D7377;margin-top:5px;"></div>
<div style="flex:1;">
<div style="display:flex;justify-content:space-between;"><strong style="font-size:13px;"><?= e($entry['action']) ?></strong><span style="font-size:11px;color:#9CA3AF;"><?= e($entry['created_at']) ?></span></div>
<div style="font-size:12px;color:#6B7280;"><?= e($entry['employee_name'] ?? 'النظام') ?><?php if ($entry['notes']): ?><?= e(mb_substr($entry['notes'], 0, 100)) ?><?php endif; ?></div>
<!-- Notes -->
<?php if (!empty($notes)): ?>
<div class="card">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;color:#0D7377;font-size:15px;">📝 الملاحظات</h3>
</div>
<div style="padding:15px 20px;">
<?php foreach ($notes as $note): ?>
<div style="padding:10px 0;border-bottom:1px solid #F3F4F6;">
<div style="font-size:13px;"><?= nl2br(e($note['note_text'])) ?></div>
<div style="font-size:11px;color:#9CA3AF;margin-top:5px;"><?= e($note['employee_name'] ?? 'النظام') ?><?= e($note['created_at']) ?></div>
</div>
<?php endforeach; ?>
<?php else: ?>
<p style="color:#6B7280;">لا يوجد سجل نشاط</p>
<?php endif; ?>
<div style="margin-top:15px;"><a href="/audit/entity/members/<?= (int) $member->id ?>" class="btn btn-sm btn-outline">عرض السجل الكامل</a></div>
</div>
</div>
<?php endif; ?>
<?php $__template->endSection(); ?>
\ No newline at end of file
<?php $__template->section('scripts'); ?>
<script>
function switchTab(tabName) {
document.querySelectorAll('.tab-content').forEach(function(el) { el.style.display = 'none'; });
document.querySelectorAll('.tab-btn').forEach(function(el) { el.style.color = '#6B7280'; el.style.borderBottomColor = 'transparent'; });
var tab = document.getElementById('tab-' + tabName);
var btn = document.querySelector('[data-tab="' + tabName + '"]');
if (tab) tab.style.display = 'block';
if (btn) { btn.style.color = '#0D7377'; btn.style.borderBottomColor = '#0D7377'; }
}
</script>
<?php $__template->endSection(); ?>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment