diff --git a/app/Http/Controllers/Auth/AuthenticatedSessionController.php b/app/Http/Controllers/Auth/AuthenticatedSessionController.php new file mode 100644 index 0000000..102966a --- /dev/null +++ b/app/Http/Controllers/Auth/AuthenticatedSessionController.php @@ -0,0 +1,61 @@ +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('/'); + } +} diff --git a/app/Http/Controllers/Auth/NewPasswordController.php b/app/Http/Controllers/Auth/NewPasswordController.php new file mode 100644 index 0000000..b9197d4 --- /dev/null +++ b/app/Http/Controllers/Auth/NewPasswordController.php @@ -0,0 +1,64 @@ + $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)]); + } +} diff --git a/app/Http/Controllers/Auth/PasswordResetLinkController.php b/app/Http/Controllers/Auth/PasswordResetLinkController.php new file mode 100644 index 0000000..667ab94 --- /dev/null +++ b/app/Http/Controllers/Auth/PasswordResetLinkController.php @@ -0,0 +1,47 @@ +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)]); + } +} diff --git a/app/Http/Controllers/Auth/RegisteredUserController.php b/app/Http/Controllers/Auth/RegisteredUserController.php new file mode 100644 index 0000000..1d64a8d --- /dev/null +++ b/app/Http/Controllers/Auth/RegisteredUserController.php @@ -0,0 +1,68 @@ +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.'); + } +} diff --git a/app/Http/Controllers/FamilyController.php b/app/Http/Controllers/FamilyController.php index 2e5e576..021b01a 100644 --- a/app/Http/Controllers/FamilyController.php +++ b/app/Http/Controllers/FamilyController.php @@ -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', diff --git a/app/Mail/WelcomeEmail.php b/app/Mail/WelcomeEmail.php new file mode 100644 index 0000000..efb4480 --- /dev/null +++ b/app/Mail/WelcomeEmail.php @@ -0,0 +1,83 @@ +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 []; + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 54233e7..37b488f 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -22,6 +22,7 @@ class User extends Authenticatable * @var list */ protected $fillable = [ + 'name', 'full_name', 'email', 'mobile', diff --git a/app/Services/FamilyService.php b/app/Services/FamilyService.php index 7a42bc3..82a01a7 100644 --- a/app/Services/FamilyService.php +++ b/app/Services/FamilyService.php @@ -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; } diff --git a/database/migrations/2026_01_20_064337_create_club_saas_schema.php b/database/migrations/2026_01_20_064337_create_club_saas_schema.php deleted file mode 100644 index 06d19f6..0000000 --- a/database/migrations/2026_01_20_064337_create_club_saas_schema.php +++ /dev/null @@ -1,103 +0,0 @@ -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(); - }); - } -}; diff --git a/database/migrations/2026_01_20_080000_create_club_saas_tables.php b/database/migrations/2026_01_20_080000_create_club_saas_tables.php new file mode 100644 index 0000000..2b3c2da --- /dev/null +++ b/database/migrations/2026_01_20_080000_create_club_saas_tables.php @@ -0,0 +1,128 @@ +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(); + }); + } +}; diff --git a/public/data/countries.json b/public/data/countries.json new file mode 100644 index 0000000..9a82672 --- /dev/null +++ b/public/data/countries.json @@ -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"} +] diff --git a/public/images/logo.png b/public/images/logo.png new file mode 100644 index 0000000..757ab44 Binary files /dev/null and b/public/images/logo.png differ diff --git a/public/images/logo.svg.svg b/public/images/logo.svg.svg new file mode 100644 index 0000000..9ce9847 --- /dev/null +++ b/public/images/logo.svg.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/resources/views/auth/forgot-password.blade.php b/resources/views/auth/forgot-password.blade.php new file mode 100644 index 0000000..0c5c2d0 --- /dev/null +++ b/resources/views/auth/forgot-password.blade.php @@ -0,0 +1,52 @@ +@extends('layouts.app') + +@section('content') +
+
+
+
+
+
+

Forgot Password?

+

No problem. Just let us know your email address and we'll email you a password reset link.

+
+ +
+ @csrf + + +
+ + + @error('email') + + {{ $message }} + + @enderror +
+ + +
+ +
+ + + +
+
+
+
+
+
+@endsection diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php new file mode 100644 index 0000000..accb44f --- /dev/null +++ b/resources/views/auth/login.blade.php @@ -0,0 +1,76 @@ +@extends('layouts.app') + +@section('content') +
+
+
+
+
+
+

Welcome Back

+

Sign in to your account

+
+ +
+ @csrf + + +
+ + + @error('email') + + {{ $message }} + + @enderror +
+ + +
+ + + @error('password') + + {{ $message }} + + @enderror +
+ + +
+ + +
+ + +
+ +
+ + +
+ @if (Route::has('password.request')) + + Forgot Password? + + @endif +
+
+
+
+
+
+
+@endsection diff --git a/resources/views/auth/register.blade.php b/resources/views/auth/register.blade.php new file mode 100644 index 0000000..62ece77 --- /dev/null +++ b/resources/views/auth/register.blade.php @@ -0,0 +1,236 @@ +@extends('layouts.app') + +@section('content') + + + + + + + + + + + +
+
+
+
+
+

Create Your Profile

+
+
+
+ @csrf + +
+ +
+ +
+ + + @error('full_name') + + {{ $message }} + + @enderror +
+ + +
+ + + @error('email') + + {{ $message }} + + @enderror +
+ + +
+ + + @error('password') + + {{ $message }} + + @enderror +
+ + +
+ + +
+
+ + +
+ +
+ + + + + @error('mobile_number') + + {{ $message }} + + @enderror +
+ + +
+ + + @error('gender') + + {{ $message }} + + @enderror +
+ + +
+ + + @error('birthdate') + + {{ $message }} + + @enderror +
+ + +
+ +
+
+
+ + +
+ +
+
+
+
+
+
+
+ + + + + + + + + + +@stack('styles') +@stack('scripts') + + +@endsection diff --git a/resources/views/auth/reset-password.blade.php b/resources/views/auth/reset-password.blade.php new file mode 100644 index 0000000..55eda4c --- /dev/null +++ b/resources/views/auth/reset-password.blade.php @@ -0,0 +1,78 @@ +@extends('layouts.app') + +@section('content') +
+
+
+
+
+
+

Reset Password

+

Enter your new password below.

+
+ +
+ @csrf + + + + + +
+ + + @error('email') + + {{ $message }} + + @enderror +
+ + +
+ + + @error('password') + + {{ $message }} + + @enderror +
+ + +
+ + +
+ + +
+ +
+ + + +
+
+
+
+
+
+@endsection diff --git a/resources/views/components/README.md b/resources/views/components/README.md new file mode 100644 index 0000000..6e1537a --- /dev/null +++ b/resources/views/components/README.md @@ -0,0 +1,331 @@ +# Reusable Form Components + +This directory contains reusable Blade components for forms across the application. + +## Available Components + +### 1. 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 + +``` + +**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 +
+ + +
+``` + +--- + +### 2. 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 + +``` + +**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 +
+ + +
+``` + +--- + +### 3. 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 + +``` + +**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 +
+ + +
+``` + +--- + +### 4. 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 + +``` + +**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 +
+ + +
+``` + +--- + +## Dependencies + +All components require: + +### Flag Icons +```html + +``` + +### 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') + + + +
+
+
+
+
+

User Information

+
+
+
+ @csrf + + + + + + + + + + + + + + + +
+
+
+
+
+ + +@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 diff --git a/resources/views/components/call-code-dropdown.blade.php b/resources/views/components/call-code-dropdown.blade.php new file mode 100644 index 0000000..d1a6a2b --- /dev/null +++ b/resources/views/components/call-code-dropdown.blade.php @@ -0,0 +1,79 @@ +@props(['name' => 'call_code', 'id' => 'call_code', 'value' => '', 'required' => false, 'error' => null]) + +
+ + + @if($error) + + {{ $error }} + + @endif +
+ +@once + @push('scripts') + + @endpush + + @push('styles') + + @endpush +@endonce diff --git a/resources/views/components/country-code-dropdown.blade.php b/resources/views/components/country-code-dropdown.blade.php new file mode 100644 index 0000000..726846c --- /dev/null +++ b/resources/views/components/country-code-dropdown.blade.php @@ -0,0 +1,191 @@ +@props(['name' => 'country_code', 'id' => 'country_code', 'value' => '+1', 'required' => false, 'error' => null]) + +
+ + + + + + + {{ $slot }} +
+ +@if($error) + + {{ $error }} + +@endif + +@once + @push('styles') + + @endpush + + @push('scripts') + + @endpush +@endonce diff --git a/resources/views/components/country-dropdown.blade.php b/resources/views/components/country-dropdown.blade.php new file mode 100644 index 0000000..8d21835 --- /dev/null +++ b/resources/views/components/country-dropdown.blade.php @@ -0,0 +1,80 @@ +@props(['name' => 'country', 'id' => 'country', 'value' => '', 'required' => false, 'error' => null, 'label' => 'Nationality']) + +
+ + + @if($error) + + {{ $error }} + + @endif +
+ +@once + @push('scripts') + + @endpush + + @push('styles') + + @endpush +@endonce diff --git a/resources/views/components/currency-dropdown.blade.php b/resources/views/components/currency-dropdown.blade.php new file mode 100644 index 0000000..5a2af84 --- /dev/null +++ b/resources/views/components/currency-dropdown.blade.php @@ -0,0 +1,93 @@ +@props(['name' => 'currency', 'id' => 'currency', 'value' => '', 'required' => false, 'error' => null]) + +
+ + + @if($error) + + {{ $error }} + + @endif +
+ +@once + @push('scripts') + + @endpush + + @push('styles') + + @endpush +@endonce diff --git a/resources/views/components/nationality-dropdown.blade.php b/resources/views/components/nationality-dropdown.blade.php new file mode 100644 index 0000000..c5f2218 --- /dev/null +++ b/resources/views/components/nationality-dropdown.blade.php @@ -0,0 +1,115 @@ +@props(['name' => 'nationality', 'id' => 'nationality', 'value' => '', 'required' => false, 'error' => null]) + + + +@if($error) + + {{ $error }} + +@endif + +@once + @push('scripts') + + @endpush +@endonce diff --git a/resources/views/components/timezone-dropdown.blade.php b/resources/views/components/timezone-dropdown.blade.php new file mode 100644 index 0000000..950b79d --- /dev/null +++ b/resources/views/components/timezone-dropdown.blade.php @@ -0,0 +1,92 @@ +@props(['name' => 'timezone', 'id' => 'timezone', 'value' => '', 'required' => false, 'error' => null]) + +
+ + + @if($error) + + {{ $error }} + + @endif +
+ +@once + @push('scripts') + + @endpush + + @push('styles') + + @endpush +@endonce diff --git a/resources/views/emails/reset-password.blade.php b/resources/views/emails/reset-password.blade.php new file mode 100644 index 0000000..244badf --- /dev/null +++ b/resources/views/emails/reset-password.blade.php @@ -0,0 +1,105 @@ + + + + + + Reset Password + + + +
+
+

{{ config('app.name', 'Club SaaS') }}

+
+
+

Reset Your Password

+

Hello,

+

You are receiving this email because we received a password reset request for your account.

+

This password reset link will expire in 60 minutes.

+ + + +

If you did not request a password reset, no further action is required.

+
+ +
+ + diff --git a/resources/views/emails/welcome.blade.php b/resources/views/emails/welcome.blade.php new file mode 100644 index 0000000..b6c0e41 --- /dev/null +++ b/resources/views/emails/welcome.blade.php @@ -0,0 +1,171 @@ + + + + + + Welcome to {{ config('app.name', 'Club SaaS') }} + + + +
+
+

{{ config('app.name', 'Club SaaS') }}

+

Welcome to the Family

+
+
+
+

Dear {{ $user->full_name }},

+

Welcome to the Family!

+

We are thrilled to have you join our community. Your account has been successfully created and you are now part of our family.

+
+ + @if($guardian && $relationship) +
+

Your Family Information

+

Guardian: {{ $guardian->full_name }}

+

Relationship: {{ ucfirst($relationship->relationship_type) }}

+ @if($user->birthdate) +

Birthdate: {{ \Carbon\Carbon::parse($user->birthdate)->format('F j, Y') }}

+ @endif +
+ @endif + +
+ +

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.

+ + + +

+ If you have any questions, feel free to contact us at any time. +

+
+ +
+ + diff --git a/resources/views/examples/form-with-components.blade.php b/resources/views/examples/form-with-components.blade.php new file mode 100644 index 0000000..ec1301a --- /dev/null +++ b/resources/views/examples/form-with-components.blade.php @@ -0,0 +1,161 @@ +@extends('layouts.app') + +@section('content') + + + + + + +
+
+
+
+
+

Example Form with Reusable Components

+
+
+
+ @csrf + +
+
+ +
+ + + + + @error('mobile_number') + + {{ $message }} + + @enderror +
+ + +
+ + + + +
+
+ +
+ +
+ + +
+ + +
+ + +
+
+
+ +
+
Component Features:
+
    +
  • Country Code Dropdown: Bootstrap-based with flag icons and search
  • +
  • Nationality Dropdown: Select2-based with flag icons and search
  • +
  • Both components preserve values on validation errors
  • +
  • Both components support required/optional fields
  • +
  • Both components display validation errors
  • +
  • Multiple instances can be used on the same page
  • +
+
+ +
+ +
+
+
+
+ + +
+
+

How to Use These Components

+
+
+
1. Country Code Dropdown
+
<x-country-code-dropdown
+    name="country_code"
+    id="country_code"
+    :value="old('country_code', '+971')"
+    :required="true"
+    :error="$errors->first('country_code')">
+    <input type="tel" class="form-control" name="mobile_number" placeholder="Phone number">
+</x-country-code-dropdown>
+ +
2. Nationality Dropdown
+
<x-nationality-dropdown
+    name="nationality"
+    id="nationality"
+    :value="old('nationality')"
+    :required="true"
+    :error="$errors->first('nationality')" />
+ +
3. Required Dependencies
+
<!-- CSS -->
+<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flag-icons@6.6.6/css/flag-icons.min.css">
+<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
+
+<!-- JS -->
+<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
+<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
+
+<!-- Component Scripts -->
+@stack('styles')
+@stack('scripts')
+
+
+
+
+
+ + + + + + + + +@stack('styles') +@stack('scripts') +@endsection diff --git a/resources/views/family/create.blade.php b/resources/views/family/create.blade.php index 7e1fe50..79ae609 100644 --- a/resources/views/family/create.blade.php +++ b/resources/views/family/create.blade.php @@ -33,8 +33,8 @@ @error('gender')
{{ $message }}
@@ -51,18 +51,30 @@
- - + + @error('blood_type')
{{ $message }}
@enderror
- - - @error('nationality') -
{{ $message }}
- @enderror +
diff --git a/resources/views/family/dashboard.blade.php b/resources/views/family/dashboard.blade.php index e68025e..5934788 100644 --- a/resources/views/family/dashboard.blade.php +++ b/resources/views/family/dashboard.blade.php @@ -12,63 +12,265 @@ -
+
-
-
-
- {{ $user->full_name }} + + + + +
+ @if($user->mobile) +
+ + {{ $user->mobile }} +
+ @endif + @if($user->email) +
+ + {{ $user->email }} +
+ @endif +
+ + +
+
+
+
Gender
+
{{ $user->gender == 'm' ? 'Male' : 'Female' }}
+
+
+
Age
+
{{ $user->age }} years
+
+
+
+
+
Nationality
+
{{ $user->nationality }}
+
+
+
Horoscope
+
+ @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 }} +
+
+
+
+
+ Next Birthday + + {{ $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]) }} + +
+
+ Member Since + {{ $user->created_at->format('d/m/Y') }} +
+
+
+
+
@foreach($dependents as $relationship)
-
-
-
- {{ $relationship->dependent->full_name }} + +
+ +
+
+
+
+ @if($relationship->dependent->media_gallery[0] ?? false) + {{ $relationship->dependent->full_name }} + @else +
+ {{ strtoupper(substr($relationship->dependent->full_name, 0, 1)) }} +
+ @endif +
+
+
+
{{ $relationship->dependent->full_name }}
+
+ @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 + {{ $ageGroup }} + Active +
+
-
{{ $relationship->dependent->full_name }}
-

- Age: {{ $relationship->dependent->age }} ({{ $relationship->dependent->life_stage }}) -

- {{ ucfirst($relationship->relationship_type) }}
-
+ +
@endforeach
-
+
@@ -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; + } + + @endsection diff --git a/resources/views/family/edit.blade.php b/resources/views/family/edit.blade.php index 2da9e45..88d58d6 100644 --- a/resources/views/family/edit.blade.php +++ b/resources/views/family/edit.blade.php @@ -34,8 +34,8 @@ @error('gender')
{{ $message }}
@@ -52,18 +52,30 @@
- - + + @error('blood_type')
{{ $message }}
@enderror
- - - @error('nationality') -
{{ $message }}
- @enderror +
diff --git a/resources/views/family/show.blade.php b/resources/views/family/show.blade.php index 0e40b75..5db1140 100644 --- a/resources/views/family/show.blade.php +++ b/resources/views/family/show.blade.php @@ -2,91 +2,484 @@ @section('content')
-
-
-
-
-

Family Member Details

-
- Back to Family - + +
+
+

Member Profile

+

Comprehensive member information and analytics

+
+ + Back to Family + +
+ + +
+
+ +
+ @if($relationship->dependent->media_gallery[0] ?? false) + {{ $relationship->dependent->full_name }} + @else +
+ {{ strtoupper(substr($relationship->dependent->full_name, 0, 1)) }} +
+ @endif +
+ + +
+
+

{{ $relationship->dependent->full_name }}

+
-
-
- {{ $relationship->dependent->full_name }} -

{{ $relationship->dependent->full_name }}

- {{ ucfirst($relationship->relationship_type) }} - @if($relationship->is_billing_contact) - Billing Contact - @endif -
+

"Every rep brings me closer to my best self. Consistency is my superpower!"

-
-
-
Age
-

{{ $relationship->dependent->age }} years ({{ $relationship->dependent->life_stage }})

-
-
-
Birthdate
-

{{ $relationship->dependent->birthdate->format('F j, Y') }}

-
-
-
Gender
-

{{ ucfirst($relationship->dependent->gender) }}

-
-
-
Horoscope
-

{{ $relationship->dependent->horoscope }}

-
-
-
Nationality
-

{{ $relationship->dependent->nationality }}

-
-
-
Blood Type
-

{{ $relationship->dependent->blood_type ?? 'Not specified' }}

-
- @if($relationship->dependent->email) -
-
Email
-

{{ $relationship->dependent->email }}

-
- @endif - @if($relationship->dependent->mobile) -
-
Mobile
-

{{ $relationship->dependent->mobile }}

-
- @endif -
+ + -
- - Edit - + +
+ + {{ $relationship->dependent->nationality }} + + + + {{ $relationship->dependent->gender == 'm' ? 'Male' : 'Female' }} + + + + Age {{ $relationship->dependent->age }} + + + @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 }} + + + + Active + + + + Joined {{ $relationship->dependent->created_at->format('F Y') }} + +
+ + + +
+
+
+ + + + + +
+ +
+ +
+ +
+
+
+
+ +
Profile Statistics
+
+

Key performance metrics and milestones

+ +
+ +
+
+
+ +
+
+
Total Sessions
+
127
+
+
+
+ Sessions completed this year +
+
+
+ + +
+
+
+ +
+
+
Total Revenue
+
$4250
+
+
+
+ Revenue generated this year +
+
+
+ + +
+
+
+ +
+
+
Attendance Rate
+
85%
+
+
+
+ Average session attendance +
+
+
+ + +
+
+
+ +
+
+
Member Since
+
1.5
+
+
+
+ Years of membership +
+
+
+ + +
+
+
+ +
+
+
Achievements
+
8
+
+
+
+ Total badges earned +
+
+
+ + +
+
+
+ +
+
+
Goal Completion
+
75%
+
+
+
+ Current goals achieved +
+
+
+
+
+
+
+ + +
+
+
+
+ +
Revenue Chart
+
+

Revenue analytics over time

+ +
+
+ +

Revenue chart visualization coming soon...

+ Chart will display revenue trends over time +
+
+
- -
-
-
Club Memberships
+ +
+
+
+ +
Complete Payment & Revenue History
+
+

All package payments and revenue transactions in one view

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DateTransaction TypePackage/ItemDurationSessionsAmountStatusMethodEvidence
2023-12-15Package PaymentPremium Fitness + Personal Training2023-12-15 to 2024-06-1518/24649.5 BHD✓ PaidCredit Card + + +
2024-02-15Package PaymentPremium Fitness + Personal Training2023-12-15 to 2024-06-1518/24649.5 BHD○ DueAuto-pay-
2023-06-15Package PaymentBasic Gym Membership2023-06-15 to 2023-12-15Unlimited599 BHD✓ PaidBank Transfer + +
2024-02-01Package PaymentNutrition Consultation Package2024-02-01 to 2024-05-010/6450 BHD○ DueCredit Card-
2024-01-08Service/ProductPersonal Training Session - Paid--75 BHD✓ PaidCredit Card-
2024-01-05Service/ProductProtein Supplement - Paid--45 BHD✓ PaidCash-
2024-01-01Service/ProductMonthly Membership - Due--99 BHD✓ PaidAuto-pay-
+
-
+
+ + +
+
+
Club Memberships
@if($relationship->dependent->memberClubs->count() > 0) -
+
@foreach($relationship->dependent->memberClubs as $club) -
-
-
{{ $club->club_name }}
- Status: {{ ucfirst($club->pivot->status) }} +
+
+
+
+
{{ $club->club_name }}
+ Status: {{ ucfirst($club->pivot->status) }} +
+ + {{ ucfirst($club->pivot->status) }} + +
- - {{ ucfirst($club->pivot->status) }} -
@endforeach
@@ -96,12 +489,10 @@
- -
-
-
Recent Invoices
-
-
+ +
+
+
Recent Invoices
@if($relationship->dependent->studentInvoices->count() > 0)
@@ -139,19 +530,357 @@
- @if($relationship->dependent->studentInvoices->count() > 5) - - @endif @else

No invoices found.

@endif
+ + +
+
+
+
Attendance Records
+

Attendance tracking coming soon...

+
+
+
+ + +
+ +
+
+
+ +
Health Tracking
+
+

Monitor health metrics and progress over time

+ + +
+ +
+
+ +
75
+ Weight (kg) +
+
+ + +
+
+ +
12.8%
+ Body Fat +
+
+ + +
+
+ +
68.2%
+ Body Water +
+
+ + +
+
+ +
70.5
+ Muscle Mass +
+
+ + +
+
+ +
3.7
+ Bone Mass +
+
+ + +
+
+ +
1895
+ BMR (cal) +
+
+
+
+
+ + +
+ +
+
+
+
Body Composition Analysis
+ +
+ +

Radar chart visualization coming soon...

+ Chart will compare current vs previous body composition metrics +
+
+
+
+ + +
+
+
+
Compare
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MetricCurrentChangePrevious
Weight75kg77kg
Body Fat12.8%14.5%
BMI22.523.2
Body Water68.2%65.8%
Muscle Mass70.5kg68.2kg
Bone Mass3.7kg3.6kg
Visceral Fat34
BMR1895cal1865cal
Protein19.8%18.9%
Body Age25yrs27yrs
+
+
+
+
+
+ + +
+
+
Health Tracking History
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DateWeight (kg)Body Fat %BMIBody Water %Muscle Mass (kg)Bone Mass (kg)Visceral FatBMRProtein %Body Age
Jan 08, 20247512.822.568.270.53.73189519.825
Jan 01, 20247714.523.265.868.23.64186518.927
Dec 25, 20237916.823.963.266.73.55184518.229
Dec 18, 20238219.224.660.864.73.47182017.631
Dec 11, 20238421.525.258.5633.48179517.133
+
+
+
+
+ + +
+
+
+
Goals & Progress
+

Goal tracking coming soon...

+
+
+
+ + +
+
+
+
Achievements & Badges
+

Achievement system coming soon...

+
+
+
+ + +
+
+
+
Tournament History
+

Tournament records coming soon...

+
+
+
+ + +
+
+
+
Event Participation
+

Event history coming soon...

+
+
+
+ + @endsection diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index 251829b..7c957ee 100644 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -18,6 +18,12 @@ + + + + + + + + @stack('styles')