built user profile added the logo created the family view etc

This commit is contained in:
Ghassan Yusuf 2026-01-20 16:19:00 +03:00
parent 4a7353e3fc
commit 43c2676d6f
33 changed files with 3783 additions and 257 deletions

View File

@ -0,0 +1,61 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class AuthenticatedSessionController extends Controller
{
/**
* Display the login view.
*
* @return \Illuminate\View\View
*/
public function create()
{
return view('auth.login');
}
/**
* Handle an incoming authentication request.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(Request $request)
{
$credentials = $request->validate([
'email' => ['required', 'email'],
'password' => ['required'],
]);
if (Auth::attempt($credentials)) {
$request->session()->regenerate();
return redirect()->route('family.dashboard');
}
return back()->withErrors([
'email' => 'The provided credentials do not match our records.',
])->onlyInput('email');
}
/**
* Destroy an authenticated session.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(Request $request)
{
Auth::guard('web')->logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect('/');
}
}

View File

@ -0,0 +1,64 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Str;
class NewPasswordController extends Controller
{
/**
* Display the password reset view.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*/
public function create(Request $request)
{
return view('auth.reset-password', ['request' => $request]);
}
/**
* Handle an incoming new password request.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Illuminate\Validation\ValidationException
*/
public function store(Request $request)
{
$request->validate([
'token' => ['required'],
'email' => ['required', 'email'],
'password' => ['required', 'confirmed', 'min:8'],
]);
// Here we will attempt to reset the user's password. If it is successful we
// will update the password on an actual user model and persist it to the
// database. Otherwise we will parse the error and return the response.
$status = Password::reset(
$request->only('email', 'password', 'password_confirmation', 'token'),
function ($user) use ($request) {
$user->forceFill([
'password' => Hash::make($request->password),
'remember_token' => Str::random(60),
])->save();
event(new PasswordReset($user));
}
);
// If the password was successfully reset, we will redirect the user back to
// the application's home authenticated view. If there is an error we can
// redirect them back to where they came from with their error message.
return $status == Password::PASSWORD_RESET
? redirect()->route('login')->with('status', __($status))
: back()->withInput($request->only('email'))
->withErrors(['email' => __($status)]);
}
}

View File

@ -0,0 +1,47 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;
class PasswordResetLinkController extends Controller
{
/**
* Display the password reset link request view.
*
* @return \Illuminate\View\View
*/
public function create()
{
return view('auth.forgot-password');
}
/**
* Handle an incoming password reset link request.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Illuminate\Validation\ValidationException
*/
public function store(Request $request)
{
$request->validate([
'email' => ['required', 'email'],
]);
// We will send the password reset link to this user. Once we have attempted
// to send the link, we will examine the response then see the message we
// need to show to the user. Finally, we'll send out a proper response.
$status = Password::sendResetLink(
$request->only('email')
);
return $status == Password::RESET_LINK_SENT
? back()->with('status', __($status))
: back()->withInput($request->only('email'))
->withErrors(['email' => __($status)]);
}
}

View File

@ -0,0 +1,68 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Models\User;
use App\Mail\WelcomeEmail;
use Illuminate\Auth\Events\Registered;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Mail;
use Illuminate\Validation\Rules;
class RegisteredUserController extends Controller
{
/**
* Display the registration view.
*
* @return \Illuminate\View\View
*/
public function create()
{
return view('auth.register');
}
/**
* Handle an incoming registration request.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Illuminate\Validation\ValidationException
*/
public function store(Request $request)
{
$request->validate([
'full_name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users,email'],
'password' => ['required', 'confirmed', Rules\Password::defaults()],
'mobile_number' => ['required', 'string', 'max:20'],
'gender' => ['required', 'in:m,f'],
'birthdate' => ['required', 'date', 'before:today'],
'country_code' => ['required', 'string', 'max:10'],
'nationality' => ['required', 'string', 'max:255'],
]);
$user = User::create([
'name' => $request->full_name,
'full_name' => $request->full_name,
'email' => $request->email,
'password' => Hash::make($request->password),
'mobile' => $request->country_code . $request->mobile_number,
'gender' => $request->gender,
'birthdate' => $request->birthdate,
'nationality' => $request->nationality,
]);
event(new Registered($user));
// Send welcome email
Mail::to($user->email)->send(new WelcomeEmail($user, $user, null));
Auth::login($user);
return redirect()->route('login')->with('success', 'Registration successful! Please login with your credentials.');
}
}

View File

@ -27,12 +27,35 @@ class FamilyController extends Controller
$user = Auth::user();
$dependents = UserRelationship::where('guardian_user_id', $user->id)
->with('dependent')
->get();
->get()
->sortBy(function($relationship) {
return $relationship->dependent->full_name;
});
$familyInvoices = $this->familyService->getFamilyInvoices($user->id);
return view('family.dashboard', compact('user', 'dependents', 'familyInvoices'));
}
/**
* Display the current user's profile.
*
* @return \Illuminate\View\View
*/
public function profile()
{
$user = Auth::user();
// Pass user directly and a flag to indicate it's the current user's profile
return view('family.show', [
'relationship' => (object)[
'dependent' => $user,
'relationship_type' => 'self',
'guardian_user_id' => $user->id,
'dependent_user_id' => $user->id,
]
]);
}
/**
* Show the form for creating a new family member.
*
@ -54,7 +77,7 @@ class FamilyController extends Controller
$validated = $request->validate([
'full_name' => 'required|string|max:255',
'email' => 'nullable|email|max:255',
'gender' => 'required|in:male,female',
'gender' => 'required|in:m,f',
'birthdate' => 'required|date',
'blood_type' => 'nullable|string|max:10',
'nationality' => 'required|string|max:100',
@ -115,7 +138,7 @@ class FamilyController extends Controller
$validated = $request->validate([
'full_name' => 'required|string|max:255',
'email' => 'nullable|email|max:255',
'gender' => 'required|in:male,female',
'gender' => 'required|in:m,f',
'birthdate' => 'required|date',
'blood_type' => 'nullable|string|max:10',
'nationality' => 'required|string|max:100',

83
app/Mail/WelcomeEmail.php Normal file
View File

@ -0,0 +1,83 @@
<?php
namespace App\Mail;
use App\Models\User;
use App\Models\UserRelationship;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
class WelcomeEmail extends Mailable
{
/**
* The user instance.
*
* @var \App\Models\User
*/
public $user;
/**
* The guardian user instance.
*
* @var \App\Models\User|null
*/
public $guardian;
/**
* The relationship instance.
*
* @var \App\Models\UserRelationship|null
*/
public $relationship;
/**
* Create a new message instance.
*
* @param \App\Models\User $user
* @param \App\Models\User|null $guardian
* @param \App\Models\UserRelationship|null $relationship
* @return void
*/
public function __construct(User $user, ?User $guardian = null, ?UserRelationship $relationship = null)
{
$this->user = $user;
$this->guardian = $guardian;
$this->relationship = $relationship;
}
/**
* Get the message envelope.
*
* @return \Illuminate\Mail\Mailables\Envelope
*/
public function envelope()
{
return new Envelope(
subject: 'Welcome to ' . config('app.name', 'Club SaaS') . ' - ' . $this->user->full_name,
);
}
/**
* Get the message content definition.
*
* @return \Illuminate\Mail\Mailables\Content
*/
public function content()
{
return new Content(
view: 'emails.welcome',
);
}
/**
* Get the attachments for the message.
*
* @return array
*/
public function attachments()
{
return [];
}
}

View File

@ -22,6 +22,7 @@ class User extends Authenticatable
* @var list<string>
*/
protected $fillable = [
'name',
'full_name',
'email',
'mobile',

View File

@ -5,6 +5,8 @@ namespace App\Services;
use App\Models\User;
use App\Models\UserRelationship;
use App\Models\Invoice;
use App\Mail\WelcomeEmail;
use Illuminate\Support\Facades\Mail;
class FamilyService
{
@ -17,15 +19,24 @@ class FamilyService
*/
public function createDependent(User $guardian, array $data): User
{
// Normalize gender value to match database enum (m/f)
$gender = $data['gender'];
if ($gender === 'male') {
$gender = 'm';
} elseif ($gender === 'female') {
$gender = 'f';
}
// Create the dependent user
$dependent = User::create([
'name' => $data['full_name'], // Required by database
'full_name' => $data['full_name'],
'email' => $data['email'] ?? null,
'password' => $data['password'] ?? null,
'mobile' => $data['mobile'] ?? null,
'gender' => $data['gender'],
'gender' => $gender,
'birthdate' => $data['birthdate'],
'blood_type' => $data['blood_type'] ?? null,
'blood_type' => $data['blood_type'] ?? 'Unknown',
'nationality' => $data['nationality'],
'addresses' => $data['addresses'] ?? [],
'social_links' => $data['social_links'] ?? [],
@ -33,13 +44,18 @@ class FamilyService
]);
// Create the relationship between guardian and dependent
UserRelationship::create([
$relationship = UserRelationship::create([
'guardian_user_id' => $guardian->id,
'dependent_user_id' => $dependent->id,
'relationship_type' => $data['relationship_type'],
'is_billing_contact' => $data['is_billing_contact'] ?? false,
]);
// Send welcome email if dependent has an email address
if (!empty($dependent->email)) {
Mail::to($dependent->email)->send(new WelcomeEmail($dependent, $guardian, $relationship));
}
return $dependent;
}

View File

@ -1,103 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
// 1. Drop the existing users table
Schema::dropIfExists('users');
// 2. Create a new users table with all required columns
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('full_name');
$table->string('email')->nullable()->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('mobile')->nullable();
$table->string('password')->nullable();
$table->rememberToken();
$table->enum('gender', ['male', 'female']);
$table->date('birthdate');
$table->string('blood_type')->nullable();
$table->string('nationality');
$table->json('addresses');
$table->json('social_links');
$table->json('media_gallery');
$table->timestamps();
});
// 3. Create user_relationships table
Schema::create('user_relationships', function (Blueprint $table) {
$table->id();
$table->foreignId('guardian_user_id')->constrained('users')->cascadeOnDelete();
$table->foreignId('dependent_user_id')->constrained('users')->cascadeOnDelete();
$table->string('relationship_type'); // son, daughter, spouse, sponsor, other
$table->boolean('is_billing_contact')->default(false);
$table->timestamps();
});
// 4. Create tenants table (The Clubs)
Schema::create('tenants', function (Blueprint $table) {
$table->id();
$table->foreignId('owner_user_id')->constrained('users')->cascadeOnDelete();
$table->string('club_name');
$table->string('slug')->unique();
$table->string('logo')->nullable();
$table->decimal('gps_lat', 10, 7)->nullable();
$table->decimal('gps_long', 10, 7)->nullable();
$table->timestamps();
});
// 5. Create memberships table
Schema::create('memberships', function (Blueprint $table) {
$table->id();
$table->foreignId('tenant_id')->constrained('tenants')->cascadeOnDelete();
$table->foreignId('user_id')->constrained('users')->cascadeOnDelete();
$table->enum('status', ['active', 'inactive'])->default('active');
$table->timestamps();
});
// 6. Create invoices table
Schema::create('invoices', function (Blueprint $table) {
$table->id();
$table->foreignId('tenant_id')->constrained('tenants')->cascadeOnDelete();
$table->decimal('amount', 10, 2);
$table->string('status');
$table->date('due_date');
$table->foreignId('student_user_id')->constrained('users')->cascadeOnDelete();
$table->foreignId('payer_user_id')->constrained('users')->cascadeOnDelete();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
// Drop tables in reverse order
Schema::dropIfExists('invoices');
Schema::dropIfExists('memberships');
Schema::dropIfExists('tenants');
Schema::dropIfExists('user_relationships');
Schema::dropIfExists('users');
// Recreate the original users table
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
};

View File

@ -0,0 +1,128 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
// 1. Modify the existing users table to add Club SaaS columns
Schema::table('users', function (Blueprint $table) {
// Add full_name column
$table->string('full_name')->after('id');
// Make email nullable
$table->string('email')->nullable()->change();
// Add mobile column (nullable)
$table->string('mobile')->nullable()->after('email');
// Make password nullable
$table->string('password')->nullable()->change();
// Add gender enum column
$table->enum('gender', ['m', 'f'])->nullable()->after('remember_token');
// Add birthdate date column
$table->date('birthdate')->nullable()->after('gender');
// Add blood_type enum column with default
$table->enum('blood_type', [
'A+', 'A-', 'B+', 'B-', 'AB+', 'AB-', 'O+', 'O-', 'Unknown'
])->default('Unknown')->after('birthdate');
// Add nationality string column
$table->string('nationality')->nullable()->after('blood_type');
// Add addresses JSON column
$table->json('addresses')->nullable()->after('nationality');
// Add social_links JSON column
$table->json('social_links')->nullable()->after('addresses');
// Add media_gallery JSON column
$table->json('media_gallery')->nullable()->after('social_links');
});
// 2. Create user_relationships table (Linking Guardians to Dependents)
Schema::create('user_relationships', function (Blueprint $table) {
$table->id();
$table->foreignId('guardian_user_id')->constrained('users')->cascadeOnDelete();
$table->foreignId('dependent_user_id')->constrained('users')->cascadeOnDelete();
$table->string('relationship_type'); // son, daughter, spouse, sponsor, other
$table->boolean('is_billing_contact')->default(false);
$table->timestamps();
});
// 3. Create tenants table (The Clubs)
Schema::create('tenants', function (Blueprint $table) {
$table->id();
$table->foreignId('owner_user_id')->constrained('users')->cascadeOnDelete();
$table->string('club_name');
$table->string('slug')->unique();
$table->string('logo')->nullable();
$table->decimal('gps_lat', 10, 7)->nullable();
$table->decimal('gps_long', 10, 7)->nullable();
$table->timestamps();
});
// 4. Create memberships table
Schema::create('memberships', function (Blueprint $table) {
$table->id();
$table->foreignId('tenant_id')->constrained('tenants')->cascadeOnDelete();
$table->foreignId('user_id')->constrained('users')->cascadeOnDelete();
$table->enum('status', ['active', 'inactive'])->default('active');
$table->timestamps();
});
// 5. Create invoices table (Crucial for Payer logic)
Schema::create('invoices', function (Blueprint $table) {
$table->id();
$table->foreignId('tenant_id')->constrained('tenants')->cascadeOnDelete();
$table->decimal('amount', 10, 2);
$table->string('status');
$table->date('due_date');
$table->foreignId('student_user_id')->constrained('users')->cascadeOnDelete(); // Who took the class
$table->foreignId('payer_user_id')->constrained('users')->cascadeOnDelete(); // Who pays (could be a parent/sponsor)
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
// Drop tables in reverse order
Schema::dropIfExists('invoices');
Schema::dropIfExists('memberships');
Schema::dropIfExists('tenants');
Schema::dropIfExists('user_relationships');
// Revert users table modifications
Schema::table('users', function (Blueprint $table) {
$table->dropColumn([
'full_name',
'mobile',
'gender',
'birthdate',
'blood_type',
'nationality',
'addresses',
'social_links',
'media_gallery',
]);
// Revert email to not nullable
$table->string('email')->nullable(false)->change();
// Revert password to not nullable
$table->string('password')->nullable(false)->change();
});
}
};

194
public/data/countries.json Normal file
View File

@ -0,0 +1,194 @@
[
{"name":"Afghanistan","iso2":"AF","iso3":"AFG","flag":"af","call_code":"+93","currency":"AFN","currency_symbol":"؋","timezone":"Asia/Kabul"},
{"name":"Albania","iso2":"AL","iso3":"ALB","flag":"al","call_code":"+355","currency":"ALL","currency_symbol":"L","timezone":"Europe/Tirane"},
{"name":"Algeria","iso2":"DZ","iso3":"DZA","flag":"dz","call_code":"+213","currency":"DZD","currency_symbol":"د.ج","timezone":"Africa/Algiers"},
{"name":"Andorra","iso2":"AD","iso3":"AND","flag":"ad","call_code":"+376","currency":"EUR","currency_symbol":"€","timezone":"Europe/Andorra"},
{"name":"Angola","iso2":"AO","iso3":"AGO","flag":"ao","call_code":"+244","currency":"AOA","currency_symbol":"Kz","timezone":"Africa/Luanda"},
{"name":"Argentina","iso2":"AR","iso3":"ARG","flag":"ar","call_code":"+54","currency":"ARS","currency_symbol":"$","timezone":"America/Argentina/Buenos_Aires"},
{"name":"Armenia","iso2":"AM","iso3":"ARM","flag":"am","call_code":"+374","currency":"AMD","currency_symbol":"֏","timezone":"Asia/Yerevan"},
{"name":"Australia","iso2":"AU","iso3":"AUS","flag":"au","call_code":"+61","currency":"AUD","currency_symbol":"$","timezone":"Australia/Sydney"},
{"name":"Austria","iso2":"AT","iso3":"AUT","flag":"at","call_code":"+43","currency":"EUR","currency_symbol":"€","timezone":"Europe/Vienna"},
{"name":"Azerbaijan","iso2":"AZ","iso3":"AZE","flag":"az","call_code":"+994","currency":"AZN","currency_symbol":"₼","timezone":"Asia/Baku"},
{"name":"Bahamas","iso2":"BS","iso3":"BHS","flag":"bs","call_code":"+1-242","currency":"BSD","currency_symbol":"$","timezone":"America/Nassau"},
{"name":"Bahrain","iso2":"BH","iso3":"BHR","flag":"bh","call_code":"+973","currency":"BHD","currency_symbol":".د.ب","timezone":"Asia/Bahrain"},
{"name":"Bangladesh","iso2":"BD","iso3":"BGD","flag":"bd","call_code":"+880","currency":"BDT","currency_symbol":"৳","timezone":"Asia/Dhaka"},
{"name":"Barbados","iso2":"BB","iso3":"BRB","flag":"bb","call_code":"+1-246","currency":"BBD","currency_symbol":"$","timezone":"America/Barbados"},
{"name":"Belarus","iso2":"BY","iso3":"BLR","flag":"by","call_code":"+375","currency":"BYN","currency_symbol":"Br","timezone":"Europe/Minsk"},
{"name":"Belgium","iso2":"BE","iso3":"BEL","flag":"be","call_code":"+32","currency":"EUR","currency_symbol":"€","timezone":"Europe/Brussels"},
{"name":"Belize","iso2":"BZ","iso3":"BLZ","flag":"bz","call_code":"+501","currency":"BZD","currency_symbol":"$","timezone":"America/Belize"},
{"name":"Benin","iso2":"BJ","iso3":"BEN","flag":"bj","call_code":"+229","currency":"XOF","currency_symbol":"Fr","timezone":"Africa/Porto-Novo"},
{"name":"Bhutan","iso2":"BT","iso3":"BTN","flag":"bt","call_code":"+975","currency":"BTN","currency_symbol":"Nu.","timezone":"Asia/Thimphu"},
{"name":"Bolivia","iso2":"BO","iso3":"BOL","flag":"bo","call_code":"+591","currency":"BOB","currency_symbol":"Bs.","timezone":"America/La_Paz"},
{"name":"Bosnia and Herzegovina","iso2":"BA","iso3":"BIH","flag":"ba","call_code":"+387","currency":"BAM","currency_symbol":"KM","timezone":"Europe/Sarajevo"},
{"name":"Botswana","iso2":"BW","iso3":"BWA","flag":"bw","call_code":"+267","currency":"BWP","currency_symbol":"P","timezone":"Africa/Gaborone"},
{"name":"Brazil","iso2":"BR","iso3":"BRA","flag":"br","call_code":"+55","currency":"BRL","currency_symbol":"R$","timezone":"America/Sao_Paulo"},
{"name":"Brunei","iso2":"BN","iso3":"BRN","flag":"bn","call_code":"+673","currency":"BND","currency_symbol":"$","timezone":"Asia/Brunei"},
{"name":"Bulgaria","iso2":"BG","iso3":"BGR","flag":"bg","call_code":"+359","currency":"BGN","currency_symbol":"лв","timezone":"Europe/Sofia"},
{"name":"Burkina Faso","iso2":"BF","iso3":"BFA","flag":"bf","call_code":"+226","currency":"XOF","currency_symbol":"Fr","timezone":"Africa/Ouagadougou"},
{"name":"Burundi","iso2":"BI","iso3":"BDI","flag":"bi","call_code":"+257","currency":"BIF","currency_symbol":"Fr","timezone":"Africa/Bujumbura"},
{"name":"Cambodia","iso2":"KH","iso3":"KHM","flag":"kh","call_code":"+855","currency":"KHR","currency_symbol":"៛","timezone":"Asia/Phnom_Penh"},
{"name":"Cameroon","iso2":"CM","iso3":"CMR","flag":"cm","call_code":"+237","currency":"XAF","currency_symbol":"Fr","timezone":"Africa/Douala"},
{"name":"Canada","iso2":"CA","iso3":"CAN","flag":"ca","call_code":"+1","currency":"CAD","currency_symbol":"$","timezone":"America/Toronto"},
{"name":"Cape Verde","iso2":"CV","iso3":"CPV","flag":"cv","call_code":"+238","currency":"CVE","currency_symbol":"$","timezone":"Atlantic/Cape_Verde"},
{"name":"Central African Republic","iso2":"CF","iso3":"CAF","flag":"cf","call_code":"+236","currency":"XAF","currency_symbol":"Fr","timezone":"Africa/Bangui"},
{"name":"Chad","iso2":"TD","iso3":"TCD","flag":"td","call_code":"+235","currency":"XAF","currency_symbol":"Fr","timezone":"Africa/Ndjamena"},
{"name":"Chile","iso2":"CL","iso3":"CHL","flag":"cl","call_code":"+56","currency":"CLP","currency_symbol":"$","timezone":"America/Santiago"},
{"name":"China","iso2":"CN","iso3":"CHN","flag":"cn","call_code":"+86","currency":"CNY","currency_symbol":"¥","timezone":"Asia/Shanghai"},
{"name":"Colombia","iso2":"CO","iso3":"COL","flag":"co","call_code":"+57","currency":"COP","currency_symbol":"$","timezone":"America/Bogota"},
{"name":"Comoros","iso2":"KM","iso3":"COM","flag":"km","call_code":"+269","currency":"KMF","currency_symbol":"Fr","timezone":"Indian/Comoro"},
{"name":"Congo","iso2":"CG","iso3":"COG","flag":"cg","call_code":"+242","currency":"XAF","currency_symbol":"Fr","timezone":"Africa/Brazzaville"},
{"name":"Costa Rica","iso2":"CR","iso3":"CRI","flag":"cr","call_code":"+506","currency":"CRC","currency_symbol":"₡","timezone":"America/Costa_Rica"},
{"name":"Croatia","iso2":"HR","iso3":"HRV","flag":"hr","call_code":"+385","currency":"EUR","currency_symbol":"€","timezone":"Europe/Zagreb"},
{"name":"Cuba","iso2":"CU","iso3":"CUB","flag":"cu","call_code":"+53","currency":"CUP","currency_symbol":"$","timezone":"America/Havana"},
{"name":"Cyprus","iso2":"CY","iso3":"CYP","flag":"cy","call_code":"+357","currency":"EUR","currency_symbol":"€","timezone":"Asia/Nicosia"},
{"name":"Czech Republic","iso2":"CZ","iso3":"CZE","flag":"cz","call_code":"+420","currency":"CZK","currency_symbol":"Kč","timezone":"Europe/Prague"},
{"name":"Denmark","iso2":"DK","iso3":"DNK","flag":"dk","call_code":"+45","currency":"DKK","currency_symbol":"kr","timezone":"Europe/Copenhagen"},
{"name":"Djibouti","iso2":"DJ","iso3":"DJI","flag":"dj","call_code":"+253","currency":"DJF","currency_symbol":"Fr","timezone":"Africa/Djibouti"},
{"name":"Dominica","iso2":"DM","iso3":"DMA","flag":"dm","call_code":"+1-767","currency":"XCD","currency_symbol":"$","timezone":"America/Dominica"},
{"name":"Dominican Republic","iso2":"DO","iso3":"DOM","flag":"do","call_code":"+1-809","currency":"DOP","currency_symbol":"$","timezone":"America/Santo_Domingo"},
{"name":"Ecuador","iso2":"EC","iso3":"ECU","flag":"ec","call_code":"+593","currency":"USD","currency_symbol":"$","timezone":"America/Guayaquil"},
{"name":"Egypt","iso2":"EG","iso3":"EGY","flag":"eg","call_code":"+20","currency":"EGP","currency_symbol":"£","timezone":"Africa/Cairo"},
{"name":"El Salvador","iso2":"SV","iso3":"SLV","flag":"sv","call_code":"+503","currency":"USD","currency_symbol":"$","timezone":"America/El_Salvador"},
{"name":"Equatorial Guinea","iso2":"GQ","iso3":"GNQ","flag":"gq","call_code":"+240","currency":"XAF","currency_symbol":"Fr","timezone":"Africa/Malabo"},
{"name":"Eritrea","iso2":"ER","iso3":"ERI","flag":"er","call_code":"+291","currency":"ERN","currency_symbol":"Nfk","timezone":"Africa/Asmara"},
{"name":"Estonia","iso2":"EE","iso3":"EST","flag":"ee","call_code":"+372","currency":"EUR","currency_symbol":"€","timezone":"Europe/Tallinn"},
{"name":"Eswatini","iso2":"SZ","iso3":"SWZ","flag":"sz","call_code":"+268","currency":"SZL","currency_symbol":"L","timezone":"Africa/Mbabane"},
{"name":"Ethiopia","iso2":"ET","iso3":"ETH","flag":"et","call_code":"+251","currency":"ETB","currency_symbol":"Br","timezone":"Africa/Addis_Ababa"},
{"name":"Fiji","iso2":"FJ","iso3":"FJI","flag":"fj","call_code":"+679","currency":"FJD","currency_symbol":"$","timezone":"Pacific/Fiji"},
{"name":"Finland","iso2":"FI","iso3":"FIN","flag":"fi","call_code":"+358","currency":"EUR","currency_symbol":"€","timezone":"Europe/Helsinki"},
{"name":"France","iso2":"FR","iso3":"FRA","flag":"fr","call_code":"+33","currency":"EUR","currency_symbol":"€","timezone":"Europe/Paris"},
{"name":"Gabon","iso2":"GA","iso3":"GAB","flag":"ga","call_code":"+241","currency":"XAF","currency_symbol":"Fr","timezone":"Africa/Libreville"},
{"name":"Gambia","iso2":"GM","iso3":"GMB","flag":"gm","call_code":"+220","currency":"GMD","currency_symbol":"D","timezone":"Africa/Banjul"},
{"name":"Georgia","iso2":"GE","iso3":"GEO","flag":"ge","call_code":"+995","currency":"GEL","currency_symbol":"₾","timezone":"Asia/Tbilisi"},
{"name":"Germany","iso2":"DE","iso3":"DEU","flag":"de","call_code":"+49","currency":"EUR","currency_symbol":"€","timezone":"Europe/Berlin"},
{"name":"Ghana","iso2":"GH","iso3":"GHA","flag":"gh","call_code":"+233","currency":"GHS","currency_symbol":"₵","timezone":"Africa/Accra"},
{"name":"Greece","iso2":"GR","iso3":"GRC","flag":"gr","call_code":"+30","currency":"EUR","currency_symbol":"€","timezone":"Europe/Athens"},
{"name":"Grenada","iso2":"GD","iso3":"GRD","flag":"gd","call_code":"+1-473","currency":"XCD","currency_symbol":"$","timezone":"America/Grenada"},
{"name":"Guatemala","iso2":"GT","iso3":"GTM","flag":"gt","call_code":"+502","currency":"GTQ","currency_symbol":"Q","timezone":"America/Guatemala"},
{"name":"Guinea","iso2":"GN","iso3":"GIN","flag":"gn","call_code":"+224","currency":"GNF","currency_symbol":"Fr","timezone":"Africa/Conakry"},
{"name":"Guinea-Bissau","iso2":"GW","iso3":"GNB","flag":"gw","call_code":"+245","currency":"XOF","currency_symbol":"Fr","timezone":"Africa/Bissau"},
{"name":"Guyana","iso2":"GY","iso3":"GUY","flag":"gy","call_code":"+592","currency":"GYD","currency_symbol":"$","timezone":"America/Guyana"},
{"name":"Haiti","iso2":"HT","iso3":"HTI","flag":"ht","call_code":"+509","currency":"HTG","currency_symbol":"G","timezone":"America/Port-au-Prince"},
{"name":"Honduras","iso2":"HN","iso3":"HND","flag":"hn","call_code":"+504","currency":"HNL","currency_symbol":"L","timezone":"America/Tegucigalpa"},
{"name":"Hungary","iso2":"HU","iso3":"HUN","flag":"hu","call_code":"+36","currency":"HUF","currency_symbol":"Ft","timezone":"Europe/Budapest"},
{"name":"Iceland","iso2":"IS","iso3":"ISL","flag":"is","call_code":"+354","currency":"ISK","currency_symbol":"kr","timezone":"Atlantic/Reykjavik"},
{"name":"India","iso2":"IN","iso3":"IND","flag":"in","call_code":"+91","currency":"INR","currency_symbol":"₹","timezone":"Asia/Kolkata"},
{"name":"Indonesia","iso2":"ID","iso3":"IDN","flag":"id","call_code":"+62","currency":"IDR","currency_symbol":"Rp","timezone":"Asia/Jakarta"},
{"name":"Iran","iso2":"IR","iso3":"IRN","flag":"ir","call_code":"+98","currency":"IRR","currency_symbol":"﷼","timezone":"Asia/Tehran"},
{"name":"Iraq","iso2":"IQ","iso3":"IRQ","flag":"iq","call_code":"+964","currency":"IQD","currency_symbol":"ع.د","timezone":"Asia/Baghdad"},
{"name":"Ireland","iso2":"IE","iso3":"IRL","flag":"ie","call_code":"+353","currency":"EUR","currency_symbol":"€","timezone":"Europe/Dublin"},
{"name":"Italy","iso2":"IT","iso3":"ITA","flag":"it","call_code":"+39","currency":"EUR","currency_symbol":"€","timezone":"Europe/Rome"},
{"name":"Jamaica","iso2":"JM","iso3":"JAM","flag":"jm","call_code":"+1-876","currency":"JMD","currency_symbol":"$","timezone":"America/Jamaica"},
{"name":"Japan","iso2":"JP","iso3":"JPN","flag":"jp","call_code":"+81","currency":"JPY","currency_symbol":"¥","timezone":"Asia/Tokyo"},
{"name":"Jordan","iso2":"JO","iso3":"JOR","flag":"jo","call_code":"+962","currency":"JOD","currency_symbol":"د.ا","timezone":"Asia/Amman"},
{"name":"Kazakhstan","iso2":"KZ","iso3":"KAZ","flag":"kz","call_code":"+7","currency":"KZT","currency_symbol":"₸","timezone":"Asia/Almaty"},
{"name":"Kenya","iso2":"KE","iso3":"KEN","flag":"ke","call_code":"+254","currency":"KES","currency_symbol":"Sh","timezone":"Africa/Nairobi"},
{"name":"Kiribati","iso2":"KI","iso3":"KIR","flag":"ki","call_code":"+686","currency":"AUD","currency_symbol":"$","timezone":"Pacific/Tarawa"},
{"name":"Kuwait","iso2":"KW","iso3":"KWT","flag":"kw","call_code":"+965","currency":"KWD","currency_symbol":"د.ك","timezone":"Asia/Kuwait"},
{"name":"Kyrgyzstan","iso2":"KG","iso3":"KGZ","flag":"kg","call_code":"+996","currency":"KGS","currency_symbol":"с","timezone":"Asia/Bishkek"},
{"name":"Laos","iso2":"LA","iso3":"LAO","flag":"la","call_code":"+856","currency":"LAK","currency_symbol":"₭","timezone":"Asia/Vientiane"},
{"name":"Latvia","iso2":"LV","iso3":"LVA","flag":"lv","call_code":"+371","currency":"EUR","currency_symbol":"€","timezone":"Europe/Riga"},
{"name":"Lebanon","iso2":"LB","iso3":"LBN","flag":"lb","call_code":"+961","currency":"LBP","currency_symbol":"ل.ل","timezone":"Asia/Beirut"},
{"name":"Lesotho","iso2":"LS","iso3":"LSO","flag":"ls","call_code":"+266","currency":"LSL","currency_symbol":"L","timezone":"Africa/Maseru"},
{"name":"Liberia","iso2":"LR","iso3":"LBR","flag":"lr","call_code":"+231","currency":"LRD","currency_symbol":"$","timezone":"Africa/Monrovia"},
{"name":"Libya","iso2":"LY","iso3":"LBY","flag":"ly","call_code":"+218","currency":"LYD","currency_symbol":"ل.د","timezone":"Africa/Tripoli"},
{"name":"Liechtenstein","iso2":"LI","iso3":"LIE","flag":"li","call_code":"+423","currency":"CHF","currency_symbol":"Fr","timezone":"Europe/Vaduz"},
{"name":"Lithuania","iso2":"LT","iso3":"LTU","flag":"lt","call_code":"+370","currency":"EUR","currency_symbol":"€","timezone":"Europe/Vilnius"},
{"name":"Luxembourg","iso2":"LU","iso3":"LUX","flag":"lu","call_code":"+352","currency":"EUR","currency_symbol":"€","timezone":"Europe/Luxembourg"},
{"name":"Madagascar","iso2":"MG","iso3":"MDG","flag":"mg","call_code":"+261","currency":"MGA","currency_symbol":"Ar","timezone":"Indian/Antananarivo"},
{"name":"Malawi","iso2":"MW","iso3":"MWI","flag":"mw","call_code":"+265","currency":"MWK","currency_symbol":"MK","timezone":"Africa/Blantyre"},
{"name":"Malaysia","iso2":"MY","iso3":"MYS","flag":"my","call_code":"+60","currency":"MYR","currency_symbol":"RM","timezone":"Asia/Kuala_Lumpur"},
{"name":"Maldives","iso2":"MV","iso3":"MDV","flag":"mv","call_code":"+960","currency":"MVR","currency_symbol":"Rf","timezone":"Indian/Maldives"},
{"name":"Mali","iso2":"ML","iso3":"MLI","flag":"ml","call_code":"+223","currency":"XOF","currency_symbol":"Fr","timezone":"Africa/Bamako"},
{"name":"Malta","iso2":"MT","iso3":"MLT","flag":"mt","call_code":"+356","currency":"EUR","currency_symbol":"€","timezone":"Europe/Malta"},
{"name":"Marshall Islands","iso2":"MH","iso3":"MHL","flag":"mh","call_code":"+692","currency":"USD","currency_symbol":"$","timezone":"Pacific/Majuro"},
{"name":"Mauritania","iso2":"MR","iso3":"MRT","flag":"mr","call_code":"+222","currency":"MRU","currency_symbol":"UM","timezone":"Africa/Nouakchott"},
{"name":"Mauritius","iso2":"MU","iso3":"MUS","flag":"mu","call_code":"+230","currency":"MUR","currency_symbol":"₨","timezone":"Indian/Mauritius"},
{"name":"Mexico","iso2":"MX","iso3":"MEX","flag":"mx","call_code":"+52","currency":"MXN","currency_symbol":"$","timezone":"America/Mexico_City"},
{"name":"Micronesia","iso2":"FM","iso3":"FSM","flag":"fm","call_code":"+691","currency":"USD","currency_symbol":"$","timezone":"Pacific/Pohnpei"},
{"name":"Moldova","iso2":"MD","iso3":"MDA","flag":"md","call_code":"+373","currency":"MDL","currency_symbol":"L","timezone":"Europe/Chisinau"},
{"name":"Monaco","iso2":"MC","iso3":"MCO","flag":"mc","call_code":"+377","currency":"EUR","currency_symbol":"€","timezone":"Europe/Monaco"},
{"name":"Mongolia","iso2":"MN","iso3":"MNG","flag":"mn","call_code":"+976","currency":"MNT","currency_symbol":"₮","timezone":"Asia/Ulaanbaatar"},
{"name":"Montenegro","iso2":"ME","iso3":"MNE","flag":"me","call_code":"+382","currency":"EUR","currency_symbol":"€","timezone":"Europe/Podgorica"},
{"name":"Morocco","iso2":"MA","iso3":"MAR","flag":"ma","call_code":"+212","currency":"MAD","currency_symbol":"د.م.","timezone":"Africa/Casablanca"},
{"name":"Mozambique","iso2":"MZ","iso3":"MOZ","flag":"mz","call_code":"+258","currency":"MZN","currency_symbol":"MT","timezone":"Africa/Maputo"},
{"name":"Myanmar","iso2":"MM","iso3":"MMR","flag":"mm","call_code":"+95","currency":"MMK","currency_symbol":"K","timezone":"Asia/Yangon"},
{"name":"Namibia","iso2":"NA","iso3":"NAM","flag":"na","call_code":"+264","currency":"NAD","currency_symbol":"$","timezone":"Africa/Windhoek"},
{"name":"Nauru","iso2":"NR","iso3":"NRU","flag":"nr","call_code":"+674","currency":"AUD","currency_symbol":"$","timezone":"Pacific/Nauru"},
{"name":"Nepal","iso2":"NP","iso3":"NPL","flag":"np","call_code":"+977","currency":"NPR","currency_symbol":"₨","timezone":"Asia/Kathmandu"},
{"name":"Netherlands","iso2":"NL","iso3":"NLD","flag":"nl","call_code":"+31","currency":"EUR","currency_symbol":"€","timezone":"Europe/Amsterdam"},
{"name":"New Zealand","iso2":"NZ","iso3":"NZL","flag":"nz","call_code":"+64","currency":"NZD","currency_symbol":"$","timezone":"Pacific/Auckland"},
{"name":"Nicaragua","iso2":"NI","iso3":"NIC","flag":"ni","call_code":"+505","currency":"NIO","currency_symbol":"C$","timezone":"America/Managua"},
{"name":"Niger","iso2":"NE","iso3":"NER","flag":"ne","call_code":"+227","currency":"XOF","currency_symbol":"Fr","timezone":"Africa/Niamey"},
{"name":"Nigeria","iso2":"NG","iso3":"NGA","flag":"ng","call_code":"+234","currency":"NGN","currency_symbol":"₦","timezone":"Africa/Lagos"},
{"name":"North Korea","iso2":"KP","iso3":"PRK","flag":"kp","call_code":"+850","currency":"KPW","currency_symbol":"₩","timezone":"Asia/Pyongyang"},
{"name":"North Macedonia","iso2":"MK","iso3":"MKD","flag":"mk","call_code":"+389","currency":"MKD","currency_symbol":"ден","timezone":"Europe/Skopje"},
{"name":"Norway","iso2":"NO","iso3":"NOR","flag":"no","call_code":"+47","currency":"NOK","currency_symbol":"kr","timezone":"Europe/Oslo"},
{"name":"Oman","iso2":"OM","iso3":"OMN","flag":"om","call_code":"+968","currency":"OMR","currency_symbol":"ر.ع.","timezone":"Asia/Muscat"},
{"name":"Pakistan","iso2":"PK","iso3":"PAK","flag":"pk","call_code":"+92","currency":"PKR","currency_symbol":"₨","timezone":"Asia/Karachi"},
{"name":"Palau","iso2":"PW","iso3":"PLW","flag":"pw","call_code":"+680","currency":"USD","currency_symbol":"$","timezone":"Pacific/Palau"},
{"name":"Palestine","iso2":"PS","iso3":"PSE","flag":"ps","call_code":"+970","currency":"ILS","currency_symbol":"₪","timezone":"Asia/Gaza"},
{"name":"Panama","iso2":"PA","iso3":"PAN","flag":"pa","call_code":"+507","currency":"PAB","currency_symbol":"B/.","timezone":"America/Panama"},
{"name":"Papua New Guinea","iso2":"PG","iso3":"PNG","flag":"pg","call_code":"+675","currency":"PGK","currency_symbol":"K","timezone":"Pacific/Port_Moresby"},
{"name":"Paraguay","iso2":"PY","iso3":"PRY","flag":"py","call_code":"+595","currency":"PYG","currency_symbol":"₲","timezone":"America/Asuncion"},
{"name":"Peru","iso2":"PE","iso3":"PER","flag":"pe","call_code":"+51","currency":"PEN","currency_symbol":"S/","timezone":"America/Lima"},
{"name":"Philippines","iso2":"PH","iso3":"PHL","flag":"ph","call_code":"+63","currency":"PHP","currency_symbol":"₱","timezone":"Asia/Manila"},
{"name":"Poland","iso2":"PL","iso3":"POL","flag":"pl","call_code":"+48","currency":"PLN","currency_symbol":"zł","timezone":"Europe/Warsaw"},
{"name":"Portugal","iso2":"PT","iso3":"PRT","flag":"pt","call_code":"+351","currency":"EUR","currency_symbol":"€","timezone":"Europe/Lisbon"},
{"name":"Qatar","iso2":"QA","iso3":"QAT","flag":"qa","call_code":"+974","currency":"QAR","currency_symbol":"ر.ق","timezone":"Asia/Qatar"},
{"name":"Romania","iso2":"RO","iso3":"ROU","flag":"ro","call_code":"+40","currency":"RON","currency_symbol":"lei","timezone":"Europe/Bucharest"},
{"name":"Russia","iso2":"RU","iso3":"RUS","flag":"ru","call_code":"+7","currency":"RUB","currency_symbol":"₽","timezone":"Europe/Moscow"},
{"name":"Rwanda","iso2":"RW","iso3":"RWA","flag":"rw","call_code":"+250","currency":"RWF","currency_symbol":"Fr","timezone":"Africa/Kigali"},
{"name":"Saint Kitts and Nevis","iso2":"KN","iso3":"KNA","flag":"kn","call_code":"+1-869","currency":"XCD","currency_symbol":"$","timezone":"America/St_Kitts"},
{"name":"Saint Lucia","iso2":"LC","iso3":"LCA","flag":"lc","call_code":"+1-758","currency":"XCD","currency_symbol":"$","timezone":"America/St_Lucia"},
{"name":"Saint Vincent and the Grenadines","iso2":"VC","iso3":"VCT","flag":"vc","call_code":"+1-784","currency":"XCD","currency_symbol":"$","timezone":"America/St_Vincent"},
{"name":"Samoa","iso2":"WS","iso3":"WSM","flag":"ws","call_code":"+685","currency":"WST","currency_symbol":"T","timezone":"Pacific/Apia"},
{"name":"San Marino","iso2":"SM","iso3":"SMR","flag":"sm","call_code":"+378","currency":"EUR","currency_symbol":"€","timezone":"Europe/San_Marino"},
{"name":"Sao Tome and Principe","iso2":"ST","iso3":"STP","flag":"st","call_code":"+239","currency":"STN","currency_symbol":"Db","timezone":"Africa/Sao_Tome"},
{"name":"Saudi Arabia","iso2":"SA","iso3":"SAU","flag":"sa","call_code":"+966","currency":"SAR","currency_symbol":"ر.س","timezone":"Asia/Riyadh"},
{"name":"Senegal","iso2":"SN","iso3":"SEN","flag":"sn","call_code":"+221","currency":"XOF","currency_symbol":"Fr","timezone":"Africa/Dakar"},
{"name":"Serbia","iso2":"RS","iso3":"SRB","flag":"rs","call_code":"+381","currency":"RSD","currency_symbol":"дин","timezone":"Europe/Belgrade"},
{"name":"Seychelles","iso2":"SC","iso3":"SYC","flag":"sc","call_code":"+248","currency":"SCR","currency_symbol":"₨","timezone":"Indian/Mahe"},
{"name":"Sierra Leone","iso2":"SL","iso3":"SLE","flag":"sl","call_code":"+232","currency":"SLL","currency_symbol":"Le","timezone":"Africa/Freetown"},
{"name":"Singapore","iso2":"SG","iso3":"SGP","flag":"sg","call_code":"+65","currency":"SGD","currency_symbol":"$","timezone":"Asia/Singapore"},
{"name":"Slovakia","iso2":"SK","iso3":"SVK","flag":"sk","call_code":"+421","currency":"EUR","currency_symbol":"€","timezone":"Europe/Bratislava"},
{"name":"Slovenia","iso2":"SI","iso3":"SVN","flag":"si","call_code":"+386","currency":"EUR","currency_symbol":"€","timezone":"Europe/Ljubljana"},
{"name":"Solomon Islands","iso2":"SB","iso3":"SLB","flag":"sb","call_code":"+677","currency":"SBD","currency_symbol":"$","timezone":"Pacific/Guadalcanal"},
{"name":"Somalia","iso2":"SO","iso3":"SOM","flag":"so","call_code":"+252","currency":"SOS","currency_symbol":"Sh","timezone":"Africa/Mogadishu"},
{"name":"South Africa","iso2":"ZA","iso3":"ZAF","flag":"za","call_code":"+27","currency":"ZAR","currency_symbol":"R","timezone":"Africa/Johannesburg"},
{"name":"South Korea","iso2":"KR","iso3":"KOR","flag":"kr","call_code":"+82","currency":"KRW","currency_symbol":"₩","timezone":"Asia/Seoul"},
{"name":"South Sudan","iso2":"SS","iso3":"SSD","flag":"ss","call_code":"+211","currency":"SSP","currency_symbol":"£","timezone":"Africa/Juba"},
{"name":"Spain","iso2":"ES","iso3":"ESP","flag":"es","call_code":"+34","currency":"EUR","currency_symbol":"€","timezone":"Europe/Madrid"},
{"name":"Sri Lanka","iso2":"LK","iso3":"LKA","flag":"lk","call_code":"+94","currency":"LKR","currency_symbol":"Rs","timezone":"Asia/Colombo"},
{"name":"Sudan","iso2":"SD","iso3":"SDN","flag":"sd","call_code":"+249","currency":"SDG","currency_symbol":"ج.س.","timezone":"Africa/Khartoum"},
{"name":"Suriname","iso2":"SR","iso3":"SUR","flag":"sr","call_code":"+597","currency":"SRD","currency_symbol":"$","timezone":"America/Paramaribo"},
{"name":"Sweden","iso2":"SE","iso3":"SWE","flag":"se","call_code":"+46","currency":"SEK","currency_symbol":"kr","timezone":"Europe/Stockholm"},
{"name":"Switzerland","iso2":"CH","iso3":"CHE","flag":"ch","call_code":"+41","currency":"CHF","currency_symbol":"Fr","timezone":"Europe/Zurich"},
{"name":"Syria","iso2":"SY","iso3":"SYR","flag":"sy","call_code":"+963","currency":"SYP","currency_symbol":"£","timezone":"Asia/Damascus"},
{"name":"Taiwan","iso2":"TW","iso3":"TWN","flag":"tw","call_code":"+886","currency":"TWD","currency_symbol":"$","timezone":"Asia/Taipei"},
{"name":"Tajikistan","iso2":"TJ","iso3":"TJK","flag":"tj","call_code":"+992","currency":"TJS","currency_symbol":"ЅМ","timezone":"Asia/Dushanbe"},
{"name":"Tanzania","iso2":"TZ","iso3":"TZA","flag":"tz","call_code":"+255","currency":"TZS","currency_symbol":"Sh","timezone":"Africa/Dar_es_Salaam"},
{"name":"Thailand","iso2":"TH","iso3":"THA","flag":"th","call_code":"+66","currency":"THB","currency_symbol":"฿","timezone":"Asia/Bangkok"},
{"name":"Timor-Leste","iso2":"TL","iso3":"TLS","flag":"tl","call_code":"+670","currency":"USD","currency_symbol":"$","timezone":"Asia/Dili"},
{"name":"Togo","iso2":"TG","iso3":"TGO","flag":"tg","call_code":"+228","currency":"XOF","currency_symbol":"Fr","timezone":"Africa/Lome"},
{"name":"Tonga","iso2":"TO","iso3":"TON","flag":"to","call_code":"+676","currency":"TOP","currency_symbol":"T$","timezone":"Pacific/Tongatapu"},
{"name":"Trinidad and Tobago","iso2":"TT","iso3":"TTO","flag":"tt","call_code":"+1-868","currency":"TTD","currency_symbol":"$","timezone":"America/Port_of_Spain"},
{"name":"Tunisia","iso2":"TN","iso3":"TUN","flag":"tn","call_code":"+216","currency":"TND","currency_symbol":"د.ت","timezone":"Africa/Tunis"},
{"name":"Turkey","iso2":"TR","iso3":"TUR","flag":"tr","call_code":"+90","currency":"TRY","currency_symbol":"₺","timezone":"Europe/Istanbul"},
{"name":"Turkmenistan","iso2":"TM","iso3":"TKM","flag":"tm","call_code":"+993","currency":"TMT","currency_symbol":"T","timezone":"Asia/Ashgabat"},
{"name":"Tuvalu","iso2":"TV","iso3":"TUV","flag":"tv","call_code":"+688","currency":"AUD","currency_symbol":"$","timezone":"Pacific/Funafuti"},
{"name":"Uganda","iso2":"UG","iso3":"UGA","flag":"ug","call_code":"+256","currency":"UGX","currency_symbol":"USh","timezone":"Africa/Kampala"},
{"name":"Ukraine","iso2":"UA","iso3":"UKR","flag":"ua","call_code":"+380","currency":"UAH","currency_symbol":"₴","timezone":"Europe/Kiev"},
{"name":"United Arab Emirates","iso2":"AE","iso3":"ARE","flag":"ae","call_code":"+971","currency":"AED","currency_symbol":"د.إ","timezone":"Asia/Dubai"},
{"name":"United Kingdom","iso2":"GB","iso3":"GBR","flag":"gb","call_code":"+44","currency":"GBP","currency_symbol":"£","timezone":"Europe/London"},
{"name":"United States","iso2":"US","iso3":"USA","flag":"us","call_code":"+1","currency":"USD","currency_symbol":"$","timezone":"America/New_York"},
{"name":"Uruguay","iso2":"UY","iso3":"URY","flag":"uy","call_code":"+598","currency":"UYU","currency_symbol":"$U","timezone":"America/Montevideo"},
{"name":"Uzbekistan","iso2":"UZ","iso3":"UZB","flag":"uz","call_code":"+998","currency":"UZS","currency_symbol":"лв","timezone":"Asia/Tashkent"},
{"name":"Vanuatu","iso2":"VU","iso3":"VUT","flag":"vu","call_code":"+678","currency":"VUV","currency_symbol":"VT","timezone":"Pacific/Efate"},
{"name":"Vatican City","iso2":"VA","iso3":"VAT","flag":"va","call_code":"+379","currency":"EUR","currency_symbol":"€","timezone":"Europe/Vatican"},
{"name":"Venezuela","iso2":"VE","iso3":"VEN","flag":"ve","call_code":"+58","currency":"VES","currency_symbol":"Bs","timezone":"America/Caracas"},
{"name":"Vietnam","iso2":"VN","iso3":"VNM","flag":"vn","call_code":"+84","currency":"VND","currency_symbol":"₫","timezone":"Asia/Ho_Chi_Minh"},
{"name":"Yemen","iso2":"YE","iso3":"YEM","flag":"ye","call_code":"+967","currency":"YER","currency_symbol":"﷼","timezone":"Asia/Aden"},
{"name":"Zambia","iso2":"ZM","iso3":"ZMB","flag":"zm","call_code":"+260","currency":"ZMW","currency_symbol":"ZK","timezone":"Africa/Lusaka"},
{"name":"Zimbabwe","iso2":"ZW","iso3":"ZWE","flag":"zw","call_code":"+263","currency":"ZWL","currency_symbol":"Z$","timezone":"Africa/Harare"}
]

BIN
public/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="300" height="67">
<path d="M0 0 C5.45593183 2.73266121 9.7164094 7.50998761 11.95703125 13.19921875 C11.71089666 16.97137443 10.74201883 18.93567305 8.01953125 21.51171875 C7.49488281 22.02347656 6.97023437 22.53523438 6.4296875 23.0625 C4.95703125 24.19921875 4.95703125 24.19921875 2.95703125 24.19921875 C0.91230056 28.28868013 0.69933482 30.79728126 1.95703125 35.19921875 C4.80956396 40.42886206 8.90501248 44.57319845 12.93359375 48.9296875 C15.32327454 51.61000514 16.83572662 53.41940328 17.39453125 57.01171875 C16.83249658 61.10654274 15.17076923 63.6835448 11.95703125 66.19921875 C8.94048196 66.68963934 6.80491964 66.8262952 3.83203125 66.63671875 C3.07140381 66.61295166 2.31077637 66.58918457 1.52709961 66.56469727 C-3.34804387 66.38620586 -8.17237483 65.9105604 -13.015625 65.3359375 C-15.60400972 65.21904271 -17.96572803 65.31277438 -20.5390625 65.53125 C-21.74381226 65.62998413 -21.74381226 65.62998413 -22.97290039 65.73071289 C-24.57209466 65.87362641 -26.16941831 66.03987453 -27.76342773 66.23217773 C-31.95941781 66.60939162 -33.43138686 66.68356446 -36.859375 63.96875 C-37.57996094 63.05480469 -38.30054688 62.14085937 -39.04296875 61.19921875 C-39.97302734 60.19181641 -39.97302734 60.19181641 -40.921875 59.1640625 C-46.35970174 52.61596023 -49.57628741 45.49892875 -50.328125 36.9609375 C-50.23402344 36.04957031 -50.13992187 35.13820312 -50.04296875 34.19921875 C-47.04296875 32.19921875 -47.04296875 32.19921875 -43.04296875 32.19921875 C-42.10657665 28.89976284 -42.10657665 28.89976284 -42.04296875 25.19921875 C-43.67557589 23.82439168 -45.34521937 22.49274208 -47.04296875 21.19921875 C-47.85546875 17.44921875 -47.85546875 17.44921875 -48.04296875 14.19921875 C-33.64449136 3.03189574 -18.54794314 -4.88103767 0 0 Z M-16.04296875 4.19921875 C-14.7096478 8.19918161 -12.54718605 9.03704212 -9.04296875 11.19921875 C-7.04313341 10.40803971 -7.04313341 10.40803971 -5.04296875 9.19921875 C-4.71296875 8.20921875 -4.38296875 7.21921875 -4.04296875 6.19921875 C-8.16354335 4.2013644 -11.4949124 4.0208636 -16.04296875 4.19921875 Z M-0.04296875 11.19921875 C-0.37296875 11.85921875 -0.70296875 12.51921875 -1.04296875 13.19921875 C0.27703125 12.86921875 1.59703125 12.53921875 2.95703125 12.19921875 C1.96703125 11.86921875 0.97703125 11.53921875 -0.04296875 11.19921875 Z M-13.04296875 43.19921875 C-14.03296875 44.18921875 -15.02296875 45.17921875 -16.04296875 46.19921875 C-14.39296875 45.53921875 -12.74296875 44.87921875 -11.04296875 44.19921875 C-11.70296875 43.86921875 -12.36296875 43.53921875 -13.04296875 43.19921875 Z " fill="#EC2123" transform="translate(203.04296875,0.80078125)"/>
<path d="M0 0 C12 0 12 0 15.18359375 2.59765625 C15.97573556 3.72498534 16.72748196 4.88172318 17.4375 6.0625 C17.81841797 6.62388672 18.19933594 7.18527344 18.59179688 7.76367188 C19.75746276 9.48948853 20.87863583 11.24507479 22 13 C23.65 15.31 25.3 17.62 27 20 C27.33 13.4 27.66 6.8 28 0 C31.96 0 35.92 0 40 0 C40 13.86 40 27.72 40 42 C28 42 28 42 24.5625 39.11328125 C23.66831647 37.87427328 22.81388503 36.60567288 22 35.3125 C21.34451172 34.37374023 21.34451172 34.37374023 20.67578125 33.41601562 C19.85637595 32.24104256 19.05305694 31.05452372 18.2734375 29.8527832 C16.64082718 27.47742282 14.80056939 25.25071173 13 23 C12.67 29.27 12.34 35.54 12 42 C8.04 42 4.08 42 0 42 C0 28.14 0 14.28 0 0 Z " fill="#EC2123" transform="translate(227,13)"/>
<path d="M0 0 C4.62 0 9.24 0 14 0 C14.66 2.97 15.32 5.94 16 9 C16.34289062 8.3503125 16.68578125 7.700625 17.0390625 7.03125 C18.83088694 4.17139228 20.36820742 2.13366149 23 0 C28.74645675 -1.04598426 34.44575882 -0.85141373 40 1 C38.47723638 4.39874559 36.47613999 7.05846254 34.125 9.9375 C33.44695312 10.77152344 32.76890625 11.60554688 32.0703125 12.46484375 C31.38710937 13.30144531 30.70390625 14.13804688 30 15 C28.66497731 16.66531377 27.33114831 18.33158766 26 20 C27.712526 24.1487981 30.58883033 27.2187922 33.5 30.5625 C41 39.23498062 41 39.23498062 41 43 C25.77702692 44.06798141 25.77702692 44.06798141 21.640625 41.58203125 C20.15251133 39.78325467 19.08213 38.06359675 18 36 C17.01 34.68 16.02 33.36 15 32 C14.67 35.63 14.34 39.26 14 43 C9.38 43 4.76 43 0 43 C0 28.81 0 14.62 0 0 Z M4 4 C4 15.55 4 27.1 4 39 C5.98 39 7.96 39 10 39 C10.33 33.72 10.66 28.44 11 23 C15.57095617 25.7425737 17.50609734 27.50607658 20.6875 31.5625 C23.70042676 35.40910814 23.70042676 35.40910814 27 39 C30.08347826 39.16681109 30.08347826 39.16681109 33 39 C30.21719091 34.00359278 26.5871221 30.04048245 22.765625 25.81640625 C21 23 21 23 20.953125 19.9765625 C22.209062 16.40557745 23.85848299 14.50835201 26.5 11.8125 C29.96406721 8.34867996 29.96406721 8.34867996 32 4 C29.17352243 3.60794956 29.17352243 3.60794956 26 4 C23.68959938 5.98066962 23.68959938 5.98066962 21.875 8.6875 C20.91207031 9.99267578 20.91207031 9.99267578 19.9296875 11.32421875 C19.29289062 12.20722656 18.65609375 13.09023438 18 14 C14.38600874 18.87133042 14.38600874 18.87133042 11 20 C10.67 14.72 10.34 9.44 10 4 C8.02 4 6.04 4 4 4 Z " fill="#EC2123" transform="translate(76,13)"/>
<path d="M0 0 C0 3.96 0 7.92 0 12 C-3.63 12 -7.26 12 -11 12 C-11 13.32 -11 14.64 -11 16 C-7.37 16 -3.74 16 0 16 C0 19.63 0 23.26 0 27 C-3.63 27 -7.26 27 -11 27 C-11 28.32 -11 29.64 -11 31 C-7.37 31 -3.74 31 0 31 C0 34.63 0 38.26 0 42 C-3.64588169 42.02682987 -7.29156846 42.04675569 -10.9375 42.0625 C-12.49887695 42.07506836 -12.49887695 42.07506836 -14.09179688 42.08789062 C-15.08115234 42.09111328 -16.07050781 42.09433594 -17.08984375 42.09765625 C-18.46450806 42.10551147 -18.46450806 42.10551147 -19.86694336 42.11352539 C-22 42 -22 42 -23 41 C-23.08868299 38.04294679 -23.11520296 35.10934944 -23.09765625 32.15234375 C-23.0962413 31.2659169 -23.09482635 30.37949005 -23.09336853 29.46620178 C-23.08775215 26.62326746 -23.07519691 23.78041069 -23.0625 20.9375 C-23.05748711 19.01497509 -23.05292384 17.09244896 -23.04882812 15.16992188 C-23.03777741 10.44658451 -23.02050183 5.72330542 -23 1 C-20.47932687 0.83054298 -17.95850616 0.66436516 -15.4375 0.5 C-14.73302734 0.45230469 -14.02855469 0.40460937 -13.30273438 0.35546875 C-8.85739865 0.0686729 -4.45455341 -0.04682842 0 0 Z " fill="#EC2123" transform="translate(300,13)"/>
<path d="M0 0 C8.25 0 16.5 0 25 0 C25 4.29 25 8.58 25 13 C21.04 13 17.08 13 13 13 C13 13.66 13 14.32 13 15 C16.96 15 20.92 15 25 15 C25 19.29 25 23.58 25 28 C21.04 28 17.08 28 13 28 C13 28.66 13 29.32 13 30 C16.96 30 20.92 30 25 30 C25 34.29 25 38.58 25 43 C16.75 43 8.5 43 0 43 C0 28.81 0 14.62 0 0 Z M3 4 C3 15.55 3 27.1 3 39 C9.27 39 15.54 39 22 39 C22 37.68 22 36.36 22 35 C17.71 35 13.42 35 9 35 C9.33 31.37 9.66 27.74 10 24 C13.3 24 16.6 24 20 24 C20 22.35 20 20.7 20 19 C16.37 19 12.74 19 9 19 C9 15.7 9 12.4 9 9 C13.29 9 17.58 9 22 9 C21.67 7.35 21.34 5.7 21 4 C15.06 4 9.12 4 3 4 Z " fill="#EC2123" transform="translate(121,13)"/>
<path d="M0 0 C1.39456452 0.00667256 2.78911118 0.01863093 4.18359375 0.03515625 C4.89451172 0.03966797 5.60542969 0.04417969 6.33789062 0.04882812 C8.09899388 0.06064761 9.86005411 0.07858362 11.62109375 0.09765625 C13.56554254 5.18976506 15.50652676 10.28315284 17.44140625 15.37890625 C18.77397527 18.88554346 20.12047397 22.38587397 21.48828125 25.87890625 C21.76591309 26.59626953 22.04354492 27.31363281 22.32958984 28.05273438 C22.84414435 29.38213246 23.36497185 30.70912543 23.89306641 32.03320312 C25.3253906 35.76306767 25.62109375 37.94874109 25.62109375 42.09765625 C21.00109375 42.09765625 16.38109375 42.09765625 11.62109375 42.09765625 C10.63109375 40.11765625 9.64109375 38.13765625 8.62109375 36.09765625 C4.99109375 36.09765625 1.36109375 36.09765625 -2.37890625 36.09765625 C-3.03890625 38.07765625 -3.69890625 40.05765625 -4.37890625 42.09765625 C-9.32890625 42.09765625 -14.27890625 42.09765625 -19.37890625 42.09765625 C-17.09308723 33.92145745 -14.16515576 26.05287551 -11.08056641 18.15356445 C-10.2440408 16.00987495 -9.41462034 13.86352875 -8.5859375 11.71679688 C-4.08332998 0.11801532 -4.08332998 0.11801532 0 0 Z M-0.37890625 3.09765625 C-2.07265409 7.43809933 -3.76323791 11.77975639 -5.45092773 16.12255859 C-6.02525875 17.59913204 -6.60044624 19.07537266 -7.17651367 20.55126953 C-8.00535977 22.67523236 -8.83109778 24.80038109 -9.65625 26.92578125 C-9.91357407 27.58322327 -10.17089813 28.24066528 -10.4360199 28.91802979 C-11.60865333 31.9468104 -12.70274242 34.91653315 -13.37890625 38.09765625 C-11.39890625 38.09765625 -9.41890625 38.09765625 -7.37890625 38.09765625 C-6.71890625 36.11765625 -6.05890625 34.13765625 -5.37890625 32.09765625 C0.23109375 32.09765625 5.84109375 32.09765625 11.62109375 32.09765625 C12.61109375 34.07765625 13.60109375 36.05765625 14.62109375 38.09765625 C16.60109375 38.09765625 18.58109375 38.09765625 20.62109375 38.09765625 C16.5805666 26.30227257 12.38762902 14.6211481 7.62109375 3.09765625 C4.98109375 3.09765625 2.34109375 3.09765625 -0.37890625 3.09765625 Z " fill="#EC2123" transform="translate(46.37890625,13.90234375)"/>
<path d="M0 0 C9.9 0 19.8 0 30 0 C29.67 4.29 29.34 8.58 29 13 C26.36 13 23.72 13 21 13 C21 22.9 21 32.8 21 43 C16.38 43 11.76 43 7 43 C7 33.1 7 23.2 7 13 C4.69 13 2.38 13 0 13 C0 8.71 0 4.42 0 0 Z M3 4 C3 5.65 3 7.3 3 9 C5.64 9 8.28 9 11 9 C11 18.9 11 28.8 11 39 C12.98 39 14.96 39 17 39 C17 29.1 17 19.2 17 9 C19.64 9 22.28 9 25 9 C25 7.35 25 5.7 25 4 C17.74 4 10.48 4 3 4 Z " fill="#EC2123" transform="translate(0,13)"/>
<path d="M0 0 C2.71161391 1.00284894 3.78469211 1.54608598 5.046875 4.20703125 C5.27890625 5.06683594 5.5109375 5.92664063 5.75 6.8125 C6.11351562 8.09060547 6.11351562 8.09060547 6.484375 9.39453125 C7 12 7 12 7 17 C2.71 17 -1.58 17 -6 17 C-4.67594074 10.93139507 -2.67149708 5.60236281 0 0 Z M0 10 C0 10.99 0 11.98 0 13 C0.66 13 1.32 13 2 13 C1.67 12.01 1.34 11.02 1 10 C0.67 10 0.34 10 0 10 Z " fill="#EC2123" transform="translate(49,25)"/>
</svg>

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

@ -0,0 +1,52 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center align-items-center" style="min-height: 80vh;">
<div class="col-md-6 col-lg-4">
<div class="card shadow">
<div class="card-body p-4">
<div class="text-center mb-4">
<h3 class="fw-bold">Forgot Password?</h3>
<p class="text-muted">No problem. Just let us know your email address and we'll email you a password reset link.</p>
</div>
<form method="POST" action="{{ route('password.email') }}">
@csrf
<!-- Email Address -->
<div class="mb-3">
<label for="email" class="form-label">Email Address</label>
<input id="email" type="email"
class="form-control @error('email') is-invalid @enderror"
name="email"
value="{{ old('email') }}"
required autocomplete="email"
autofocus>
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
<!-- Send Password Reset Link Button -->
<div class="d-grid mb-3">
<button type="submit" class="btn btn-primary">
Send Password Reset Link
</button>
</div>
<!-- Back to Login -->
<div class="text-center">
<a href="{{ route('login') }}" class="text-decoration-none">
Back to Login
</a>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@ -0,0 +1,76 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center align-items-center" style="min-height: 80vh;">
<div class="col-md-6 col-lg-4">
<div class="card shadow">
<div class="card-body p-4">
<div class="text-center mb-4">
<h3 class="fw-bold">Welcome Back</h3>
<p class="text-muted">Sign in to your account</p>
</div>
<form method="POST" action="{{ route('login') }}">
@csrf
<!-- Email Address -->
<div class="mb-3">
<label for="email" class="form-label">Email Address</label>
<input id="email" type="email"
class="form-control @error('email') is-invalid @enderror"
name="email"
value="{{ old('email') }}"
required autocomplete="email"
autofocus>
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
<!-- Password -->
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input id="password" type="password"
class="form-control @error('password') is-invalid @enderror"
name="password"
required autocomplete="current-password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
<!-- Remember Me -->
<div class="mb-3 form-check">
<input class="form-check-input" type="checkbox" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }}>
<label class="form-check-label" for="remember">
Remember Me
</label>
</div>
<!-- Login Button -->
<div class="d-grid mb-3">
<button type="submit" class="btn btn-primary">
Login
</button>
</div>
<!-- Forgot Password Link -->
<div class="text-center">
@if (Route::has('password.request'))
<a class="text-decoration-none" href="{{ route('password.request') }}">
Forgot Password?
</a>
@endif
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@ -0,0 +1,236 @@
@extends('layouts.app')
@section('content')
<style>
.register-container {
min-height: calc(100vh - 72px);
display: flex;
align-items: center;
justify-content: center;
padding: 2rem 0;
}
.flatpickr-input {
width: 100%;
padding: 0.375rem 0.75rem;
font-size: 1rem;
line-height: 1.5;
color: #212529;
background-color: #fff;
border: 1px solid #ced4da;
border-radius: 0.375rem;
}
.flatpickr-input:focus {
border-color: #86b7fe;
outline: 0;
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
}
</style>
<!-- Flag Icons CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flag-icons@6.6.6/css/flag-icons.min.css">
<!-- Select2 CSS (for nationality dropdown) -->
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
<!-- Flatpickr CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
<div class="container register-container">
<div class="row justify-content-center">
<div class="col-lg-10">
<div class="card shadow">
<div class="card-header bg-white py-3">
<h3 class="text-center mb-0 fw-bold">Create Your Profile</h3>
</div>
<div class="card-body p-4">
<form method="POST" action="{{ route('register') }}" id="registrationForm">
@csrf
<div class="row">
<!-- Left Column -->
<div class="col-md-6">
<!-- Full Name -->
<div class="mb-3">
<label for="full_name" class="form-label">Full Name</label>
<input id="full_name" type="text"
class="form-control @error('full_name') is-invalid @enderror"
name="full_name"
value="{{ old('full_name') }}"
required autocomplete="name">
@error('full_name')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
<!-- Email Address -->
<div class="mb-3">
<label for="email" class="form-label">Email Address</label>
<input id="email" type="email"
class="form-control @error('email') is-invalid @enderror"
name="email"
value="{{ old('email') }}"
required autocomplete="email">
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
<!-- Password -->
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input id="password" type="password"
class="form-control @error('password') is-invalid @enderror"
name="password"
required autocomplete="new-password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
<!-- Confirm Password -->
<div class="mb-3">
<label for="password-confirm" class="form-label">Confirm Password</label>
<input id="password-confirm" type="password"
class="form-control"
name="password_confirmation"
required autocomplete="new-password">
</div>
</div>
<!-- Right Column -->
<div class="col-md-6">
<!-- Mobile Number with Country Code -->
<div class="mb-3">
<label for="mobile_number" class="form-label">Mobile Number</label>
<x-country-code-dropdown
name="country_code"
id="country_code"
:value="old('country_code', '+1')"
:required="true"
:error="$errors->first('country_code')">
<input id="mobile_number" type="tel"
class="form-control @error('mobile_number') is-invalid @enderror"
name="mobile_number"
value="{{ old('mobile_number') }}"
required autocomplete="tel"
placeholder="Phone number">
</x-country-code-dropdown>
@error('mobile_number')
<span class="invalid-feedback d-block" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
<!-- Gender -->
<div class="mb-3">
<label for="gender" class="form-label">Gender</label>
<select id="gender" class="form-select @error('gender') is-invalid @enderror"
name="gender" required>
<option value="">Select Gender</option>
<option value="m" {{ old('gender') == 'm' ? 'selected' : '' }}>Male</option>
<option value="f" {{ old('gender') == 'f' ? 'selected' : '' }}>Female</option>
</select>
@error('gender')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
<!-- Birthdate -->
<div class="mb-3">
<label for="birthdate" class="form-label">Birthdate</label>
<input id="birthdate" type="text"
class="flatpickr-input @error('birthdate') is-invalid @enderror"
name="birthdate"
value="{{ old('birthdate') }}"
required
placeholder="Select birthdate"
readonly>
@error('birthdate')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
<!-- Nationality -->
<div class="mb-3">
<x-country-dropdown
name="nationality"
id="nationality"
:value="old('nationality')"
:required="true"
:error="$errors->first('nationality')" />
</div>
</div>
</div>
<!-- Register Button -->
<div class="d-grid mt-4">
<button type="submit" class="btn btn-primary btn-lg" id="registerButton">
Create Account
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- jQuery (required for Select2) -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<!-- Select2 JS -->
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
<!-- Flatpickr JS -->
<script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
@stack('styles')
@stack('scripts')
<script>
document.addEventListener('DOMContentLoaded', function() {
// Initialize Flatpickr for birthdate
flatpickr('#birthdate', {
dateFormat: 'Y-m-d',
maxDate: 'today',
yearRange: [1900, new Date().getFullYear()],
disableMobile: true,
showMonths: 1,
clickOpens: true,
onReady: function(selectedDates, dateStr, instance) {
const calendar = instance.calendarContainer;
calendar.style.fontSize = '14px';
}
});
// Form submission handler
$('#registrationForm').on('submit', function(e) {
console.log('Form submitting...');
console.log('Country code:', $('#country_code').val());
console.log('Nationality:', $('#nationality').val());
console.log('Mobile number:', $('#mobile_number').val());
});
// Error handler
window.onerror = function(message, source, lineno, colno, error) {
console.error('JavaScript Error:', message);
console.error('Source:', source);
console.error('Line:', lineno);
console.error('Error:', error);
};
});
</script>
@endsection

View File

@ -0,0 +1,78 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center align-items-center" style="min-height: 80vh;">
<div class="col-md-6 col-lg-4">
<div class="card shadow">
<div class="card-body p-4">
<div class="text-center mb-4">
<h3 class="fw-bold">Reset Password</h3>
<p class="text-muted">Enter your new password below.</p>
</div>
<form method="POST" action="{{ route('password.update') }}">
@csrf
<!-- Token -->
<input type="hidden" name="token" value="{{ $request->token }}">
<!-- Email Address -->
<div class="mb-3">
<label for="email" class="form-label">Email Address</label>
<input id="email" type="email"
class="form-control @error('email') is-invalid @enderror"
name="email"
value="{{ $email ?? old('email') }}"
required autocomplete="email"
autofocus>
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
<!-- Password -->
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input id="password" type="password"
class="form-control @error('password') is-invalid @enderror"
name="password"
required autocomplete="new-password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
<!-- Confirm Password -->
<div class="mb-3">
<label for="password-confirm" class="form-label">Confirm Password</label>
<input id="password-confirm" type="password"
class="form-control"
name="password_confirmation"
required autocomplete="new-password">
</div>
<!-- Reset Password Button -->
<div class="d-grid mb-3">
<button type="submit" class="btn btn-primary">
Reset Password
</button>
</div>
<!-- Back to Login -->
<div class="text-center">
<a href="{{ route('login') }}" class="text-decoration-none">
Back to Login
</a>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@ -0,0 +1,331 @@
# Reusable Form Components
This directory contains reusable Blade components for forms across the application.
## Available Components
### 1. Country Dropdown (`<x-country-dropdown />`)
A dropdown with country flags that stores the ISO3 country code.
**Features:**
- Flag icons for each country
- Loads data from `/data/countries.json`
- Preserves selected value on validation errors
- Displays validation errors
- Clean, modern UI with flag icons
**Usage:**
```blade
<x-country-dropdown
name="country"
id="country"
:value="old('country')"
:required="true"
:error="$errors->first('country')" />
```
**Props:**
- `name` (default: 'country') - The name attribute for the select
- `id` (default: 'country') - The ID for the select element
- `value` (default: '') - The initial/selected value (ISO3 code)
- `required` (default: false) - Whether the field is required
- `error` (default: null) - Error message to display
**Example in a form:**
```blade
<div class="mb-3">
<label for="country" class="form-label">Country</label>
<x-country-dropdown
name="country"
id="country"
:value="old('country', 'USA')"
:required="true"
:error="$errors->first('country')" />
</div>
```
---
### 2. Call Code Dropdown (`<x-call-code-dropdown />`)
A dropdown with country flags and call codes that stores the call code.
**Features:**
- Flag icons for each country
- Displays country name and call code
- Loads data from `/data/countries.json`
- Preserves selected value on validation errors
- Displays validation errors
**Usage:**
```blade
<x-call-code-dropdown
name="country_code"
id="country_code"
:value="old('country_code', '+971')"
:required="true"
:error="$errors->first('country_code')" />
```
**Props:**
- `name` (default: 'call_code') - The name attribute for the select
- `id` (default: 'call_code') - The ID for the select element
- `value` (default: '') - The initial/selected value (call code)
- `required` (default: false) - Whether the field is required
- `error` (default: null) - Error message to display
**Example in a form:**
```blade
<div class="mb-3">
<label for="country_code" class="form-label">Country Code</label>
<x-call-code-dropdown
name="country_code"
id="country_code"
:value="old('country_code', '+971')"
:required="true"
:error="$errors->first('country_code')" />
</div>
```
---
### 3. Currency Dropdown (`<x-currency-dropdown />`)
A dropdown with country flags and currency symbols that stores the currency code.
**Features:**
- Flag icons for each country
- Displays country name, currency code, and currency symbol
- Loads data from `/data/countries.json`
- Preserves selected value on validation errors
- Displays validation errors
**Usage:**
```blade
<x-currency-dropdown
name="currency"
id="currency"
:value="old('currency', 'USD')"
:required="true"
:error="$errors->first('currency')" />
```
**Props:**
- `name` (default: 'currency') - The name attribute for the select
- `id` (default: 'currency') - The ID for the select element
- `value` (default: '') - The initial/selected value (currency code)
- `required` (default: false) - Whether the field is required
- `error` (default: null) - Error message to display
**Example in a form:**
```blade
<div class="mb-3">
<label for="currency" class="form-label">Currency</label>
<x-currency-dropdown
name="currency"
id="currency"
:value="old('currency', 'USD')"
:required="true"
:error="$errors->first('currency')" />
</div>
```
---
### 4. Timezone Dropdown (`<x-timezone-dropdown />`)
A dropdown with country flags and timezones that stores the timezone.
**Features:**
- Flag icons for each country
- Displays country name and timezone
- Loads data from `/data/countries.json`
- Preserves selected value on validation errors
- Displays validation errors
**Usage:**
```blade
<x-timezone-dropdown
name="timezone"
id="timezone"
:value="old('timezone', 'Asia/Dubai')"
:required="true"
:error="$errors->first('timezone')" />
```
**Props:**
- `name` (default: 'timezone') - The name attribute for the select
- `id` (default: 'timezone') - The ID for the select element
- `value` (default: '') - The initial/selected value (timezone)
- `required` (default: false) - Whether the field is required
- `error` (default: null) - Error message to display
**Example in a form:**
```blade
<div class="mb-3">
<label for="timezone" class="form-label">Timezone</label>
<x-timezone-dropdown
name="timezone"
id="timezone"
:value="old('timezone', 'Asia/Dubai')"
:required="true"
:error="$errors->first('timezone')" />
</div>
```
---
## Dependencies
All components require:
### Flag Icons
```html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flag-icons@6.6.6/css/flag-icons.min.css">
```
### Countries Data File
All components load country data from `/public/data/countries.json` which contains:
- Country name
- ISO2 code (2-letter)
- ISO3 code (3-letter)
- Flag code (for flag-icons)
- Call code (phone country code)
- Currency code
- Currency symbol
- Timezone
**Note:** Israel is excluded from the countries list as requested.
---
## Complete Example Form
Here's a complete example of a form using all components:
```blade
@extends('layouts.app')
@section('content')
<!-- Flag Icons CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flag-icons@6.6.6/css/flag-icons.min.css">
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">
<h4>User Information</h4>
</div>
<div class="card-body">
<form method="POST" action="#">
@csrf
<!-- Country Dropdown -->
<x-country-dropdown
name="country"
id="country"
:value="old('country', 'USA')"
:required="true"
:error="$errors->first('country')" />
<!-- Call Code Dropdown -->
<x-call-code-dropdown
name="country_code"
id="country_code"
:value="old('country_code', '+971')"
:required="true"
:error="$errors->first('country_code')" />
<!-- Currency Dropdown -->
<x-currency-dropdown
name="currency"
id="currency"
:value="old('currency', 'USD')"
:required="true"
:error="$errors->first('currency')" />
<!-- Timezone Dropdown -->
<x-timezone-dropdown
name="timezone"
id="timezone"
:value="old('timezone', 'Asia/Dubai')"
:required="true"
:error="$errors->first('timezone')" />
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- Load component scripts -->
@stack('scripts')
@endsection
```
---
## Notes
1. **Data Loading**: All components use JavaScript's `fetch()` API to load country data from `/data/countries.json`. This ensures the data is loaded asynchronously and doesn't block page rendering.
2. **Value Preservation**: All components support Laravel's `old()` helper to preserve selected values after validation errors.
3. **Validation**: All components integrate with Laravel's validation system and display error messages when validation fails.
4. **Flag Icons**: All components use the flag-icons library (v6.6.6) for displaying country flags.
5. **Multiple Instances**: You can use multiple instances of any component on the same page. Each instance will work independently.
6. **Custom Styling**: Each component includes custom CSS for flag positioning and sizing.
7. **Unique Values**:
- Currency dropdown shows unique currencies (countries with same currency are grouped)
- Timezone dropdown shows unique timezones (countries with same timezone are grouped)
8. **Israel Excluded**: As requested, Israel is not included in the countries.json file.
---
## Component Comparison
| Component | Displays | Stores | Example Value |
|-----------|----------|--------|---------------|
| Country Dropdown | Flag + Country Name | ISO3 Code | USA, GBR, ARE |
| Call Code Dropdown | Flag + Name + Call Code | Call Code | +1, +44, +971 |
| Currency Dropdown | Flag + Name + Currency | Currency Code | USD, EUR, AED |
| Timezone Dropdown | Flag + Name + Timezone | Timezone | America/New_York, Europe/London, Asia/Dubai |
---
## Troubleshooting
**Flags not displaying:**
- Ensure flag-icons CSS is loaded in your layout or view
- Check browser console for any CSS loading errors
**Dropdown not populating:**
- Check that `/public/data/countries.json` exists and is accessible
- Check browser console for any fetch errors
- Ensure the file path is correct
**Values not preserving after validation:**
- Ensure you're passing the correct value to the `:value` prop
- Use `old('field_name')` to get the previous value
- Check that the field name matches your validation rules
**Multiple instances conflicting:**
- Each component uses a unique ID based on the `id` prop
- Ensure each instance has a unique `id` prop value

View File

@ -0,0 +1,79 @@
@props(['name' => 'call_code', 'id' => 'call_code', 'value' => '', 'required' => false, 'error' => null])
<div class="mb-3">
<label for="{{ $id }}" class="form-label">Country Code</label>
<select id="{{ $id }}"
class="form-select call-code-select @error($name) is-invalid @enderror"
name="{{ $name }}"
{{ $required ? 'required' : '' }}>
<option value="">Select Country Code</option>
</select>
@if($error)
<span class="invalid-feedback" role="alert">
<strong>{{ $error }}</strong>
</span>
@endif
</div>
@once
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function() {
// Load countries from JSON file
fetch('/data/countries.json')
.then(response => response.json())
.then(countries => {
const selectElement = document.getElementById('{{ $id }}');
if (!selectElement) return;
// Populate dropdown
countries.forEach(country => {
const option = document.createElement('option');
option.value = country.call_code;
option.textContent = `${country.name} (${country.call_code})`;
option.setAttribute('data-flag', country.flag);
selectElement.appendChild(option);
});
// Set initial value if provided
const initialValue = '{{ $value }}';
if (initialValue) {
selectElement.value = initialValue;
}
// Initialize Select2 for searchable dropdown
if (typeof $ !== 'undefined' && $.fn.select2) {
$(selectElement).select2({
templateResult: function(state) {
if (!state.id) {
return state.text;
}
const option = $(state.element);
const flagCode = option.data('flag');
return $(`<span><span class="fi fi-${flagCode} me-2"></span>${state.text}</span>`);
},
templateSelection: function(state) {
if (!state.id) {
return state.text;
}
const option = $(state.element);
const flagCode = option.data('flag');
return $(`<span><span class="fi fi-${flagCode} me-2"></span>${state.text}</span>`);
},
width: '100%'
});
}
})
.catch(error => console.error('Error loading countries:', error));
});
</script>
@endpush
@push('styles')
<style>
.call-code-select {
background-size: 20px 15px;
}
</style>
@endpush
@endonce

View File

@ -0,0 +1,191 @@
@props(['name' => 'country_code', 'id' => 'country_code', 'value' => '+1', 'required' => false, 'error' => null])
<div class="input-group">
<button class="btn btn-outline-secondary dropdown-toggle country-dropdown-btn d-flex align-items-center"
type="button"
id="{{ $id }}Dropdown"
data-bs-toggle="dropdown"
aria-expanded="false">
<span class="fi fi-us me-2" id="{{ $id }}SelectedFlag"></span>
<span class="country-label" id="{{ $id }}SelectedCountry">{{ $value }}</span>
</button>
<div class="dropdown-menu p-2" aria-labelledby="{{ $id }}Dropdown" style="min-width: 300px;">
<input type="text"
class="form-control form-control-sm mb-2"
placeholder="Search country..."
id="{{ $id }}Search">
<div class="country-list" id="{{ $id }}List" style="max-height: 300px; overflow-y: auto;">
<!-- Countries will be populated by JavaScript -->
</div>
</div>
<input type="hidden" id="{{ $id }}" name="{{ $name }}" value="{{ $value }}" {{ $required ? 'required' : '' }}>
{{ $slot }}
</div>
@if($error)
<span class="invalid-feedback d-block" role="alert">
<strong>{{ $error }}</strong>
</span>
@endif
@once
@push('styles')
<style>
.country-dropdown-btn {
min-width: 150px;
display: flex;
align-items: center;
justify-content: space-between;
}
.country-list {
max-height: 300px;
overflow-y: auto;
}
.dropdown-item {
cursor: pointer;
}
.dropdown-item:hover {
background-color: #f8f9fa;
}
</style>
@endpush
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function() {
// Country data with flags and codes
const countries = [
{ code: '+1', name: 'United States', flagCode: 'us' },
{ code: '+1', name: 'Canada', flagCode: 'ca' },
{ code: '+44', name: 'United Kingdom', flagCode: 'gb' },
{ code: '+971', name: 'United Arab Emirates', flagCode: 'ae' },
{ code: '+966', name: 'Saudi Arabia', flagCode: 'sa' },
{ code: '+974', name: 'Qatar', flagCode: 'qa' },
{ code: '+965', name: 'Kuwait', flagCode: 'kw' },
{ code: '+973', name: 'Bahrain', flagCode: 'bh' },
{ code: '+968', name: 'Oman', flagCode: 'om' },
{ code: '+20', name: 'Egypt', flagCode: 'eg' },
{ code: '+91', name: 'India', flagCode: 'in' },
{ code: '+92', name: 'Pakistan', flagCode: 'pk' },
{ code: '+880', name: 'Bangladesh', flagCode: 'bd' },
{ code: '+60', name: 'Malaysia', flagCode: 'my' },
{ code: '+65', name: 'Singapore', flagCode: 'sg' },
{ code: '+81', name: 'Japan', flagCode: 'jp' },
{ code: '+86', name: 'China', flagCode: 'cn' },
{ code: '+82', name: 'South Korea', flagCode: 'kr' },
{ code: '+61', name: 'Australia', flagCode: 'au' },
{ code: '+49', name: 'Germany', flagCode: 'de' },
{ code: '+33', name: 'France', flagCode: 'fr' },
{ code: '+39', name: 'Italy', flagCode: 'it' },
{ code: '+34', name: 'Spain', flagCode: 'es' },
{ code: '+31', name: 'Netherlands', flagCode: 'nl' },
{ code: '+46', name: 'Sweden', flagCode: 'se' },
{ code: '+47', name: 'Norway', flagCode: 'no' },
{ code: '+45', name: 'Denmark', flagCode: 'dk' },
{ code: '+358', name: 'Finland', flagCode: 'fi' },
{ code: '+41', name: 'Switzerland', flagCode: 'ch' },
{ code: '+43', name: 'Austria', flagCode: 'at' },
{ code: '+48', name: 'Poland', flagCode: 'pl' },
{ code: '+420', name: 'Czech Republic', flagCode: 'cz' },
{ code: '+36', name: 'Hungary', flagCode: 'hu' },
{ code: '+40', name: 'Romania', flagCode: 'ro' },
{ code: '+30', name: 'Greece', flagCode: 'gr' },
{ code: '+90', name: 'Turkey', flagCode: 'tr' },
{ code: '+7', name: 'Russia', flagCode: 'ru' },
{ code: '+55', name: 'Brazil', flagCode: 'br' },
{ code: '+52', name: 'Mexico', flagCode: 'mx' },
{ code: '+54', name: 'Argentina', flagCode: 'ar' },
{ code: '+56', name: 'Chile', flagCode: 'cl' },
{ code: '+57', name: 'Colombia', flagCode: 'co' },
{ code: '+27', name: 'South Africa', flagCode: 'za' },
{ code: '+234', name: 'Nigeria', flagCode: 'ng' },
{ code: '+254', name: 'Kenya', flagCode: 'ke' },
{ code: '+94', name: 'Sri Lanka', flagCode: 'lk' },
{ code: '+84', name: 'Vietnam', flagCode: 'vn' },
{ code: '+66', name: 'Thailand', flagCode: 'th' },
{ code: '+62', name: 'Indonesia', flagCode: 'id' },
{ code: '+63', name: 'Philippines', flagCode: 'ph' },
{ code: '+64', name: 'New Zealand', flagCode: 'nz' },
{ code: '+351', name: 'Portugal', flagCode: 'pt' },
{ code: '+353', name: 'Ireland', flagCode: 'ie' },
{ code: '+972', name: 'Israel', flagCode: 'il' },
{ code: '+962', name: 'Jordan', flagCode: 'jo' },
{ code: '+961', name: 'Lebanon', flagCode: 'lb' },
{ code: '+964', name: 'Iraq', flagCode: 'iq' },
];
// Initialize all country code dropdowns on the page
document.querySelectorAll('[id$="List"]').forEach(function(listElement) {
const componentId = listElement.id.replace('List', '');
initializeCountryDropdown(componentId, countries);
});
function initializeCountryDropdown(componentId, countries) {
const countryList = document.getElementById(componentId + 'List');
if (!countryList) return;
// Populate country dropdown
countries.forEach(country => {
const button = document.createElement('button');
button.className = 'dropdown-item d-flex align-items-center';
button.type = 'button';
button.setAttribute('data-country-code', country.code);
button.setAttribute('data-country-name', country.name);
button.setAttribute('data-flag-code', country.flagCode);
button.innerHTML = `
<span class="fi fi-${country.flagCode} me-2"></span>
<span>${country.name} (${country.code})</span>
`;
button.addEventListener('click', function() {
selectCountry(componentId, country.code, country.name, country.flagCode);
});
countryList.appendChild(button);
});
// Search functionality
const searchInput = document.getElementById(componentId + 'Search');
if (searchInput) {
searchInput.addEventListener('input', function(e) {
const searchTerm = e.target.value.toLowerCase();
const items = countryList.querySelectorAll('.dropdown-item');
items.forEach(item => {
const text = item.textContent.toLowerCase();
if (text.includes(searchTerm)) {
item.style.display = '';
} else {
item.style.display = 'none';
}
});
});
}
// Set initial value if provided
const hiddenInput = document.getElementById(componentId);
if (hiddenInput && hiddenInput.value) {
const initialCountry = countries.find(c => c.code === hiddenInput.value);
if (initialCountry) {
selectCountry(componentId, initialCountry.code, initialCountry.name, initialCountry.flagCode);
}
}
}
function selectCountry(componentId, code, name, flagCode) {
const flagElement = document.getElementById(componentId + 'SelectedFlag');
const countryElement = document.getElementById(componentId + 'SelectedCountry');
const hiddenInput = document.getElementById(componentId);
if (flagElement) flagElement.className = `fi fi-${flagCode} me-2`;
if (countryElement) countryElement.textContent = code;
if (hiddenInput) hiddenInput.value = code;
}
});
</script>
@endpush
@endonce

View File

@ -0,0 +1,80 @@
@props(['name' => 'country', 'id' => 'country', 'value' => '', 'required' => false, 'error' => null, 'label' => 'Nationality'])
<div class="mb-3">
<label for="{{ $id }}" class="form-label">{{ $label }}</label>
<select id="{{ $id }}"
class="form-select country-select @error($name) is-invalid @enderror"
name="{{ $name }}"
{{ $required ? 'required' : '' }}
style="width: 100%;">
<option value="">Select {{ $label }}</option>
</select>
@if($error)
<span class="invalid-feedback" role="alert">
<strong>{{ $error }}</strong>
</span>
@endif
</div>
@once
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function() {
// Load countries from JSON file
fetch('/data/countries.json')
.then(response => response.json())
.then(countries => {
const selectElement = document.getElementById('{{ $id }}');
if (!selectElement) return;
// Populate dropdown
countries.forEach(country => {
const option = document.createElement('option');
option.value = country.iso3;
option.textContent = country.name;
option.setAttribute('data-flag', country.flag);
selectElement.appendChild(option);
});
// Set initial value if provided
const initialValue = '{{ $value }}';
if (initialValue) {
selectElement.value = initialValue;
}
// Initialize Select2 for searchable dropdown
if (typeof $ !== 'undefined' && $.fn.select2) {
$(selectElement).select2({
templateResult: function(state) {
if (!state.id) {
return state.text;
}
const option = $(state.element);
const flagCode = option.data('flag');
return $(`<span><span class="fi fi-${flagCode} me-2"></span>${state.text}</span>`);
},
templateSelection: function(state) {
if (!state.id) {
return state.text;
}
const option = $(state.element);
const flagCode = option.data('flag');
return $(`<span><span class="fi fi-${flagCode} me-2"></span>${state.text}</span>`);
},
width: '100%'
});
}
})
.catch(error => console.error('Error loading countries:', error));
});
</script>
@endpush
@push('styles')
<style>
.country-select {
background-size: 20px 15px;
}
</style>
@endpush
@endonce

View File

@ -0,0 +1,93 @@
@props(['name' => 'currency', 'id' => 'currency', 'value' => '', 'required' => false, 'error' => null])
<div class="mb-3">
<label for="{{ $id }}" class="form-label">Currency</label>
<select id="{{ $id }}"
class="form-select currency-select @error($name) is-invalid @enderror"
name="{{ $name }}"
{{ $required ? 'required' : '' }}>
<option value="">Select Currency</option>
</select>
@if($error)
<span class="invalid-feedback" role="alert">
<strong>{{ $error }}</strong>
</span>
@endif
</div>
@once
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function() {
// Load countries from JSON file
fetch('/data/countries.json')
.then(response => response.json())
.then(countries => {
const selectElement = document.getElementById('{{ $id }}');
if (!selectElement) return;
// Get unique currencies
const uniqueCurrencies = {};
countries.forEach(country => {
if (!uniqueCurrencies[country.currency]) {
uniqueCurrencies[country.currency] = {
currency: country.currency,
currency_symbol: country.currency_symbol,
flag: country.flag,
name: country.name
};
}
}
});
// Populate dropdown
Object.values(uniqueCurrencies).forEach(currencyData => {
const option = document.createElement('option');
option.value = currencyData.currency;
option.textContent = `${currencyData.name} (${currencyData.currency} ${currencyData.currency_symbol})`;
option.setAttribute('data-flag', currencyData.flag);
selectElement.appendChild(option);
});
// Set initial value if provided
const initialValue = '{{ $value }}';
if (initialValue) {
selectElement.value = initialValue;
}
// Initialize Select2 for searchable dropdown
if (typeof $ !== 'undefined' && $.fn.select2) {
$(selectElement).select2({
templateResult: function(state) {
if (!state.id) {
return state.text;
}
const option = $(state.element);
const flagCode = option.data('flag');
return $(`<span><span class="fi fi-${flagCode} me-2"></span>${state.text}</span>`);
},
templateSelection: function(state) {
if (!state.id) {
return state.text;
}
const option = $(state.element);
const flagCode = option.data('flag');
return $(`<span><span class="fi fi-${flagCode} me-2"></span>${state.text}</span>`);
},
width: '100%'
});
}
})
.catch(error => console.error('Error loading countries:', error));
});
</script>
@endpush
@push('styles')
<style>
.currency-select {
background-size: 20px 15px;
}
</style>
@endpush
@endonce

View File

@ -0,0 +1,115 @@
@props(['name' => 'nationality', 'id' => 'nationality', 'value' => '', 'required' => false, 'error' => null])
<select id="{{ $id }}"
class="form-select nationality-select @error($name) is-invalid @enderror"
name="{{ $name }}"
{{ $required ? 'required' : '' }}>
<option value="">Select Nationality</option>
</select>
@if($error)
<span class="invalid-feedback" role="alert">
<strong>{{ $error }}</strong>
</span>
@endif
@once
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function() {
// Country data for nationality
const countries = [
{ name: 'United States', flagCode: 'us' },
{ name: 'Canada', flagCode: 'ca' },
{ name: 'United Kingdom', flagCode: 'gb' },
{ name: 'United Arab Emirates', flagCode: 'ae' },
{ name: 'Saudi Arabia', flagCode: 'sa' },
{ name: 'Qatar', flagCode: 'qa' },
{ name: 'Kuwait', flagCode: 'kw' },
{ name: 'Bahrain', flagCode: 'bh' },
{ name: 'Oman', flagCode: 'om' },
{ name: 'Egypt', flagCode: 'eg' },
{ name: 'India', flagCode: 'in' },
{ name: 'Pakistan', flagCode: 'pk' },
{ name: 'Bangladesh', flagCode: 'bd' },
{ name: 'Malaysia', flagCode: 'my' },
{ name: 'Singapore', flagCode: 'sg' },
{ name: 'Japan', flagCode: 'jp' },
{ name: 'China', flagCode: 'cn' },
{ name: 'South Korea', flagCode: 'kr' },
{ name: 'Australia', flagCode: 'au' },
{ name: 'Germany', flagCode: 'de' },
{ name: 'France', flagCode: 'fr' },
{ name: 'Italy', flagCode: 'it' },
{ name: 'Spain', flagCode: 'es' },
{ name: 'Netherlands', flagCode: 'nl' },
{ name: 'Sweden', flagCode: 'se' },
{ name: 'Norway', flagCode: 'no' },
{ name: 'Denmark', flagCode: 'dk' },
{ name: 'Finland', flagCode: 'fi' },
{ name: 'Switzerland', flagCode: 'ch' },
{ name: 'Austria', flagCode: 'at' },
{ name: 'Poland', flagCode: 'pl' },
{ name: 'Czech Republic', flagCode: 'cz' },
{ name: 'Hungary', flagCode: 'hu' },
{ name: 'Romania', flagCode: 'ro' },
{ name: 'Greece', flagCode: 'gr' },
{ name: 'Turkey', flagCode: 'tr' },
{ name: 'Russia', flagCode: 'ru' },
{ name: 'Brazil', flagCode: 'br' },
{ name: 'Mexico', flagCode: 'mx' },
{ name: 'Argentina', flagCode: 'ar' },
{ name: 'Chile', flagCode: 'cl' },
{ name: 'Colombia', flagCode: 'co' },
{ name: 'South Africa', flagCode: 'za' },
{ name: 'Nigeria', flagCode: 'ng' },
{ name: 'Kenya', flagCode: 'ke' },
{ name: 'Sri Lanka', flagCode: 'lk' },
{ name: 'Vietnam', flagCode: 'vn' },
{ name: 'Thailand', flagCode: 'th' },
{ name: 'Indonesia', flagCode: 'id' },
{ name: 'Philippines', flagCode: 'ph' },
{ name: 'New Zealand', flagCode: 'nz' },
{ name: 'Portugal', flagCode: 'pt' },
{ name: 'Ireland', flagCode: 'ie' },
{ name: 'Israel', flagCode: 'il' },
{ name: 'Jordan', flagCode: 'jo' },
{ name: 'Lebanon', flagCode: 'lb' },
{ name: 'Iraq', flagCode: 'iq' },
];
// Initialize all nationality dropdowns on the page
document.querySelectorAll('.nationality-select').forEach(function(selectElement) {
if (typeof $ !== 'undefined' && $.fn.select2) {
const $select = $(selectElement);
$select.select2({
data: countries.map(country => ({
id: country.name,
text: country.name,
flagCode: country.flagCode
})),
templateResult: function(data) {
if (!data.id) return data.text;
return $(`<span><span class="fi fi-${data.flagCode} me-2"></span> ${data.text}</span>`);
},
templateSelection: function(data) {
if (!data.id) return data.text;
return $(`<span><span class="fi fi-${data.flagCode} me-2"></span> ${data.text}</span>`);
},
placeholder: 'Select Nationality',
allowClear: true,
width: '100%'
});
// Restore value if provided
const initialValue = selectElement.getAttribute('data-value') || '{{ $value }}';
if (initialValue) {
$select.val(initialValue).trigger('change');
}
}
});
});
</script>
@endpush
@endonce

View File

@ -0,0 +1,92 @@
@props(['name' => 'timezone', 'id' => 'timezone', 'value' => '', 'required' => false, 'error' => null])
<div class="mb-3">
<label for="{{ $id }}" class="form-label">Timezone</label>
<select id="{{ $id }}"
class="form-select timezone-select @error($name) is-invalid @enderror"
name="{{ $name }}"
{{ $required ? 'required' : '' }}>
<option value="">Select Timezone</option>
</select>
@if($error)
<span class="invalid-feedback" role="alert">
<strong>{{ $error }}</strong>
</span>
@endif
</div>
@once
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function() {
// Load countries from JSON file
fetch('/data/countries.json')
.then(response => response.json())
.then(countries => {
const selectElement = document.getElementById('{{ $id }}');
if (!selectElement) return;
// Get unique timezones
const uniqueTimezones = {};
countries.forEach(country => {
if (!uniqueTimezones[country.timezone]) {
uniqueTimezones[country.timezone] = {
timezone: country.timezone,
flag: country.flag,
name: country.name
};
}
}
});
// Populate dropdown
Object.values(uniqueTimezones).forEach(timezoneData => {
const option = document.createElement('option');
option.value = timezoneData.timezone;
option.textContent = `${timezoneData.name} (${timezoneData.timezone})`;
option.setAttribute('data-flag', timezoneData.flag);
selectElement.appendChild(option);
});
// Set initial value if provided
const initialValue = '{{ $value }}';
if (initialValue) {
selectElement.value = initialValue;
}
// Initialize Select2 for searchable dropdown
if (typeof $ !== 'undefined' && $.fn.select2) {
$(selectElement).select2({
templateResult: function(state) {
if (!state.id) {
return state.text;
}
const option = $(state.element);
const flagCode = option.data('flag');
return $(`<span><span class="fi fi-${flagCode} me-2"></span>${state.text}</span>`);
},
templateSelection: function(state) {
if (!state.id) {
return state.text;
}
const option = $(state.element);
const flagCode = option.data('flag');
return $(`<span><span class="fi fi-${flagCode} me-2"></span>${state.text}</span>`);
},
width: '100%'
});
}
})
.catch(error => console.error('Error loading countries:', error));
});
</script>
@endpush
@push('styles')
<style>
.timezone-select {
background-size: 20px 15px;
}
</style>
@endpush
@endonce

View File

@ -0,0 +1,105 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Reset Password</title>
<style>
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background-color: #f8f9fa;
margin: 0;
padding: 20px;
}
.container {
max-width: 600px;
margin: 0 auto;
background-color: #ffffff;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.header {
background-color: #0d6efd;
padding: 30px;
text-align: center;
}
.header h1 {
color: #ffffff;
margin: 0;
font-size: 24px;
font-weight: 600;
}
.content {
padding: 40px 30px;
}
.content h2 {
color: #333333;
font-size: 20px;
margin-top: 0;
margin-bottom: 20px;
}
.content p {
color: #666666;
line-height: 1.6;
margin-bottom: 20px;
}
.button-container {
text-align: center;
margin: 30px 0;
}
.button {
display: inline-block;
background-color: #0d6efd;
color: #ffffff;
text-decoration: none;
padding: 12px 30px;
border-radius: 5px;
font-weight: 500;
font-size: 16px;
border: none;
cursor: pointer;
}
.button:hover {
background-color: #0b5ed7;
}
.footer {
background-color: #f8f9fa;
padding: 20px 30px;
text-align: center;
border-top: 1px solid #e9ecef;
}
.footer p {
color: #999999;
font-size: 12px;
margin: 0;
}
.footer a {
color: #0d6efd;
text-decoration: none;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>{{ config('app.name', 'Club SaaS') }}</h1>
</div>
<div class="content">
<h2>Reset Your Password</h2>
<p>Hello,</p>
<p>You are receiving this email because we received a password reset request for your account.</p>
<p>This password reset link will expire in 60 minutes.</p>
<div class="button-container">
<a href="{{ $url }}" class="button">Reset Password</a>
</div>
<p>If you did not request a password reset, no further action is required.</p>
</div>
<div class="footer">
<p>&copy; {{ date('Y') }} {{ config('app.name', 'Club SaaS') }}. All rights reserved.</p>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,171 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Welcome to {{ config('app.name', 'Club SaaS') }}</title>
<style>
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background-color: #f8f9fa;
margin: 0;
padding: 20px;
}
.container {
max-width: 600px;
margin: 0 auto;
background-color: #ffffff;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.header {
padding: 40px 30px;
text-align: center;
background: linear-gradient(135deg, {{ $user->gender == 'm' ? '#0d6efd 0%, #0a58ca 100%' : '#d63384 0%, #a61e4d 100%' }});
}
.header h1 {
color: #ffffff;
margin: 0;
font-size: 28px;
font-weight: 600;
}
.header p {
color: rgba(255, 255, 255, 0.9);
margin: 10px 0 0 0;
font-size: 16px;
}
.content {
padding: 40px 30px;
}
.welcome-section {
text-align: center;
margin-bottom: 30px;
}
.welcome-section h2 {
color: {{ $user->gender == 'm' ? '#0d6efd' : '#d63384' }};
font-size: 24px;
margin-top: 0;
margin-bottom: 10px;
}
.welcome-section p {
color: #666666;
line-height: 1.6;
margin-bottom: 20px;
}
.info-box {
background-color: {{ $user->gender == 'm' ? 'rgba(13, 110, 253, 0.05)' : 'rgba(214, 51, 132, 0.05)' }};
border-left: 4px solid {{ $user->gender == 'm' ? '#0d6efd' : '#d63384' }};
padding: 20px;
margin: 20px 0;
border-radius: 5px;
}
.info-box h3 {
color: {{ $user->gender == 'm' ? '#0d6efd' : '#d63384' }};
font-size: 18px;
margin-top: 0;
margin-bottom: 15px;
}
.info-box p {
color: #666666;
line-height: 1.6;
margin: 0 0 10px 0;
}
.info-box strong {
color: #333333;
}
.button-container {
text-align: center;
margin: 30px 0;
}
.button {
display: inline-block;
background-color: {{ $user->gender == 'm' ? '#0d6efd' : '#d63384' }};
color: #ffffff;
text-decoration: none;
padding: 14px 40px;
border-radius: 5px;
font-weight: 500;
font-size: 16px;
border: none;
cursor: pointer;
}
.button:hover {
background-color: {{ $user->gender == 'm' ? '#0b5ed7' : '#b02a5c' }};
}
.footer {
background-color: #f8f9fa;
padding: 20px 30px;
text-align: center;
border-top: 1px solid #e9ecef;
}
.footer p {
color: #999999;
font-size: 12px;
margin: 0;
}
.footer a {
color: {{ $user->gender == 'm' ? '#0d6efd' : '#d63384' }};
text-decoration: none;
}
.divider {
height: 1px;
background-color: #e9ecef;
margin: 30px 0;
}
.greeting {
font-size: 18px;
color: #333333;
margin-bottom: 20px;
}
.greeting strong {
color: {{ $user->gender == 'm' ? '#0d6efd' : '#d63384' }};
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>{{ config('app.name', 'Club SaaS') }}</h1>
<p>Welcome to the Family</p>
</div>
<div class="content">
<div class="welcome-section">
<p class="greeting">Dear <strong>{{ $user->full_name }}</strong>,</p>
<h2>Welcome to the Family!</h2>
<p>We are thrilled to have you join our community. Your account has been successfully created and you are now part of our family.</p>
</div>
@if($guardian && $relationship)
<div class="info-box">
<h3>Your Family Information</h3>
<p><strong>Guardian:</strong> {{ $guardian->full_name }}</p>
<p><strong>Relationship:</strong> {{ ucfirst($relationship->relationship_type) }}</p>
@if($user->birthdate)
<p><strong>Birthdate:</strong> {{ \Carbon\Carbon::parse($user->birthdate)->format('F j, Y') }}</p>
@endif
</div>
@endif
<div class="divider"></div>
<p>We're excited to have you with us. If you have any questions or need assistance, please don't hesitate to reach out to your guardian or our support team.</p>
<div class="button-container">
<a href="{{ url('/login') }}" class="button">Access Your Account</a>
</div>
<p style="text-align: center; color: #999999; font-size: 14px; margin-top: 30px;">
If you have any questions, feel free to contact us at any time.
</p>
</div>
<div class="footer">
<p>&copy; {{ date('Y') }} {{ config('app.name', 'Club SaaS') }}. All rights reserved.</p>
<p style="margin-top: 10px;">
<a href="{{ url('/') }}">Visit Website</a> |
<a href="mailto:{{ config('mail.from.address') }}">Contact Support</a>
</p>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,161 @@
@extends('layouts.app')
@section('content')
<!-- Flag Icons CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flag-icons@6.6.6/css/flag-icons.min.css">
<!-- Select2 CSS -->
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card shadow">
<div class="card-header bg-white">
<h3 class="mb-0">Example Form with Reusable Components</h3>
</div>
<div class="card-body p-4">
<form method="POST" action="#">
@csrf
<div class="row">
<div class="col-md-6">
<!-- Example 1: Mobile Number with Country Code -->
<div class="mb-3">
<label for="mobile_number" class="form-label">Mobile Number</label>
<x-country-code-dropdown
name="country_code"
id="country_code"
:value="old('country_code', '+971')"
:required="true"
:error="$errors->first('country_code')">
<input id="mobile_number" type="tel"
class="form-control @error('mobile_number') is-invalid @enderror"
name="mobile_number"
value="{{ old('mobile_number') }}"
required
placeholder="Phone number">
</x-country-code-dropdown>
@error('mobile_number')
<span class="invalid-feedback d-block" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
<!-- Example 2: Emergency Contact with Country Code -->
<div class="mb-3">
<label for="emergency_number" class="form-label">Emergency Contact</label>
<x-country-code-dropdown
name="emergency_country_code"
id="emergency_country_code"
:value="old('emergency_country_code', '+971')"
:required="false"
:error="$errors->first('emergency_country_code')">
<input id="emergency_number" type="tel"
class="form-control"
name="emergency_number"
value="{{ old('emergency_number') }}"
placeholder="Emergency phone number">
</x-country-code-dropdown>
</div>
</div>
<div class="col-md-6">
<!-- Example 3: Nationality Dropdown -->
<div class="mb-3">
<label for="nationality" class="form-label">Nationality</label>
<x-nationality-dropdown
name="nationality"
id="nationality"
:value="old('nationality')"
:required="true"
:error="$errors->first('nationality')" />
</div>
<!-- Example 4: Second Nationality (Optional) -->
<div class="mb-3">
<label for="second_nationality" class="form-label">Second Nationality (Optional)</label>
<x-nationality-dropdown
name="second_nationality"
id="second_nationality"
:value="old('second_nationality')"
:required="false"
:error="$errors->first('second_nationality')" />
</div>
</div>
</div>
<div class="alert alert-info mt-3">
<h5>Component Features:</h5>
<ul class="mb-0">
<li><strong>Country Code Dropdown:</strong> Bootstrap-based with flag icons and search</li>
<li><strong>Nationality Dropdown:</strong> Select2-based with flag icons and search</li>
<li>Both components preserve values on validation errors</li>
<li>Both components support required/optional fields</li>
<li>Both components display validation errors</li>
<li>Multiple instances can be used on the same page</li>
</ul>
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary btn-lg">
Submit Form
</button>
</div>
</form>
</div>
</div>
<!-- Usage Instructions -->
<div class="card shadow mt-4">
<div class="card-header bg-white">
<h4 class="mb-0">How to Use These Components</h4>
</div>
<div class="card-body">
<h5>1. Country Code Dropdown</h5>
<pre class="bg-light p-3 rounded"><code>&lt;x-country-code-dropdown
name="country_code"
id="country_code"
:value="old('country_code', '+971')"
:required="true"
:error="$errors->first('country_code')"&gt;
&lt;input type="tel" class="form-control" name="mobile_number" placeholder="Phone number"&gt;
&lt;/x-country-code-dropdown&gt;</code></pre>
<h5 class="mt-4">2. Nationality Dropdown</h5>
<pre class="bg-light p-3 rounded"><code>&lt;x-nationality-dropdown
name="nationality"
id="nationality"
:value="old('nationality')"
:required="true"
:error="$errors->first('nationality')" /&gt;</code></pre>
<h5 class="mt-4">3. Required Dependencies</h5>
<pre class="bg-light p-3 rounded"><code>&lt;!-- CSS --&gt;
&lt;link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flag-icons@6.6.6/css/flag-icons.min.css"&gt;
&lt;link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" /&gt;
&lt;!-- JS --&gt;
&lt;script src="https://code.jquery.com/jquery-3.6.0.min.js"&gt;&lt;/script&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"&gt;&lt;/script&gt;
&lt;!-- Component Scripts --&gt;
@stack('styles')
@stack('scripts')</code></pre>
</div>
</div>
</div>
</div>
</div>
<!-- jQuery (required for Select2) -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<!-- Select2 JS -->
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
<!-- Load component scripts -->
@stack('styles')
@stack('scripts')
@endsection

View File

@ -33,8 +33,8 @@
<label for="gender" class="form-label">Gender</label>
<select class="form-select @error('gender') is-invalid @enderror" id="gender" name="gender" required>
<option value="">Select Gender</option>
<option value="male" {{ old('gender') == 'male' ? 'selected' : '' }}>Male</option>
<option value="female" {{ old('gender') == 'female' ? 'selected' : '' }}>Female</option>
<option value="m" {{ old('gender') == 'm' ? 'selected' : '' }}>Male</option>
<option value="f" {{ old('gender') == 'f' ? 'selected' : '' }}>Female</option>
</select>
@error('gender')
<div class="invalid-feedback">{{ $message }}</div>
@ -51,18 +51,30 @@
<div class="row mb-3">
<div class="col-md-6">
<label for="blood_type" class="form-label">Blood Type <span class="text-muted">(Optional)</span></label>
<input type="text" class="form-control @error('blood_type') is-invalid @enderror" id="blood_type" name="blood_type" value="{{ old('blood_type') }}">
<label for="blood_type" class="form-label">Blood Type</label>
<select class="form-select @error('blood_type') is-invalid @enderror" id="blood_type" name="blood_type">
<option value="">Select Blood Type</option>
<option value="A+" {{ old('blood_type') == 'A+' ? 'selected' : '' }}>A+</option>
<option value="A-" {{ old('blood_type') == 'A-' ? 'selected' : '' }}>A-</option>
<option value="B+" {{ old('blood_type') == 'B+' ? 'selected' : '' }}>B+</option>
<option value="B-" {{ old('blood_type') == 'B-' ? 'selected' : '' }}>B-</option>
<option value="AB+" {{ old('blood_type') == 'AB+' ? 'selected' : '' }}>AB+</option>
<option value="AB-" {{ old('blood_type') == 'AB-' ? 'selected' : '' }}>AB-</option>
<option value="O+" {{ old('blood_type') == 'O+' ? 'selected' : '' }}>O+</option>
<option value="O-" {{ old('blood_type') == 'O-' ? 'selected' : '' }}>O-</option>
<option value="Unknown" {{ old('blood_type') == 'Unknown' ? 'selected' : '' }}>Unknown</option>
</select>
@error('blood_type')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="col-md-6">
<label for="nationality" class="form-label">Nationality</label>
<input type="text" class="form-control @error('nationality') is-invalid @enderror" id="nationality" name="nationality" value="{{ old('nationality') }}" required>
@error('nationality')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
<x-country-dropdown
name="nationality"
id="nationality"
:value="old('nationality')"
:required="true"
:error="$errors->first('nationality')" />
</div>
</div>

View File

@ -12,63 +12,265 @@
</div>
<!-- Family Members Card Grid -->
<div class="row row-cols-1 row-cols-md-3 g-4 mb-5">
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-4 g-4 mb-5">
<!-- Current User Card -->
<div class="col">
<div class="card h-100 shadow-sm">
<div class="card-body text-center">
<div class="mb-3">
<img src="{{ $user->media_gallery[0] ?? 'https://via.placeholder.com/150' }}"
class="rounded-circle" alt="{{ $user->full_name }}"
width="100" height="100">
<a href="{{ route('profile.show') }}" class="text-decoration-none">
<div class="card h-100 shadow-sm border-0 overflow-hidden d-flex flex-column family-card">
<!-- Header with gradient background -->
<div class="p-4 pb-3" style="background: linear-gradient(135deg, {{ $user->gender == 'm' ? 'rgba(13, 110, 253, 0.1) 0%, rgba(13, 110, 253, 0.05) 50%' : 'rgba(214, 51, 132, 0.1) 0%, rgba(214, 51, 132, 0.05) 50%' }}, transparent 100%);">
<div class="d-flex align-items-start gap-3">
<div class="position-relative">
<div class="rounded-circle border border-4 border-white shadow" style="width: 80px; height: 80px; overflow: hidden; box-shadow: 0 0 0 2px {{ $user->gender == 'm' ? 'rgba(13, 110, 253, 0.3)' : 'rgba(214, 51, 132, 0.3)' }} !important;">
@if($user->media_gallery[0] ?? false)
<img src="{{ $user->media_gallery[0] }}" alt="{{ $user->full_name }}" class="w-100 h-100" style="object-fit: cover;">
@else
<div class="w-100 h-100 d-flex align-items-center justify-center text-white fw-bold fs-4" style="background: linear-gradient(135deg, {{ $user->gender == 'm' ? '#0d6efd 0%, #0a58ca 100%' : '#d63384 0%, #a61e4d 100%' }});">
{{ strtoupper(substr($user->full_name, 0, 1)) }}
</div>
@endif
</div>
</div>
<div class="flex-grow-1 min-w-0">
<h5 class="fw-bold mb-2 text-truncate">{{ $user->full_name }}</h5>
<div class="d-flex flex-wrap gap-2">
<span class="badge {{ $user->gender == 'm' ? 'bg-primary' : 'bg-danger' }}">Adult</span>
<span class="badge bg-success">Active</span>
</div>
</div>
</div>
<h5 class="card-title">{{ $user->full_name }}</h5>
<p class="card-text text-muted">
Age: {{ $user->age }} ({{ $user->life_stage }})
</p>
<p class="card-text">
<small class="text-muted">{{ $user->horoscope }}</small>
</p>
<a href="{{ route('profile.edit') }}" class="btn btn-primary">
<i class="bi bi-pencil"></i> Edit Profile
</a>
</div>
</div>
<!-- Contact Info -->
<div class="px-4 py-3 bg-light border-top border-bottom">
@if($user->mobile)
<div class="d-flex align-items-center gap-2 small mb-2">
<i class="bi bi-telephone-fill {{ $user->gender == 'm' ? 'text-primary' : 'text-danger' }}"></i>
<span class="fw-medium">{{ $user->mobile }}</span>
</div>
@endif
@if($user->email)
<div class="d-flex align-items-center gap-2 small">
<i class="bi bi-envelope-fill {{ $user->gender == 'm' ? 'text-primary' : 'text-danger' }}"></i>
<span class="fw-medium text-truncate">{{ $user->email }}</span>
</div>
@endif
</div>
<!-- Details -->
<div class="px-4 py-3 flex-grow-1">
<div class="row g-3 mb-3">
<div class="col-6">
<div class="small text-muted text-uppercase fw-medium mb-1" style="font-size: 0.7rem; letter-spacing: 0.5px;">Gender</div>
<div class="fw-semibold text-capitalize">{{ $user->gender == 'm' ? 'Male' : 'Female' }}</div>
</div>
<div class="col-6">
<div class="small text-muted text-uppercase fw-medium mb-1" style="font-size: 0.7rem; letter-spacing: 0.5px;">Age</div>
<div class="fw-semibold">{{ $user->age }} years</div>
</div>
</div>
<div class="row g-3 mb-3">
<div class="col-6">
<div class="small text-muted text-uppercase fw-medium mb-1" style="font-size: 0.7rem; letter-spacing: 0.5px;">Nationality</div>
<div class="fw-semibold fs-5 nationality-display" data-iso3="{{ $user->nationality }}">{{ $user->nationality }}</div>
</div>
<div class="col-6">
<div class="small text-muted text-uppercase fw-medium mb-1" style="font-size: 0.7rem; letter-spacing: 0.5px;">Horoscope</div>
<div class="fw-semibold">
@php
$horoscopeSymbols = [
'Aries' => '♈',
'Taurus' => '♉',
'Gemini' => '♊',
'Cancer' => '♋',
'Leo' => '♌',
'Virgo' => '♍',
'Libra' => '♎',
'Scorpio' => '♏',
'Sagittarius' => '♐',
'Capricorn' => '♑',
'Aquarius' => '♒',
'Pisces' => '♓'
];
$horoscope = $user->horoscope ?? 'N/A';
$symbol = $horoscopeSymbols[$horoscope] ?? '';
@endphp
{{ $symbol }} {{ $horoscope }}
</div>
</div>
</div>
<div class="pt-2 border-top">
<div class="d-flex justify-content-between align-items-center small mb-2">
<span class="text-muted fw-medium">Next Birthday</span>
<span class="fw-semibold {{ $user->gender == 'm' ? 'text-primary' : 'text-danger' }}">
{{ $user->birthdate->copy()->year(now()->year)->isFuture()
? $user->birthdate->copy()->year(now()->year)->diffForHumans(['parts' => 2, 'syntax' => \Carbon\CarbonInterface::DIFF_ABSOLUTE])
: $user->birthdate->copy()->year(now()->year + 1)->diffForHumans(['parts' => 2, 'syntax' => \Carbon\CarbonInterface::DIFF_ABSOLUTE]) }}
</span>
</div>
<div class="d-flex justify-content-between align-items-center small">
<span class="text-muted fw-medium">Member Since</span>
<span class="fw-semibold">{{ $user->created_at->format('d/m/Y') }}</span>
</div>
</div>
</div>
</div>
</a>
</div>
<!-- Dependents Cards -->
@foreach($dependents as $relationship)
<div class="col">
<div class="card h-100 shadow-sm">
<div class="card-body text-center">
<div class="mb-3">
<img src="{{ $relationship->dependent->media_gallery[0] ?? 'https://via.placeholder.com/150' }}"
class="rounded-circle" alt="{{ $relationship->dependent->full_name }}"
width="100" height="100">
<a href="{{ route('family.show', $relationship->dependent->id) }}" class="text-decoration-none">
<div class="card h-100 shadow-sm border-0 overflow-hidden d-flex flex-column family-card">
<!-- Header with gradient background -->
<div class="p-4 pb-3" style="background: linear-gradient(135deg, {{ $relationship->dependent->gender == 'm' ? 'rgba(13, 110, 253, 0.1) 0%, rgba(13, 110, 253, 0.05) 50%' : 'rgba(214, 51, 132, 0.1) 0%, rgba(214, 51, 132, 0.05) 50%' }}, transparent 100%);">
<div class="d-flex align-items-start gap-3">
<div class="position-relative">
<div class="rounded-circle border border-4 border-white shadow" style="width: 80px; height: 80px; overflow: hidden; box-shadow: 0 0 0 2px {{ $relationship->dependent->gender == 'm' ? 'rgba(13, 110, 253, 0.3)' : 'rgba(214, 51, 132, 0.3)' }} !important;">
@if($relationship->dependent->media_gallery[0] ?? false)
<img src="{{ $relationship->dependent->media_gallery[0] }}" alt="{{ $relationship->dependent->full_name }}" class="w-100 h-100" style="object-fit: cover;">
@else
<div class="w-100 h-100 d-flex align-items-center justify-center text-white fw-bold fs-4" style="background: linear-gradient(135deg, {{ $relationship->dependent->gender == 'm' ? '#0d6efd 0%, #0a58ca 100%' : '#d63384 0%, #a61e4d 100%' }});">
{{ strtoupper(substr($relationship->dependent->full_name, 0, 1)) }}
</div>
@endif
</div>
</div>
<div class="flex-grow-1 min-w-0">
<h5 class="fw-bold mb-2 text-truncate">{{ $relationship->dependent->full_name }}</h5>
<div class="d-flex flex-wrap gap-2">
@php
$age = $relationship->dependent->age;
$ageGroup = 'Adult';
if ($age < 2) {
$ageGroup = 'Infant';
} elseif ($age < 4) {
$ageGroup = 'Toddler';
} elseif ($age < 6) {
$ageGroup = 'Preschooler';
} elseif ($age < 13) {
$ageGroup = 'Child';
} elseif ($age < 20) {
$ageGroup = 'Teenager';
} elseif ($age < 40) {
$ageGroup = 'Young Adult';
} elseif ($age < 60) {
$ageGroup = 'Adult';
} else {
$ageGroup = 'Senior';
}
@endphp
<span class="badge {{ $relationship->dependent->gender == 'm' ? 'bg-primary' : 'bg-danger' }}">{{ $ageGroup }}</span>
<span class="badge bg-success">Active</span>
</div>
</div>
</div>
<h5 class="card-title">{{ $relationship->dependent->full_name }}</h5>
<p class="card-text text-muted">
Age: {{ $relationship->dependent->age }} ({{ $relationship->dependent->life_stage }})
</p>
<span class="badge bg-secondary">{{ ucfirst($relationship->relationship_type) }}</span>
</div>
<div class="card-footer bg-transparent border-top-0 text-center">
<div class="btn-group" role="group">
<a href="{{ route('family.edit', $relationship->dependent->id) }}" class="btn btn-sm btn-outline-secondary">
<i class="bi bi-pencil"></i> Edit
</a>
<a href="{{ route('family.show', $relationship->dependent->id) }}" class="btn btn-sm btn-outline-primary">
<i class="bi bi-eye"></i> View
</a>
<!-- Sponsor/Guardian Info -->
<div class="px-4 py-2 {{ $relationship->dependent->gender == 'm' ? 'bg-primary' : 'bg-danger' }} bg-opacity-10 border-bottom">
<div class="d-flex align-items-center gap-2 small">
<i class="bi bi-person-badge {{ $relationship->dependent->gender == 'm' ? 'text-primary' : 'text-danger' }}"></i>
<span class="fw-medium {{ $relationship->dependent->gender == 'm' ? 'text-primary' : 'text-danger' }}">
{{ ucfirst($relationship->relationship_type) }}: {{ $user->full_name }}
</span>
</div>
</div>
<!-- Contact Info -->
<div class="px-4 py-3 bg-light border-bottom">
@if($relationship->dependent->mobile)
<div class="d-flex align-items-center gap-2 small mb-2">
<i class="bi bi-telephone-fill {{ $relationship->dependent->gender == 'm' ? 'text-primary' : 'text-danger' }}"></i>
<span class="fw-medium">{{ $relationship->dependent->mobile }}</span>
</div>
@elseif($user->mobile)
<div class="d-flex align-items-center gap-2 small mb-2">
<i class="bi bi-telephone-fill {{ $relationship->dependent->gender == 'm' ? 'text-primary' : 'text-danger' }}"></i>
<span class="fw-medium">{{ $user->mobile }}</span>
<span class="badge {{ $relationship->dependent->gender == 'm' ? 'bg-info' : 'bg-danger' }} {{ $relationship->dependent->gender == 'm' ? 'text-dark' : 'text-white' }} ms-auto">Guardian's</span>
</div>
@endif
@if($relationship->dependent->email)
<div class="d-flex align-items-center gap-2 small">
<i class="bi bi-envelope-fill {{ $relationship->dependent->gender == 'm' ? 'text-primary' : 'text-danger' }}"></i>
<span class="fw-medium text-truncate">{{ $relationship->dependent->email }}</span>
</div>
@elseif($user->email)
<div class="d-flex align-items-center gap-2 small">
<i class="bi bi-envelope-fill {{ $relationship->dependent->gender == 'm' ? 'text-primary' : 'text-danger' }}"></i>
<span class="fw-medium text-truncate">{{ $user->email }}</span>
<span class="badge {{ $relationship->dependent->gender == 'm' ? 'bg-info' : 'bg-danger' }} {{ $relationship->dependent->gender == 'm' ? 'text-dark' : 'text-white' }} ms-auto">Guardian's</span>
</div>
@endif
</div>
<!-- Details -->
<div class="px-4 py-3 flex-grow-1">
<div class="row g-3 mb-3">
<div class="col-6">
<div class="small text-muted text-uppercase fw-medium mb-1" style="font-size: 0.7rem; letter-spacing: 0.5px;">Gender</div>
<div class="fw-semibold text-capitalize">{{ $relationship->dependent->gender == 'm' ? 'Male' : 'Female' }}</div>
</div>
<div class="col-6">
<div class="small text-muted text-uppercase fw-medium mb-1" style="font-size: 0.7rem; letter-spacing: 0.5px;">Age</div>
<div class="fw-semibold">{{ $relationship->dependent->age }} years</div>
</div>
</div>
<div class="row g-3 mb-3">
<div class="col-6">
<div class="small text-muted text-uppercase fw-medium mb-1" style="font-size: 0.7rem; letter-spacing: 0.5px;">Nationality</div>
<div class="fw-semibold fs-5 nationality-display" data-iso3="{{ $relationship->dependent->nationality }}">{{ $relationship->dependent->nationality }}</div>
</div>
<div class="col-6">
<div class="small text-muted text-uppercase fw-medium mb-1" style="font-size: 0.7rem; letter-spacing: 0.5px;">Horoscope</div>
<div class="fw-semibold">
@php
$horoscopeSymbols = [
'Aries' => '♈',
'Taurus' => '♉',
'Gemini' => '♊',
'Cancer' => '♋',
'Leo' => '♌',
'Virgo' => '♍',
'Libra' => '♎',
'Scorpio' => '♏',
'Sagittarius' => '♐',
'Capricorn' => '♑',
'Aquarius' => '♒',
'Pisces' => '♓'
];
$horoscope = $relationship->dependent->horoscope ?? 'N/A';
$symbol = $horoscopeSymbols[$horoscope] ?? '';
@endphp
{{ $symbol }} {{ $horoscope }}
</div>
</div>
</div>
<div class="pt-2 border-top">
<div class="d-flex justify-content-between align-items-center small mb-2">
<span class="text-muted fw-medium">Next Birthday</span>
<span class="fw-semibold {{ $relationship->dependent->gender == 'm' ? 'text-primary' : 'text-danger' }}">
{{ $relationship->dependent->birthdate->copy()->year(now()->year)->isFuture()
? $relationship->dependent->birthdate->copy()->year(now()->year)->diffForHumans(['parts' => 2, 'syntax' => \Carbon\CarbonInterface::DIFF_ABSOLUTE])
: $relationship->dependent->birthdate->copy()->year(now()->year + 1)->diffForHumans(['parts' => 2, 'syntax' => \Carbon\CarbonInterface::DIFF_ABSOLUTE]) }}
</span>
</div>
<div class="d-flex justify-content-between align-items-center small">
<span class="text-muted fw-medium">Member Since</span>
<span class="fw-semibold">{{ $relationship->dependent->created_at->format('d/m/Y') }}</span>
</div>
</div>
</div>
</div>
</div>
</a>
</div>
@endforeach
<!-- Add New Family Member Card -->
<div class="col">
<div class="card h-100 shadow-sm border-dashed">
<div class="card h-100 shadow-sm border-dashed add-card">
<a href="{{ route('family.create') }}" class="card-body text-center text-decoration-none d-flex flex-column justify-content-center align-items-center" style="height: 100%;">
<div class="mb-3">
<i class="bi bi-plus-circle" style="font-size: 3rem;"></i>
@ -149,5 +351,75 @@
border-width: 2px !important;
border-color: #dee2e6 !important;
}
/* Family Card Hover Effects */
.family-card {
transition: all 0.3s ease-in-out;
cursor: pointer;
}
.family-card:hover {
transform: translateY(-8px);
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15) !important;
}
.family-card:hover .rounded-circle {
transform: scale(1.1);
transition: transform 0.3s ease-in-out;
}
/* Remove underline from card links */
a.text-decoration-none:hover .family-card {
text-decoration: none;
}
/* Add Card Hover Effects */
.add-card {
transition: all 0.3s ease-in-out;
}
.add-card:hover {
transform: translateY(-8px);
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15) !important;
border-color: #0d6efd !important;
}
.add-card:hover .bi-plus-circle {
color: #0d6efd;
transition: color 0.3s ease-in-out;
}
.add-card:hover h5 {
color: #0d6efd;
transition: color 0.3s ease-in-out;
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Load countries from JSON file
fetch('/data/countries.json')
.then(response => response.json())
.then(countries => {
// Convert all nationality displays from ISO3 to country name with flag
document.querySelectorAll('.nationality-display').forEach(element => {
const iso3Code = element.getAttribute('data-iso3');
if (!iso3Code) return;
const country = countries.find(c => c.iso3 === iso3Code);
if (country) {
// Get flag emoji from ISO2 code
const flagEmoji = country.iso2
.toUpperCase()
.split('')
.map(char => String.fromCodePoint(127397 + char.charCodeAt(0)))
.join('');
element.textContent = `${flagEmoji} ${country.iso2.toUpperCase()}`;
}
});
})
.catch(error => console.error('Error loading countries:', error));
});
</script>
@endsection

View File

@ -34,8 +34,8 @@
<label for="gender" class="form-label">Gender</label>
<select class="form-select @error('gender') is-invalid @enderror" id="gender" name="gender" required>
<option value="">Select Gender</option>
<option value="male" {{ old('gender', $relationship->dependent->gender) == 'male' ? 'selected' : '' }}>Male</option>
<option value="female" {{ old('gender', $relationship->dependent->gender) == 'female' ? 'selected' : '' }}>Female</option>
<option value="m" {{ old('gender', $relationship->dependent->gender) == 'm' ? 'selected' : '' }}>Male</option>
<option value="f" {{ old('gender', $relationship->dependent->gender) == 'f' ? 'selected' : '' }}>Female</option>
</select>
@error('gender')
<div class="invalid-feedback">{{ $message }}</div>
@ -52,18 +52,30 @@
<div class="row mb-3">
<div class="col-md-6">
<label for="blood_type" class="form-label">Blood Type <span class="text-muted">(Optional)</span></label>
<input type="text" class="form-control @error('blood_type') is-invalid @enderror" id="blood_type" name="blood_type" value="{{ old('blood_type', $relationship->dependent->blood_type) }}">
<label for="blood_type" class="form-label">Blood Type</label>
<select class="form-select @error('blood_type') is-invalid @enderror" id="blood_type" name="blood_type">
<option value="">Select Blood Type</option>
<option value="A+" {{ old('blood_type', $relationship->dependent->blood_type) == 'A+' ? 'selected' : '' }}>A+</option>
<option value="A-" {{ old('blood_type', $relationship->dependent->blood_type) == 'A-' ? 'selected' : '' }}>A-</option>
<option value="B+" {{ old('blood_type', $relationship->dependent->blood_type) == 'B+' ? 'selected' : '' }}>B+</option>
<option value="B-" {{ old('blood_type', $relationship->dependent->blood_type) == 'B-' ? 'selected' : '' }}>B-</option>
<option value="AB+" {{ old('blood_type', $relationship->dependent->blood_type) == 'AB+' ? 'selected' : '' }}>AB+</option>
<option value="AB-" {{ old('blood_type', $relationship->dependent->blood_type) == 'AB-' ? 'selected' : '' }}>AB-</option>
<option value="O+" {{ old('blood_type', $relationship->dependent->blood_type) == 'O+' ? 'selected' : '' }}>O+</option>
<option value="O-" {{ old('blood_type', $relationship->dependent->blood_type) == 'O-' ? 'selected' : '' }}>O-</option>
<option value="Unknown" {{ old('blood_type', $relationship->dependent->blood_type) == 'Unknown' ? 'selected' : '' }}>Unknown</option>
</select>
@error('blood_type')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="col-md-6">
<label for="nationality" class="form-label">Nationality</label>
<input type="text" class="form-control @error('nationality') is-invalid @enderror" id="nationality" name="nationality" value="{{ old('nationality', $relationship->dependent->nationality) }}" required>
@error('nationality')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
<x-country-dropdown
name="nationality"
id="nationality"
:value="old('nationality', $relationship->dependent->nationality)"
:required="true"
:error="$errors->first('nationality')" />
</div>
</div>

View File

@ -2,91 +2,484 @@
@section('content')
<div class="container py-4">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card shadow-sm">
<div class="card-header bg-white d-flex justify-content-between align-items-center">
<h4 class="mb-0">Family Member Details</h4>
<a href="{{ route('family.dashboard') }}" class="btn btn-outline-secondary btn-sm">
<i class="bi bi-arrow-left"></i> Back to Family
</a>
<!-- Header -->
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h2 class="fw-bold mb-1">Member Profile</h2>
<p class="text-muted mb-0">Comprehensive member information and analytics</p>
</div>
<a href="{{ route('family.dashboard') }}" class="btn btn-outline-secondary">
<i class="bi bi-arrow-left me-2"></i>Back to Family
</a>
</div>
<!-- Profile Card -->
<div class="card shadow-sm border-0 mb-4 overflow-hidden">
<div class="d-flex">
<!-- Profile Picture -->
<div style="width: 180px; min-height: 250px;">
@if($relationship->dependent->media_gallery[0] ?? false)
<img src="{{ $relationship->dependent->media_gallery[0] }}" alt="{{ $relationship->dependent->full_name }}" class="w-100 h-100" style="object-fit: cover;">
@else
<div class="w-100 h-100 d-flex align-items-center justify-content-center text-white fw-bold" style="font-size: 3rem; background: linear-gradient(135deg, {{ $relationship->dependent->gender == 'm' ? '#0d6efd 0%, #0a58ca 100%' : '#d63384 0%, #a61e4d 100%' }});">
{{ strtoupper(substr($relationship->dependent->full_name, 0, 1)) }}
</div>
@endif
</div>
<!-- Profile Info -->
<div class="flex-grow-1 p-4">
<div class="d-flex justify-content-between align-items-start mb-2">
<h3 class="fw-bold mb-0">{{ $relationship->dependent->full_name }}</h3>
<button class="btn btn-primary btn-sm">
<i class="bi bi-person-plus me-1"></i>Follow
</button>
</div>
<div class="card-body">
<div class="text-center mb-4">
<img src="{{ $relationship->dependent->media_gallery[0] ?? 'https://via.placeholder.com/150' }}"
class="rounded-circle mb-3" alt="{{ $relationship->dependent->full_name }}"
width="120" height="120">
<h3>{{ $relationship->dependent->full_name }}</h3>
<span class="badge bg-secondary">{{ ucfirst($relationship->relationship_type) }}</span>
@if($relationship->is_billing_contact)
<span class="badge bg-info ms-2">Billing Contact</span>
@endif
</div>
<p class="text-muted fst-italic mb-3">"Every rep brings me closer to my best self. Consistency is my superpower!"</p>
<div class="row">
<div class="col-md-6 mb-3">
<h6 class="text-muted">Age</h6>
<p>{{ $relationship->dependent->age }} years ({{ $relationship->dependent->life_stage }})</p>
</div>
<div class="col-md-6 mb-3">
<h6 class="text-muted">Birthdate</h6>
<p>{{ $relationship->dependent->birthdate->format('F j, Y') }}</p>
</div>
<div class="col-md-6 mb-3">
<h6 class="text-muted">Gender</h6>
<p>{{ ucfirst($relationship->dependent->gender) }}</p>
</div>
<div class="col-md-6 mb-3">
<h6 class="text-muted">Horoscope</h6>
<p>{{ $relationship->dependent->horoscope }}</p>
</div>
<div class="col-md-6 mb-3">
<h6 class="text-muted">Nationality</h6>
<p>{{ $relationship->dependent->nationality }}</p>
</div>
<div class="col-md-6 mb-3">
<h6 class="text-muted">Blood Type</h6>
<p>{{ $relationship->dependent->blood_type ?? 'Not specified' }}</p>
</div>
@if($relationship->dependent->email)
<div class="col-md-6 mb-3">
<h6 class="text-muted">Email</h6>
<p>{{ $relationship->dependent->email }}</p>
</div>
@endif
@if($relationship->dependent->mobile)
<div class="col-md-6 mb-3">
<h6 class="text-muted">Mobile</h6>
<p>{{ $relationship->dependent->mobile }}</p>
</div>
@endif
</div>
<!-- Achievement Badges -->
<div class="d-flex gap-2 mb-3 flex-wrap">
<a href="#" class="border bg-white rounded px-2 py-1 text-decoration-none" style="font-size: 1rem;">🏆 <span class="fw-semibold text-dark">3</span></a>
<a href="#" class="border bg-white rounded px-2 py-1 text-decoration-none" style="font-size: 1rem;">🥇 <span class="fw-semibold text-dark">4</span></a>
<a href="#" class="border bg-white rounded px-2 py-1 text-decoration-none" style="font-size: 1rem;">🥈 <span class="fw-semibold text-dark">6</span></a>
<a href="#" class="border bg-white rounded px-2 py-1 text-decoration-none" style="font-size: 1rem;">🥉 <span class="fw-semibold text-dark">3</span></a>
<a href="#" class="border bg-white rounded px-2 py-1 text-decoration-none" style="font-size: 1rem;">🎯 <span class="fw-semibold text-dark">8</span></a>
<a href="#" class="border bg-white rounded px-2 py-1 text-decoration-none" style="font-size: 1rem;"> <span class="fw-semibold text-dark">12</span></a>
</div>
<div class="d-flex justify-content-end mt-3">
<a href="{{ route('family.edit', $relationship->dependent->id) }}" class="btn btn-primary">
<i class="bi bi-pencil"></i> Edit
</a>
<!-- Status Badges -->
<div class="d-flex gap-3 mb-3 align-items-center flex-wrap">
<span class="text-muted small">
<span class="fw-semibold text-dark nationality-display" data-iso3="{{ $relationship->dependent->nationality }}">{{ $relationship->dependent->nationality }}</span>
</span>
<span class="text-muted small">
<i class="bi bi-{{ $relationship->dependent->gender == 'm' ? 'gender-male' : 'gender-female' }} me-1"></i>
<span class="fw-semibold text-dark">{{ $relationship->dependent->gender == 'm' ? 'Male' : 'Female' }}</span>
</span>
<span class="text-muted small">
<i class="bi bi-calendar-event me-1"></i>
Age <span class="fw-semibold text-dark">{{ $relationship->dependent->age }}</span>
</span>
<span class="text-muted small">
@php
$horoscopeSymbols = [
'Aries' => '♈',
'Taurus' => '♉',
'Gemini' => '♊',
'Cancer' => '♋',
'Leo' => '♌',
'Virgo' => '♍',
'Libra' => '♎',
'Scorpio' => '♏',
'Sagittarius' => '♐',
'Capricorn' => '♑',
'Aquarius' => '♒',
'Pisces' => '♓'
];
$horoscope = $relationship->dependent->horoscope ?? 'N/A';
$symbol = $horoscopeSymbols[$horoscope] ?? '';
@endphp
{{ $symbol }} <span class="fw-semibold text-dark">{{ $horoscope }}</span>
</span>
<span class="text-muted small">
<i class="bi bi-check-circle-fill text-success me-1"></i>
<span class="fw-semibold text-success">Active</span>
</span>
<span class="text-muted small">
<i class="bi bi-calendar-check me-1"></i>
Joined <span class="fw-semibold text-dark">{{ $relationship->dependent->created_at->format('F Y') }}</span>
</span>
</div>
<!-- Social Media Icons -->
<div class="d-flex gap-2 flex-wrap">
<a href="#" class="btn btn-sm btn-outline-secondary rounded-circle" style="width: 32px; height: 32px; padding: 0; display: flex; align-items: center; justify-content: center;" title="Facebook">
<i class="bi bi-facebook"></i>
</a>
<a href="#" class="btn btn-sm btn-outline-secondary rounded-circle" style="width: 32px; height: 32px; padding: 0; display: flex; align-items: center; justify-content: center;" title="Twitter/X">
<span style="font-weight: bold; font-size: 1.2rem;">X</span>
</a>
<a href="#" class="btn btn-sm btn-outline-secondary rounded-circle" style="width: 32px; height: 32px; padding: 0; display: flex; align-items: center; justify-content: center;" title="Instagram">
<i class="bi bi-instagram"></i>
</a>
<a href="#" class="btn btn-sm btn-outline-secondary rounded-circle" style="width: 32px; height: 32px; padding: 0; display: flex; align-items: center; justify-content: center;" title="LinkedIn">
<i class="bi bi-linkedin"></i>
</a>
<a href="#" class="btn btn-sm btn-outline-secondary rounded-circle" style="width: 32px; height: 32px; padding: 0; display: flex; align-items: center; justify-content: center;" title="YouTube">
<i class="bi bi-youtube"></i>
</a>
<a href="#" class="btn btn-sm btn-outline-secondary rounded-circle" style="width: 32px; height: 32px; padding: 0; display: flex; align-items: center; justify-content: center;" title="TikTok">
<i class="bi bi-tiktok"></i>
</a>
<a href="#" class="btn btn-sm btn-outline-secondary rounded-circle" style="width: 32px; height: 32px; padding: 0; display: flex; align-items: center; justify-content: center;" title="Snapchat">
<i class="bi bi-snapchat"></i>
</a>
<a href="#" class="btn btn-sm btn-outline-secondary rounded-circle" style="width: 32px; height: 32px; padding: 0; display: flex; align-items: center; justify-content: center;" title="WhatsApp">
<i class="bi bi-whatsapp"></i>
</a>
<a href="#" class="btn btn-sm btn-outline-secondary rounded-circle" style="width: 32px; height: 32px; padding: 0; display: flex; align-items: center; justify-content: center;" title="Telegram">
<i class="bi bi-telegram"></i>
</a>
<a href="#" class="btn btn-sm btn-outline-secondary rounded-circle" style="width: 32px; height: 32px; padding: 0; display: flex; align-items: center; justify-content: center;" title="Discord">
<i class="bi bi-discord"></i>
</a>
<a href="#" class="btn btn-sm btn-outline-secondary rounded-circle" style="width: 32px; height: 32px; padding: 0; display: flex; align-items: center; justify-content: center;" title="Reddit">
<i class="bi bi-reddit"></i>
</a>
<a href="#" class="btn btn-sm btn-outline-secondary rounded-circle" style="width: 32px; height: 32px; padding: 0; display: flex; align-items: center; justify-content: center;" title="Pinterest">
<i class="bi bi-pinterest"></i>
</a>
<a href="#" class="btn btn-sm btn-outline-secondary rounded-circle" style="width: 32px; height: 32px; padding: 0; display: flex; align-items: center; justify-content: center;" title="Twitch">
<i class="bi bi-twitch"></i>
</a>
<a href="#" class="btn btn-sm btn-outline-secondary rounded-circle" style="width: 32px; height: 32px; padding: 0; display: flex; align-items: center; justify-content: center;" title="GitHub">
<i class="bi bi-github"></i>
</a>
<a href="#" class="btn btn-sm btn-outline-secondary rounded-circle" style="width: 32px; height: 32px; padding: 0; display: flex; align-items: center; justify-content: center;" title="Spotify">
<i class="bi bi-spotify"></i>
</a>
<a href="#" class="btn btn-sm btn-outline-secondary rounded-circle" style="width: 32px; height: 32px; padding: 0; display: flex; align-items: center; justify-content: center;" title="Skype">
<i class="bi bi-skype"></i>
</a>
<a href="#" class="btn btn-sm btn-outline-secondary rounded-circle" style="width: 32px; height: 32px; padding: 0; display: flex; align-items: center; justify-content: center;" title="Slack">
<i class="bi bi-slack"></i>
</a>
<a href="#" class="btn btn-sm btn-outline-secondary rounded-circle" style="width: 32px; height: 32px; padding: 0; display: flex; align-items: center; justify-content: center;" title="Medium">
<i class="bi bi-medium"></i>
</a>
<a href="#" class="btn btn-sm btn-outline-secondary rounded-circle" style="width: 32px; height: 32px; padding: 0; display: flex; align-items: center; justify-content: center;" title="Vimeo">
<i class="bi bi-vimeo"></i>
</a>
<a href="#" class="btn btn-sm btn-outline-secondary rounded-circle" style="width: 32px; height: 32px; padding: 0; display: flex; align-items: center; justify-content: center;" title="Messenger">
<i class="bi bi-messenger"></i>
</a>
<a href="#" class="btn btn-sm btn-outline-secondary rounded-circle" style="width: 32px; height: 32px; padding: 0; display: flex; align-items: center; justify-content: center;" title="WeChat">
<i class="bi bi-wechat"></i>
</a>
<a href="#" class="btn btn-sm btn-outline-secondary rounded-circle" style="width: 32px; height: 32px; padding: 0; display: flex; align-items: center; justify-content: center;" title="Line">
<i class="bi bi-line"></i>
</a>
</div>
</div>
</div>
</div>
<!-- Navigation Tabs -->
<ul class="nav nav-tabs nav-fill mb-4" id="profileTabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active text-dark" id="overview-tab" data-bs-toggle="tab" data-bs-target="#overview" type="button" role="tab">
<i class="bi bi-eye me-2"></i>Overview
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link text-dark" id="attendance-tab" data-bs-toggle="tab" data-bs-target="#attendance" type="button" role="tab">
<i class="bi bi-calendar-check me-2"></i>Attendance
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link text-dark" id="health-tab" data-bs-toggle="tab" data-bs-target="#health" type="button" role="tab">
<i class="bi bi-heart-pulse me-2"></i>Health
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link text-dark" id="goals-tab" data-bs-toggle="tab" data-bs-target="#goals" type="button" role="tab">
<i class="bi bi-bullseye me-2"></i>Goals
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link text-dark" id="achievements-tab" data-bs-toggle="tab" data-bs-target="#achievements" type="button" role="tab">
<i class="bi bi-trophy me-2"></i>Achievements
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link text-dark" id="tournaments-tab" data-bs-toggle="tab" data-bs-target="#tournaments" type="button" role="tab">
<i class="bi bi-award me-2"></i>Tournaments
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link text-dark" id="events-tab" data-bs-toggle="tab" data-bs-target="#events" type="button" role="tab">
<i class="bi bi-calendar-event me-2"></i>Events
</button>
</li>
</ul>
<!-- Tab Content -->
<div class="tab-content" id="profileTabsContent">
<!-- Overview Tab -->
<div class="tab-pane fade show active" id="overview" role="tabpanel">
<!-- Profile Statistics and Revenue Chart Row -->
<div class="row mb-4">
<!-- Profile Statistics -->
<div class="col-lg-6">
<div class="card shadow-sm border-0 h-100">
<div class="card-body p-4">
<div class="d-flex align-items-center mb-2">
<i class="bi bi-bar-chart-line text-primary me-2"></i>
<h5 class="mb-0 fw-bold">Profile Statistics</h5>
</div>
<p class="text-muted small mb-4">Key performance metrics and milestones</p>
<div class="row g-3">
<!-- Total Sessions -->
<div class="col-md-6">
<div class="d-flex align-items-center gap-3 p-3 bg-light rounded">
<div class="rounded-circle d-flex align-items-center justify-content-center" style="width: 48px; height: 48px; background-color: #6f42c1;">
<i class="bi bi-people-fill text-white"></i>
</div>
<div class="flex-grow-1">
<div class="small text-muted mb-1">Total Sessions</div>
<div class="h4 fw-bold mb-2">127</div>
<div class="progress" style="height: 4px; background-color: #e9ecef;">
<div class="progress-bar" role="progressbar" style="width: 85%; background: linear-gradient(90deg, #6f42c1 0%, #8b5cf6 100%);"></div>
</div>
<small class="text-muted">Sessions completed this year</small>
</div>
</div>
</div>
<!-- Total Revenue -->
<div class="col-md-6">
<div class="d-flex align-items-center gap-3 p-3 bg-light rounded">
<div class="rounded-circle d-flex align-items-center justify-content-center" style="width: 48px; height: 48px; background-color: #10b981;">
<i class="bi bi-currency-dollar text-white"></i>
</div>
<div class="flex-grow-1">
<div class="small text-muted mb-1">Total Revenue</div>
<div class="h4 fw-bold mb-2">$4250</div>
<div class="progress" style="height: 4px; background-color: #e9ecef;">
<div class="progress-bar" role="progressbar" style="width: 70%; background: linear-gradient(90deg, #6f42c1 0%, #10b981 100%);"></div>
</div>
<small class="text-muted">Revenue generated this year</small>
</div>
</div>
</div>
<!-- Attendance Rate -->
<div class="col-md-6">
<div class="d-flex align-items-center gap-3 p-3 bg-light rounded">
<div class="rounded-circle d-flex align-items-center justify-content-center" style="width: 48px; height: 48px; background-color: #3b82f6;">
<i class="bi bi-graph-up-arrow text-white"></i>
</div>
<div class="flex-grow-1">
<div class="small text-muted mb-1">Attendance Rate</div>
<div class="h4 fw-bold mb-2">85%</div>
<div class="progress" style="height: 4px; background-color: #e9ecef;">
<div class="progress-bar" role="progressbar" style="width: 85%; background: linear-gradient(90deg, #6f42c1 0%, #10b981 100%);"></div>
</div>
<small class="text-muted">Average session attendance</small>
</div>
</div>
</div>
<!-- Member Since -->
<div class="col-md-6">
<div class="d-flex align-items-center gap-3 p-3 bg-light rounded">
<div class="rounded-circle d-flex align-items-center justify-content-center" style="width: 48px; height: 48px; background-color: #f59e0b;">
<i class="bi bi-calendar-check text-white"></i>
</div>
<div class="flex-grow-1">
<div class="small text-muted mb-1">Member Since</div>
<div class="h4 fw-bold mb-2">1.5</div>
<div class="progress" style="height: 4px; background-color: #e9ecef;">
<div class="progress-bar" role="progressbar" style="width: 30%; background: linear-gradient(90deg, #6f42c1 0%, #10b981 100%);"></div>
</div>
<small class="text-muted">Years of membership</small>
</div>
</div>
</div>
<!-- Achievements -->
<div class="col-md-6">
<div class="d-flex align-items-center gap-3 p-3 bg-light rounded">
<div class="rounded-circle d-flex align-items-center justify-content-center" style="width: 48px; height: 48px; background-color: #8b5cf6;">
<i class="bi bi-trophy-fill text-white"></i>
</div>
<div class="flex-grow-1">
<div class="small text-muted mb-1">Achievements</div>
<div class="h4 fw-bold mb-2">8</div>
<div class="progress" style="height: 4px; background-color: #e9ecef;">
<div class="progress-bar" role="progressbar" style="width: 40%; background: linear-gradient(90deg, #6f42c1 0%, #10b981 100%);"></div>
</div>
<small class="text-muted">Total badges earned</small>
</div>
</div>
</div>
<!-- Goal Completion -->
<div class="col-md-6">
<div class="d-flex align-items-center gap-3 p-3 bg-light rounded">
<div class="rounded-circle d-flex align-items-center justify-content-center" style="width: 48px; height: 48px; background-color: #10b981;">
<i class="bi bi-check-circle-fill text-white"></i>
</div>
<div class="flex-grow-1">
<div class="small text-muted mb-1">Goal Completion</div>
<div class="h4 fw-bold mb-2">75%</div>
<div class="progress" style="height: 4px; background-color: #e9ecef;">
<div class="progress-bar" role="progressbar" style="width: 75%; background: linear-gradient(90deg, #6f42c1 0%, #10b981 100%);"></div>
</div>
<small class="text-muted">Current goals achieved</small>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Revenue Chart -->
<div class="col-lg-6">
<div class="card shadow-sm border-0 h-100">
<div class="card-body p-4">
<div class="d-flex align-items-center mb-2">
<i class="bi bi-bar-chart-line text-primary me-2"></i>
<h5 class="mb-0 fw-bold">Revenue Chart</h5>
</div>
<p class="text-muted small mb-4">Revenue analytics over time</p>
<div class="d-flex align-items-center justify-content-center" style="min-height: 300px;">
<div class="text-center">
<i class="bi bi-graph-up text-muted" style="font-size: 3rem;"></i>
<p class="text-muted mt-3 mb-1">Revenue chart visualization coming soon...</p>
<small class="text-muted">Chart will display revenue trends over time</small>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Memberships -->
<div class="card shadow-sm mt-4">
<div class="card-header bg-white">
<h5 class="mb-0">Club Memberships</h5>
<!-- Complete Payment & Revenue History -->
<div class="card shadow-sm border-0 mb-4">
<div class="card-body p-4">
<div class="d-flex align-items-center mb-2">
<i class="bi bi-receipt text-primary me-2"></i>
<h5 class="mb-0 fw-bold">Complete Payment & Revenue History</h5>
</div>
<p class="text-muted small mb-4">All package payments and revenue transactions in one view</p>
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead class="table-light">
<tr>
<th class="text-muted small fw-semibold">Date</th>
<th class="text-muted small fw-semibold">Transaction Type</th>
<th class="text-muted small fw-semibold">Package/Item</th>
<th class="text-muted small fw-semibold">Duration</th>
<th class="text-muted small fw-semibold">Sessions</th>
<th class="text-muted small fw-semibold">Amount</th>
<th class="text-muted small fw-semibold">Status</th>
<th class="text-muted small fw-semibold">Method</th>
<th class="text-muted small fw-semibold">Evidence</th>
</tr>
</thead>
<tbody>
<tr>
<td class="small">2023-12-15</td>
<td class="small text-primary">Package Payment</td>
<td class="small">Premium Fitness + Personal Training</td>
<td class="small text-muted">2023-12-15 to 2024-06-15</td>
<td class="small">18/24</td>
<td class="small fw-semibold" style="color: #10b981;">649.5 BHD</td>
<td><span class="badge bg-success-subtle text-success small"> Paid</span></td>
<td class="small">Credit Card</td>
<td class="small">
<i class="bi bi-file-earmark-text text-primary"></i>
<i class="bi bi-download text-secondary ms-1"></i>
</td>
</tr>
<tr>
<td class="small">2024-02-15</td>
<td class="small text-primary">Package Payment</td>
<td class="small">Premium Fitness + Personal Training</td>
<td class="small text-muted">2023-12-15 to 2024-06-15</td>
<td class="small">18/24</td>
<td class="small fw-semibold" style="color: #f59e0b;">649.5 BHD</td>
<td><span class="badge bg-warning-subtle text-warning small"> Due</span></td>
<td class="small">Auto-pay</td>
<td class="small">-</td>
</tr>
<tr>
<td class="small">2023-06-15</td>
<td class="small text-primary">Package Payment</td>
<td class="small">Basic Gym Membership</td>
<td class="small text-muted">2023-06-15 to 2023-12-15</td>
<td class="small">Unlimited</td>
<td class="small fw-semibold" style="color: #10b981;">599 BHD</td>
<td><span class="badge bg-success-subtle text-success small"> Paid</span></td>
<td class="small">Bank Transfer</td>
<td class="small">
<i class="bi bi-file-earmark-text text-primary"></i>
</td>
</tr>
<tr>
<td class="small">2024-02-01</td>
<td class="small text-primary">Package Payment</td>
<td class="small">Nutrition Consultation Package</td>
<td class="small text-muted">2024-02-01 to 2024-05-01</td>
<td class="small">0/6</td>
<td class="small fw-semibold" style="color: #f59e0b;">450 BHD</td>
<td><span class="badge bg-warning-subtle text-warning small"> Due</span></td>
<td class="small">Credit Card</td>
<td class="small">-</td>
</tr>
<tr>
<td class="small">2024-01-08</td>
<td class="small text-secondary">Service/Product</td>
<td class="small">Personal Training Session - Paid</td>
<td class="small">-</td>
<td class="small">-</td>
<td class="small fw-semibold" style="color: #10b981;">75 BHD</td>
<td><span class="badge bg-success-subtle text-success small"> Paid</span></td>
<td class="small">Credit Card</td>
<td class="small">-</td>
</tr>
<tr>
<td class="small">2024-01-05</td>
<td class="small text-secondary">Service/Product</td>
<td class="small">Protein Supplement - Paid</td>
<td class="small">-</td>
<td class="small">-</td>
<td class="small fw-semibold" style="color: #10b981;">45 BHD</td>
<td><span class="badge bg-success-subtle text-success small"> Paid</span></td>
<td class="small">Cash</td>
<td class="small">-</td>
</tr>
<tr>
<td class="small">2024-01-01</td>
<td class="small text-secondary">Service/Product</td>
<td class="small">Monthly Membership - Due</td>
<td class="small">-</td>
<td class="small">-</td>
<td class="small fw-semibold" style="color: #10b981;">99 BHD</td>
<td><span class="badge bg-success-subtle text-success small"> Paid</span></td>
<td class="small">Auto-pay</td>
<td class="small">-</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="card-body">
</div>
<!-- Club Memberships -->
<div class="card shadow-sm border-0 mb-4">
<div class="card-body p-4">
<h5 class="fw-bold mb-3">Club Memberships</h5>
@if($relationship->dependent->memberClubs->count() > 0)
<div class="list-group">
<div class="row g-3">
@foreach($relationship->dependent->memberClubs as $club)
<div class="list-group-item list-group-item-action d-flex justify-content-between align-items-center">
<div>
<h6 class="mb-1">{{ $club->club_name }}</h6>
<small class="text-muted">Status: {{ ucfirst($club->pivot->status) }}</small>
<div class="col-md-6">
<div class="border rounded p-3">
<div class="d-flex justify-content-between align-items-start">
<div>
<h6 class="fw-bold mb-1">{{ $club->club_name }}</h6>
<small class="text-muted">Status: {{ ucfirst($club->pivot->status) }}</small>
</div>
<span class="badge bg-{{ $club->pivot->status === 'active' ? 'success' : 'secondary' }}">
{{ ucfirst($club->pivot->status) }}
</span>
</div>
</div>
<span class="badge bg-{{ $club->pivot->status === 'active' ? 'success' : 'secondary' }} rounded-pill">
{{ ucfirst($club->pivot->status) }}
</span>
</div>
@endforeach
</div>
@ -96,12 +489,10 @@
</div>
</div>
<!-- Invoices -->
<div class="card shadow-sm mt-4">
<div class="card-header bg-white">
<h5 class="mb-0">Recent Invoices</h5>
</div>
<div class="card-body">
<!-- Recent Invoices -->
<div class="card shadow-sm border-0">
<div class="card-body p-4">
<h5 class="fw-bold mb-3">Recent Invoices</h5>
@if($relationship->dependent->studentInvoices->count() > 0)
<div class="table-responsive">
<table class="table table-hover">
@ -139,19 +530,357 @@
</tbody>
</table>
</div>
@if($relationship->dependent->studentInvoices->count() > 5)
<div class="text-center mt-3">
<a href="{{ route('invoices.index') }}" class="btn btn-outline-primary btn-sm">
View All Invoices
</a>
</div>
@endif
@else
<p class="text-center text-muted my-4">No invoices found.</p>
@endif
</div>
</div>
</div>
<!-- Attendance Tab -->
<div class="tab-pane fade" id="attendance" role="tabpanel">
<div class="card shadow-sm border-0">
<div class="card-body p-4">
<h5 class="fw-bold mb-3">Attendance Records</h5>
<p class="text-muted">Attendance tracking coming soon...</p>
</div>
</div>
</div>
<!-- Health Tab -->
<div class="tab-pane fade" id="health" role="tabpanel">
<!-- Health Tracking Header -->
<div class="card shadow-sm border-0 mb-4">
<div class="card-body p-4">
<div class="d-flex align-items-center mb-2">
<i class="bi bi-heart-pulse text-danger me-2"></i>
<h5 class="mb-0 fw-bold">Health Tracking</h5>
</div>
<p class="text-muted small mb-4">Monitor health metrics and progress over time</p>
<!-- Health Metrics Cards -->
<div class="row g-3">
<!-- Weight -->
<div class="col-md-2">
<div class="text-center p-3 bg-light rounded">
<i class="bi bi-speedometer2 text-purple mb-2" style="font-size: 1.5rem; color: #8b5cf6;"></i>
<div class="h4 fw-bold mb-0">75</div>
<small class="text-muted">Weight (kg)</small>
</div>
</div>
<!-- Body Fat -->
<div class="col-md-2">
<div class="text-center p-3 bg-light rounded">
<i class="bi bi-activity text-warning mb-2" style="font-size: 1.5rem;"></i>
<div class="h4 fw-bold mb-0">12.8%</div>
<small class="text-muted">Body Fat</small>
</div>
</div>
<!-- Body Water -->
<div class="col-md-2">
<div class="text-center p-3 bg-light rounded">
<i class="bi bi-droplet text-info mb-2" style="font-size: 1.5rem;"></i>
<div class="h4 fw-bold mb-0">68.2%</div>
<small class="text-muted">Body Water</small>
</div>
</div>
<!-- Muscle Mass -->
<div class="col-md-2">
<div class="text-center p-3 bg-light rounded">
<i class="bi bi-heart text-success mb-2" style="font-size: 1.5rem;"></i>
<div class="h4 fw-bold mb-0">70.5</div>
<small class="text-muted">Muscle Mass</small>
</div>
</div>
<!-- Bone Mass -->
<div class="col-md-2">
<div class="text-center p-3 bg-light rounded">
<i class="bi bi-capsule text-secondary mb-2" style="font-size: 1.5rem;"></i>
<div class="h4 fw-bold mb-0">3.7</div>
<small class="text-muted">Bone Mass</small>
</div>
</div>
<!-- BMR -->
<div class="col-md-2">
<div class="text-center p-3 bg-light rounded">
<i class="bi bi-lightning text-danger mb-2" style="font-size: 1.5rem;"></i>
<div class="h4 fw-bold mb-0">1895</div>
<small class="text-muted">BMR (cal)</small>
</div>
</div>
</div>
</div>
</div>
<!-- Body Composition Analysis & Compare Row -->
<div class="row mb-4">
<!-- Body Composition Analysis -->
<div class="col-lg-7">
<div class="card shadow-sm border-0 h-100">
<div class="card-body p-4">
<h5 class="fw-bold mb-4">Body Composition Analysis</h5>
<div class="text-center py-5">
<i class="bi bi-radar text-muted" style="font-size: 3rem;"></i>
<p class="text-muted mt-3 mb-1">Radar chart visualization coming soon...</p>
<small class="text-muted">Chart will compare current vs previous body composition metrics</small>
</div>
</div>
</div>
</div>
<!-- Compare -->
<div class="col-lg-5">
<div class="card shadow-sm border-0 h-100">
<div class="card-body p-4">
<h5 class="fw-bold mb-4">Compare</h5>
<div class="table-responsive">
<table class="table table-sm align-middle">
<thead>
<tr class="border-bottom">
<th class="text-muted small fw-semibold">Metric</th>
<th class="text-muted small fw-semibold text-end">Current</th>
<th class="text-muted small fw-semibold text-center">Change</th>
<th class="text-muted small fw-semibold text-end">Previous</th>
</tr>
</thead>
<tbody>
<tr>
<td class="small"><i class="bi bi-speedometer2 me-2"></i>Weight</td>
<td class="small text-end fw-semibold">75kg</td>
<td class="text-center"><i class="bi bi-arrow-down text-success"></i></td>
<td class="small text-end text-muted">77kg</td>
</tr>
<tr>
<td class="small"><i class="bi bi-activity me-2"></i>Body Fat</td>
<td class="small text-end fw-semibold">12.8%</td>
<td class="text-center"><i class="bi bi-arrow-down text-success"></i></td>
<td class="small text-end text-muted">14.5%</td>
</tr>
<tr>
<td class="small"><i class="bi bi-calculator me-2"></i>BMI</td>
<td class="small text-end fw-semibold">22.5</td>
<td class="text-center"><i class="bi bi-arrow-down text-success"></i></td>
<td class="small text-end text-muted">23.2</td>
</tr>
<tr>
<td class="small"><i class="bi bi-droplet me-2"></i>Body Water</td>
<td class="small text-end fw-semibold">68.2%</td>
<td class="text-center"><i class="bi bi-arrow-up text-success"></i></td>
<td class="small text-end text-muted">65.8%</td>
</tr>
<tr>
<td class="small"><i class="bi bi-heart me-2"></i>Muscle Mass</td>
<td class="small text-end fw-semibold">70.5kg</td>
<td class="text-center"><i class="bi bi-arrow-up text-success"></i></td>
<td class="small text-end text-muted">68.2kg</td>
</tr>
<tr>
<td class="small"><i class="bi bi-capsule me-2"></i>Bone Mass</td>
<td class="small text-end fw-semibold">3.7kg</td>
<td class="text-center"><i class="bi bi-arrow-up text-success"></i></td>
<td class="small text-end text-muted">3.6kg</td>
</tr>
<tr>
<td class="small"><i class="bi bi-activity me-2"></i>Visceral Fat</td>
<td class="small text-end fw-semibold">3</td>
<td class="text-center"><i class="bi bi-arrow-down text-success"></i></td>
<td class="small text-end text-muted">4</td>
</tr>
<tr>
<td class="small"><i class="bi bi-lightning me-2"></i>BMR</td>
<td class="small text-end fw-semibold">1895cal</td>
<td class="text-center"><i class="bi bi-arrow-up text-success"></i></td>
<td class="small text-end text-muted">1865cal</td>
</tr>
<tr>
<td class="small"><i class="bi bi-heart-pulse me-2"></i>Protein</td>
<td class="small text-end fw-semibold">19.8%</td>
<td class="text-center"><i class="bi bi-arrow-up text-success"></i></td>
<td class="small text-end text-muted">18.9%</td>
</tr>
<tr>
<td class="small"><i class="bi bi-calendar-heart me-2"></i>Body Age</td>
<td class="small text-end fw-semibold">25yrs</td>
<td class="text-center"><i class="bi bi-arrow-down text-success"></i></td>
<td class="small text-end text-muted">27yrs</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- Health Tracking History -->
<div class="card shadow-sm border-0">
<div class="card-body p-4">
<h5 class="fw-bold mb-4">Health Tracking History</h5>
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead class="table-light">
<tr>
<th class="text-muted small fw-semibold">Date</th>
<th class="text-muted small fw-semibold text-center"><i class="bi bi-speedometer2 me-1"></i>Weight (kg)</th>
<th class="text-muted small fw-semibold text-center"><i class="bi bi-activity me-1"></i>Body Fat %</th>
<th class="text-muted small fw-semibold text-center"><i class="bi bi-calculator me-1"></i>BMI</th>
<th class="text-muted small fw-semibold text-center"><i class="bi bi-droplet me-1"></i>Body Water %</th>
<th class="text-muted small fw-semibold text-center"><i class="bi bi-heart me-1"></i>Muscle Mass (kg)</th>
<th class="text-muted small fw-semibold text-center"><i class="bi bi-capsule me-1"></i>Bone Mass (kg)</th>
<th class="text-muted small fw-semibold text-center"><i class="bi bi-activity me-1"></i>Visceral Fat</th>
<th class="text-muted small fw-semibold text-center"><i class="bi bi-lightning me-1"></i>BMR</th>
<th class="text-muted small fw-semibold text-center"><i class="bi bi-heart-pulse me-1"></i>Protein %</th>
<th class="text-muted small fw-semibold text-center"><i class="bi bi-calendar-heart me-1"></i>Body Age</th>
</tr>
</thead>
<tbody>
<tr>
<td class="small fw-semibold">Jan 08, 2024</td>
<td class="small text-center">75</td>
<td class="small text-center">12.8</td>
<td class="small text-center">22.5</td>
<td class="small text-center">68.2</td>
<td class="small text-center">70.5</td>
<td class="small text-center">3.7</td>
<td class="small text-center">3</td>
<td class="small text-center">1895</td>
<td class="small text-center">19.8</td>
<td class="small text-center">25</td>
</tr>
<tr>
<td class="small fw-semibold">Jan 01, 2024</td>
<td class="small text-center">77</td>
<td class="small text-center">14.5</td>
<td class="small text-center">23.2</td>
<td class="small text-center">65.8</td>
<td class="small text-center">68.2</td>
<td class="small text-center">3.6</td>
<td class="small text-center">4</td>
<td class="small text-center">1865</td>
<td class="small text-center">18.9</td>
<td class="small text-center">27</td>
</tr>
<tr>
<td class="small fw-semibold">Dec 25, 2023</td>
<td class="small text-center">79</td>
<td class="small text-center">16.8</td>
<td class="small text-center">23.9</td>
<td class="small text-center">63.2</td>
<td class="small text-center">66.7</td>
<td class="small text-center">3.5</td>
<td class="small text-center">5</td>
<td class="small text-center">1845</td>
<td class="small text-center">18.2</td>
<td class="small text-center">29</td>
</tr>
<tr>
<td class="small fw-semibold">Dec 18, 2023</td>
<td class="small text-center">82</td>
<td class="small text-center">19.2</td>
<td class="small text-center">24.6</td>
<td class="small text-center">60.8</td>
<td class="small text-center">64.7</td>
<td class="small text-center">3.4</td>
<td class="small text-center">7</td>
<td class="small text-center">1820</td>
<td class="small text-center">17.6</td>
<td class="small text-center">31</td>
</tr>
<tr>
<td class="small fw-semibold">Dec 11, 2023</td>
<td class="small text-center">84</td>
<td class="small text-center">21.5</td>
<td class="small text-center">25.2</td>
<td class="small text-center">58.5</td>
<td class="small text-center">63</td>
<td class="small text-center">3.4</td>
<td class="small text-center">8</td>
<td class="small text-center">1795</td>
<td class="small text-center">17.1</td>
<td class="small text-center">33</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Goals Tab -->
<div class="tab-pane fade" id="goals" role="tabpanel">
<div class="card shadow-sm border-0">
<div class="card-body p-4">
<h5 class="fw-bold mb-3">Goals & Progress</h5>
<p class="text-muted">Goal tracking coming soon...</p>
</div>
</div>
</div>
<!-- Achievements Tab -->
<div class="tab-pane fade" id="achievements" role="tabpanel">
<div class="card shadow-sm border-0">
<div class="card-body p-4">
<h5 class="fw-bold mb-3">Achievements & Badges</h5>
<p class="text-muted">Achievement system coming soon...</p>
</div>
</div>
</div>
<!-- Tournaments Tab -->
<div class="tab-pane fade" id="tournaments" role="tabpanel">
<div class="card shadow-sm border-0">
<div class="card-body p-4">
<h5 class="fw-bold mb-3">Tournament History</h5>
<p class="text-muted">Tournament records coming soon...</p>
</div>
</div>
</div>
<!-- Events Tab -->
<div class="tab-pane fade" id="events" role="tabpanel">
<div class="card shadow-sm border-0">
<div class="card-body p-4">
<h5 class="fw-bold mb-3">Event Participation</h5>
<p class="text-muted">Event history coming soon...</p>
</div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Load countries from JSON file
fetch('/data/countries.json')
.then(response => response.json())
.then(countries => {
// Convert all nationality displays from ISO3 to country name with flag
document.querySelectorAll('.nationality-display').forEach(element => {
const iso3Code = element.getAttribute('data-iso3');
if (!iso3Code) return;
const country = countries.find(c => c.iso3 === iso3Code);
if (country) {
// Get flag emoji from ISO2 code
const flagEmoji = country.iso2
.toUpperCase()
.split('')
.map(char => String.fromCodePoint(127397 + char.charCodeAt(0)))
.join('');
element.textContent = `${flagEmoji} ${country.name}`;
}
});
})
.catch(error => console.error('Error loading countries:', error));
});
</script>
@endsection

View File

@ -18,6 +18,12 @@
<!-- Bootstrap Icons -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
<!-- Flag Icons -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flag-icons@6.6.6/css/flag-icons.min.css">
<!-- Select2 CSS -->
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
<!-- Custom Styles -->
<style>
body {
@ -52,12 +58,14 @@
border-color: #0a58ca;
}
</style>
@stack('styles')
</head>
<body>
<nav class="navbar navbar-expand-md navbar-light bg-white shadow-sm">
<div class="container">
<a class="navbar-brand" href="{{ url('/') }}">
{{ config('app.name', 'Club SaaS') }}
<img src="{{ asset('images/logo.png') }}" alt="TAKEONE" height="40">
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
<span class="navbar-toggler-icon"></span>
@ -67,6 +75,9 @@
<!-- Left Side Of Navbar -->
<ul class="navbar-nav me-auto">
@auth
<li class="nav-item">
<a class="nav-link" href="#">Marketplace</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ route('family.dashboard') }}">My Family</a>
</li>
@ -98,9 +109,6 @@
</a>
<div class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="{{ route('profile.edit') }}">
{{ __('Profile') }}
</a>
<a class="dropdown-item" href="{{ route('logout') }}"
onclick="event.preventDefault();
document.getElementById('logout-form').submit();">
@ -124,5 +132,13 @@
<!-- Bootstrap JS Bundle with Popper -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
<!-- jQuery (required for Select2) -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<!-- Select2 JS -->
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
@stack('scripts')
</body>
</html>

View File

@ -3,13 +3,45 @@
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\FamilyController;
use App\Http\Controllers\InvoiceController;
use App\Http\Controllers\Auth\PasswordResetLinkController;
use App\Http\Controllers\Auth\NewPasswordController;
use App\Http\Controllers\Auth\RegisteredUserController;
use App\Http\Controllers\Auth\AuthenticatedSessionController;
Route::get('/', function () {
return view('welcome');
});
// Authentication routes
Route::get('/login', [AuthenticatedSessionController::class, 'create'])
->name('login');
Route::post('/login', [AuthenticatedSessionController::class, 'store']);
Route::post('/logout', [AuthenticatedSessionController::class, 'destroy'])
->name('logout');
Route::get('/register', [RegisteredUserController::class, 'create'])
->name('register');
Route::post('/register', [RegisteredUserController::class, 'store']);
// Password reset routes
Route::get('/forgot-password', [PasswordResetLinkController::class, 'create'])
->name('password.request');
Route::post('/forgot-password', [PasswordResetLinkController::class, 'store'])
->name('password.email');
Route::get('/reset-password/{token}', [NewPasswordController::class, 'create'])
->name('password.reset');
Route::post('/reset-password', [NewPasswordController::class, 'store'])
->name('password.update');
// Family routes
Route::middleware(['auth'])->group(function () {
Route::get('/profile', [FamilyController::class, 'profile'])->name('profile.show');
Route::get('/family', [FamilyController::class, 'dashboard'])->name('family.dashboard');
Route::get('/family/create', [FamilyController::class, 'create'])->name('family.create');
Route::post('/family', [FamilyController::class, 'store'])->name('family.store');