built user profile added the logo created the family view etc
This commit is contained in:
1 parent
4a7353e3fc
commit
43c2676d6f
33 files changed
+3783
-257
No files matched your search
@@ -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
|
||||
@@ -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
|
||||
@@ -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/[email protected]/css/flag-icons.min.css">
|
||||
|
||||
<!-- Select2 CSS (for nationality dropdown) -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/[email protected]/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/[email protected]/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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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>© {{ date('Y') }} {{ config('app.name', 'Club SaaS') }}. All rights reserved.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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>© {{ 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>
|
||||
@@ -0,0 +1,161 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<!-- Flag Icons CSS -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/css/flag-icons.min.css">
|
||||
|
||||
<!-- Select2 CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/[email protected]/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><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></code></pre>
|
||||
|
||||
<h5 class="mt-4">2. Nationality Dropdown</h5>
|
||||
<pre class="bg-light p-3 rounded"><code><x-nationality-dropdown
|
||||
name="nationality"
|
||||
id="nationality"
|
||||
:value="old('nationality')"
|
||||
:required="true"
|
||||
:error="$errors->first('nationality')" /></code></pre>
|
||||
|
||||
<h5 class="mt-4">3. Required Dependencies</h5>
|
||||
<pre class="bg-light p-3 rounded"><code><!-- CSS -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/css/flag-icons.min.css">
|
||||
<link href="https://cdn.jsdelivr.net/npm/[email protected]/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/[email protected]/dist/js/select2.min.js"></script>
|
||||
|
||||
<!-- Component Scripts -->
|
||||
@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/[email protected]/dist/js/select2.min.js"></script>
|
||||
|
||||
<!-- Load component scripts -->
|
||||
@stack('styles')
|
||||
@stack('scripts')
|
||||
@endsection
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -18,6 +18,12 @@
|
||||
<!-- Bootstrap Icons -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.css">
|
||||
|
||||
<!-- Flag Icons -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/css/flag-icons.min.css">
|
||||
|
||||
<!-- Select2 CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/[email protected]/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/[email protected]/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/[email protected]/dist/js/select2.min.js"></script>
|
||||
|
||||
@stack('scripts')
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user