Files
Kent-logistics-Laravel/resources/views/admin/customers_add.blade.php

1279 lines
38 KiB
PHP

@extends('admin.layouts.app')
@section('page-title', 'Add Customer')
@section('content')
<style>
/* === ZOOM-SAFE CSS VARIABLES === */
.card-body {
overflow-y: auto;
scrollbar-width: none;
-ms-overflow-style: none;
}
.card-body::-webkit-scrollbar {
display: none;
}
:root {
/* Base sizing - 1rem = 16px at 100% zoom */
--base-font-size: clamp(0.875rem, 0.75rem + 0.5vw, 1rem);
--scale-factor: 1;
/* REDUCED SPACING SYSTEM - Fit content without scrolling */
--space-xxs: 0.0625rem; /* 1px */
--space-xs: 0.125rem; /* 2px */
--space-sm: 0.25rem; /* 4px */
--space-md: 0.375rem; /* 6px */
--space-lg: 0.5rem; /* 8px */
--space-xl: 0.75rem; /* 12px */
--space-xxl: 1rem; /* 16px */
/* Container padding - scales with viewport */
--container-padding: clamp(0.5rem, 2vw, 1rem);
/* Card dimensions */
--card-border-radius: clamp(0.5rem, 1vw, 0.75rem);
--card-padding: clamp(0.375rem, 1vw, 0.75rem);
/* Form elements - SPECIFIC HEIGHTS */
--input-height: clamp(1.875rem, 2.5vw, 2.25rem); /* 30px min, 36px max */
--input-padding: clamp(0.375rem, 0.75vw, 0.5rem);
--input-border-width: clamp(1px, 0.125vw, 2px);
/* Typography - fluid scales */
--font-size-xs: clamp(0.75rem, 0.75rem + 0.125vw, 0.8125rem);
--font-size-sm: clamp(0.8125rem, 0.8125rem + 0.125vw, 0.875rem);
--font-size-base: var(--base-font-size);
--font-size-lg: clamp(0.875rem, 0.875rem + 0.125vw, 1rem);
--font-size-xl: clamp(1rem, 1rem + 0.125vw, 1.125rem);
--font-size-xxl: clamp(1.125rem, 1.125rem + 0.25vw, 1.25rem);
/* Gradients and colors */
--primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
--success-gradient: linear-gradient(135deg, #10b981 0%, #059669 100%);
--glass-bg: #ffffff;
--glass-border: rgba(255, 255, 255, 0.2);
--shadow-soft: 0 2px 8px rgba(0, 0, 0, 0.06);
--shadow-medium: 0 4px 12px rgba(0, 0, 0, 0.08);
--shadow-strong: 0 8px 16px rgba(0, 0, 0, 0.1);
}
/* === BASE RESET FOR ZOOM SAFETY === */
html {
font-size: 100%;
-webkit-text-size-adjust: 100%;
text-size-adjust: 100%;
}
body {
font-size: var(--base-font-size);
line-height: 1.3;
min-height: 100vh;
overflow-x: hidden;
}
/* === CONTAINER SYSTEM === */
.container-fluid {
padding-left: var(--container-padding);
padding-right: var(--container-padding);
width: 100%;
max-width: 100%;
margin: 0 auto;
height: auto;
min-height: calc(100vh - 1.5rem);
}
.container-fluid.py-4 {
padding-top: 0.125rem !important;
}
/* === CARD SYSTEM === */
.card {
background: var(--glass-bg);
border-radius: var(--card-border-radius);
box-shadow: var(--shadow-strong);
border: 1px solid #e4e6ef;
animation: cardEntrance 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94);
position: relative;
overflow: hidden;
width: 100%;
min-width: 0;
margin-top: 0 !important;
margin-bottom: 0.25rem;
}
.card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(45deg,
rgba(102, 126, 234, 0.03) 0%,
rgba(118, 75, 162, 0.03) 50%,
rgba(16, 185, 129, 0.03) 100%);
pointer-events: none;
}
@keyframes cardEntrance {
0% {
opacity: 0;
transform: translateY(15px) scale(0.95);
}
100% {
opacity: 1;
transform: translateY(0) scale(1);
}
}
/* Premium Card Header */
.card-header {
background: var(--primary-gradient);
color: white;
border-bottom: none;
padding: clamp(0.5rem, 1.25vw, 0.875rem) clamp(0.75rem, 1.5vw, 1rem);
position: relative;
overflow: hidden;
}
.card-header::before {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: linear-gradient(45deg,
transparent 0%,
rgba(255, 255, 255, 0.1) 50%,
transparent 100%);
animation: headerShimmer 6s infinite linear;
transform: rotate(45deg);
}
@keyframes headerShimmer {
0% { transform: translateX(-100%) rotate(45deg); }
100% { transform: translateX(100%) rotate(45deg); }
}
.card-header h4 {
margin: 0;
font-weight: 800;
font-size: var(--font-size-xl);
position: relative;
display: flex;
align-items: center;
gap: var(--space-xs);
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
}
.card-header h4::before {
content: '✨';
font-size: 1.1em;
filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.1));
}
.card-body {
padding: var(--card-padding);
background: #f8fafc;
position: relative;
max-height: calc(100vh - 150px);
overflow-y: auto;
}
/* === FORM ELEMENTS === */
.form-label {
font-weight: 600;
color: #1e293b;
margin-bottom: 0.125rem !important; /* 🔽 remove gap */
padding-bottom: 0 !important;
line-height: 1; /* 🔽 tighter line */
min-height: auto !important; /* 🔽 remove hidden height */
font-size: var(--font-size-sm);
display: flex;
align-items: center;
gap: 0.125rem;
}
.form-label::before {
content: '';
width: 0.125rem;
height: 0.625rem;
background: var(--primary-gradient);
border-radius: 1px;
display: inline-block;
flex-shrink: 0;
}
/* TEXT INPUT BOXES */
input[type="text"].form-control,
input[type="email"].form-control,
input[type="tel"].form-control {
height: var(--input-height); /* 30px-36px */
min-height: var(--input-height);
border: var(--input-border-width) solid #e2e8f0;
border-radius: 0.375rem;
padding: var(--input-padding);
font-size: var(--font-size-sm);
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
background: #ffffff;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
position: relative;
width: 100%;
box-sizing: border-box;
margin-bottom: 0.125rem; /* 2px spacing below input */
}
/* SELECT BOXES */
.form-select {
height: var(--input-height); /* 30px-36px */
min-height: var(--input-height);
border: var(--input-border-width) solid #e2e8f0;
border-radius: 0.375rem;
padding: var(--input-padding);
font-size: var(--font-size-sm);
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
background: #ffffff;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
position: relative;
width: 100%;
box-sizing: border-box;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%23667eea' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
background-position: right clamp(0.5rem, 1vw, 0.75rem) center;
background-repeat: no-repeat;
background-size: clamp(0.625rem, 1vw, 0.75rem);
padding-right: clamp(1.5rem, 2.5vw, 2rem);
cursor: pointer;
appearance: none;
min-width: 0;
margin-bottom: 0.125rem; /* 2px spacing below select */
}
/* TEXTAREA */
textarea.form-control {
min-height: clamp(3rem, 6vw, 4rem); /* 48px-64px */
height: auto;
line-height: 1.3;
background: #ffffff;
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
max-width: 100%;
box-sizing: border-box;
margin-bottom: 0; /* 2px spacing below textarea */
border: var(--input-border-width) solid #e2e8f0;
border-radius: 0.375rem;
padding: var(--input-padding);
font-size: var(--font-size-sm);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
resize: vertical;
}
/* FOCUS STATES */
.form-control:focus, .form-select:focus {
border-color: #667eea;
box-shadow:
0 0 0 1px rgba(102, 126, 234, 0.15),
0 2px 6px rgba(102, 126, 234, 0.1),
inset 0 1px 0 rgba(255, 255, 255, 0.8);
background: #ffffff;
transform: translateY(-1px);
outline: none;
}
.form-control:hover, .form-select:hover {
border-color: #cbd5e1;
transform: translateY(-1px);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04);
}
/* === GRID SYSTEM - MINIMAL VERTICAL SPACING === */
.row.g-3 {
margin: 0 !important; /* Reduced negative margin */
display: flex;
flex-wrap: wrap;
}
.row.g-3 > [class*="col-"] {
padding: var(--space-xs) !important; /* Minimal padding */
animation: formElementEntrance 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
min-width: 0;
box-sizing: border-box;
margin-bottom: 0 !important; /* 2px between rows */
}
@keyframes formElementEntrance {
0% {
opacity: 0;
transform: translateY(10px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
/* Staggered Animation Delays */
.row.g-3 > [class*="col-"]:nth-child(1) { animation-delay: 0.05s; }
.row.g-3 > [class*="col-"]:nth-child(2) { animation-delay: 0.1s; }
.row.g-3 > [class*="col-"]:nth-child(3) { animation-delay: 0.15s; }
.row.g-3 > [class*="col-"]:nth-child(4) { animation-delay: 0.2s; }
.row.g-3 > [class*="col-"]:nth-child(5) { animation-delay: 0.25s; }
.row.g-3 > [class*="col-"]:nth-child(6) { animation-delay: 0.3s; }
/* === BUTTONS === */
/* CREATE CUSTOMER BUTTON */
.btn-success {
height: clamp(2rem, 3vw, 2.375rem); /* 32px-38px */
min-height: clamp(2rem, 3vw, 2.375rem);
background: var(--success-gradient);
border: none;
padding: clamp(0.5rem, 1vw, 0.625rem) clamp(0.75rem, 1.5vw, 1.125rem);
border-radius: 0.375rem;
color: white;
font-weight: 600;
font-size: var(--font-size-sm);
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: var(--shadow-medium);
position: relative;
overflow: hidden;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.25rem;
white-space: nowrap;
cursor: pointer;
text-decoration: none;
border: none;
}
/* CANCEL BUTTON */
.btn-secondary {
height: clamp(2rem, 3vw, 2.375rem); /* 32px-38px */
min-height: clamp(2rem, 3vw, 2.375rem);
background: #64748b;
border: none;
padding: clamp(0.5rem, 1vw, 0.625rem) clamp(0.75rem, 1.5vw, 1.125rem);
border-radius: 0.375rem;
color: white;
font-weight: 600;
font-size: var(--font-size-sm);
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: var(--shadow-soft);
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.25rem;
white-space: nowrap;
cursor: pointer;
text-decoration: none;
}
/* BUTTON HOVER EFFECTS */
.btn-success::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg,
transparent,
rgba(255, 255, 255, 0.2),
transparent);
transition: left 0.4s ease;
}
.btn-success:hover::before {
left: 100%;
}
.btn-success:hover {
transform: translateY(-1px);
box-shadow:
0 4px 12px rgba(16, 185, 129, 0.25),
0 0 0 1px rgba(255, 255, 255, 0.05);
}
.btn-success:active {
transform: translateY(0);
transition: all 0.1s ease;
}
.btn-secondary:hover {
background: #475569;
transform: translateY(-1px);
box-shadow: var(--shadow-medium);
}
/* Loading Animation */
.btn-success.loading {
pointer-events: none;
padding-right: clamp(1.5rem, 3vw, 2rem);
}
.btn-success.loading::after {
content: '';
position: absolute;
right: clamp(0.5rem, 1vw, 0.75rem);
top: 50%;
width: clamp(0.75rem, 1vw, 0.875rem);
height: clamp(0.75rem, 1vw, 0.875rem);
margin-top: -0.5rem;
border: 1.5px solid transparent;
border-top: 1.5px solid white;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Success message styles */
.success-message {
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
color: white;
padding: var(--space-md);
border-radius: 0.375rem;
margin-bottom: var(--space-md);
animation: slideDown 0.4s ease-out;
display: flex;
align-items: center;
gap: var(--space-sm);
box-shadow: var(--shadow-medium);
}
.success-message i {
font-size: 1.125em;
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* TEXTAREA FOCUS */
textarea.form-control:focus {
transform: translateY(-1px);
box-shadow:
0 0 0 1px rgba(102, 126, 234, 0.15),
0 3px 8px rgba(102, 126, 234, 0.1);
}
/* === VALIDATION STATES === */
.form-control:valid {
border-left: 1px solid #10b981;
}
.form-control:invalid:not(:focus):not(:placeholder-shown) {
border-left: 1px solid #ef4444;
animation: shake 0.3s ease-in-out;
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-1px); }
75% { transform: translateX(1px); }
}
/* === UTILITY CLASSES === */
.required-field::after {
content: '*';
color: #ef4444;
margin-left: 0.0625rem;
font-weight: 700;
}
.input-hint {
font-size: var(--font-size-xs);
color: #64748b;
margin-top: 0.0625rem; /* 1px */
display: flex;
align-items: center;
gap: 0.0625rem; /* 1px */
line-height: 1.2;
margin-bottom: 0.125rem; /* 2px */
}
.input-hint::before {
content: '💡';
font-size: 0.75em;
}
/* Success Animation */
@keyframes successPulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.005); }
}
.success-animation {
animation: successPulse 0.4s ease-in-out;
}
/* === FORM ACTIONS SECTION - MINIMAL SPACING === */
.d-flex.flex-column.flex-md-row {
gap: 0.375rem !important; /* Reduced from 0.5rem */
margin-top: 0.75rem !important; /* Reduced from 1rem */
padding-top: 0.375rem; /* Reduced from 0.5rem */
border-top: 1px solid #e2e8f0;
}
/* === RESPONSIVE ADJUSTMENTS === */
@media (max-width: 768px) {
:root {
--card-padding: clamp(0.375rem, 1.5vw, 0.5rem);
--container-padding: clamp(0.375rem, 1.5vw, 0.5rem);
}
.row.g-3 {
margin: calc(var(--space-xs) * -0.25) !important; /* Further reduced */
}
.row.g-3 > [class*="col-"] {
padding: var(--space-xs) !important;
margin-bottom: 0.0625rem !important; /* 1px on mobile */
}
.card-header {
padding: var(--space-md) var(--space-sm);
}
.d-flex.flex-column.flex-md-row {
flex-direction: column;
gap: 0.25rem !important; /* Even tighter on mobile */
}
.btn-success,
.btn-secondary {
width: 100%;
margin-bottom: var(--space-xs);
}
.btn-success {
order: 1;
}
.btn-secondary {
order: 2;
}
.form-label {
font-size: var(--font-size-xs);
}
.input-hint {
font-size: 0.6875rem;
}
/* Stack form fields on mobile with minimal spacing */
.col-12 {
margin-bottom: 0.125rem !important;
}
}
@media (max-width: 576px) {
:root {
--base-font-size: 0.8125rem;
--input-height: 1.875rem; /* 30px on mobile */
--space-sm: 0.1875rem; /* 3px */
}
.card-header h4 {
font-size: var(--font-size-lg);
flex-wrap: wrap;
gap: var(--space-xs);
}
.form-label {
font-size: 0.75rem;
margin-bottom: 0.03125rem; /* 0.5px */
}
.card-body {
padding: var(--space-sm);
max-height: calc(100vh - 140px);
}
.row.g-3 > [class*="col-"] {
margin-bottom: 0.0625rem !important; /* 1px */
}
/* Mobile button heights */
.btn-success,
.btn-secondary {
height: 2.125rem; /* 34px on mobile */
min-height: 2.125rem;
}
/* Reduce hint text size further on mobile */
.input-hint {
font-size: 0.625rem;
line-height: 1.1;
margin-top: 0.03125rem;
margin-bottom: 0.0625rem;
}
}
@media (min-width: 769px) and (max-width: 992px) {
:root {
--card-padding: clamp(0.5rem, 1.25vw, 0.75rem);
}
.row.g-3 > [class*="col-md-6"] {
flex: 0 0 100%;
max-width: 100%;
}
.row.g-3 > [class*="col-"] {
margin-bottom: 0.1875rem !important; /* 3px on tablet */
}
}
@media (min-width: 1200px) {
:root {
--container-padding: clamp(0.75rem, 2vw, 1.5rem);
}
.card-body {
max-height: calc(100vh - 180px);
}
/* Slightly more spacing on large screens */
.row.g-3 > [class*="col-"] {
margin-bottom: 0.1875rem !important; /* 3px */
}
}
/* High DPI/Retina adjustments */
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
.form-control, .form-select {
border-width: 1px;
}
.btn-success,
.btn-secondary {
border-width: 1px;
}
}
/* === ACCESSIBILITY & PERFORMANCE === */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* Focus visibility for keyboard navigation */
.form-control:focus-visible,
.form-select:focus-visible,
.btn-success:focus-visible,
.btn-secondary:focus-visible {
outline: 1px solid #667eea;
outline-offset: 1px;
}
/* Prevent text size adjustment on orientation change */
html {
-webkit-text-size-adjust: none;
text-size-adjust: none;
}
/* Ensure images/icons scale properly */
img, svg, i[class^="bi-"] {
max-width: 100%;
height: auto;
display: inline-block;
vertical-align: middle;
font-size: 1em;
}
/* Icon sizing */
.bi {
font-size: 0.9375em;
width: 0.9375em;
height: 0.9375em;
}
/* === LAYOUT UTILITIES === */
.text-end {
text-align: end;
}
.me-2 {
margin-right: 0.25rem !important; /* Reduced */
}
.me-3 {
margin-right: 0.375rem !important; /* Reduced */
}
.mt-4 {
margin-top: 0.75rem !important; /* Reduced */
}
/* ===== HIDE VERTICAL SCROLLBAR ===== */
html,
body {
overflow-y: auto;
scrollbar-width: none;
-ms-overflow-style: none;
}
html::-webkit-scrollbar,
body::-webkit-scrollbar {
width: 0;
display: none;
}
/* Fix for nested card container */
.card .card {
margin: 0;
box-shadow: none;
border: none;
background: transparent;
}
.card .card .card-header {
border-radius: var(--card-border-radius) var(--card-border-radius) 0 0;
}
/* Ensure all inputs are properly sized */
input[type="text"],
input[type="email"],
input[type="tel"],
textarea,
select {
max-width: 100%;
box-sizing: border-box;
}
/* Improve touch targets on mobile */
@media (hover: none) and (pointer: coarse) {
.form-control,
.form-select,
.btn-success,
.btn-secondary {
min-height: clamp(2.125rem, 4vw, 2.5rem);
}
input,
select,
textarea {
font-size: 16px;
}
}
/* Print styles */
@media print {
.card {
box-shadow: none;
border: 1px solid #ddd;
}
.btn-success,
.btn-secondary {
display: none;
}
.row.g-3 > [class*="col-"] {
padding: 0.0625rem !important;
margin-bottom: 0.0625rem !important;
}
}
/* Error message spacing */
.text-danger.mt-1 {
margin-top: 0.0625rem !important; /* 1px */
font-size: var(--font-size-xs);
margin-bottom: 0.125rem; /* 2px */
line-height: 1.2;
}
/* Alert message spacing */
.alert {
margin-bottom: 0.5rem !important;
padding: 0.375rem 0.5rem !important;
font-size: var(--font-size-sm);
}
/* Custom class for very tight spacing when needed */
.tight-spacing {
margin-top: -0.125rem;
margin-bottom: -0.125rem;
}
/* Additional class to remove all vertical spacing */
.no-vertical-spacing {
margin-top: 0 !important;
margin-bottom: 0 !important;
padding-top: 0 !important;
padding-bottom: 0 !important;
}
/* Compact form field wrapper */
.compact-field {
margin-top: 0.5rem !important; /* 🔥 ADD THIS */
margin-bottom: 0.5rem !important; /* 🔽 tighter */
}
.compact-field .form-label {
margin-bottom: 0.03125rem;
}
.compact-field .form-control,
.compact-field .form-select {
margin-bottom: 0.0625rem;
}
.compact-field .input-hint,
.compact-field .text-danger {
margin-top: 0.03125rem;
margin-bottom: 0.0625rem;
}
</style>
<div class="container-fluid py-4">
<!-- Main card container -->
<div class="card">
<!-- Premium Card Header -->
<div class="card-header">
<h4>
<i class="bi bi-person-plus-fill me-2"></i>
Add New Customer
</h4>
</div>
<!-- Card Body -->
<div class="card-body">
<!-- Success Message (hidden by default) -->
<div id="successMessage" class="success-message" style="display: none;">
<i class="bi bi-check-circle-fill"></i>
<div>
<strong>Customer added successfully!</strong>
<p class="mb-0">Redirecting to customers list...</p>
</div>
</div>
<!-- Error Message (hidden by default) -->
<div id="errorMessage" class="alert alert-danger" style="display: none;">
<i class="bi bi-exclamation-circle-fill me-2"></i>
<span id="errorText"></span>
</div>
<form action="{{ route('admin.customers.store') }}" method="POST" id="customerForm">
@csrf
<div class="row g-3">
<!-- Customer Name -->
<div class="col-12 col-md-6 compact-field">
<label for="customer_name" class="form-label required-field">Customer Name</label>
<input type="text"
id="customer_name"
name="customer_name"
class="form-control"
placeholder="Enter full name"
required
pattern="[A-Za-z\s]{2,}"
aria-label="Customer Name"
aria-describedby="customerNameHint"
value="{{ old('customer_name') }}">
<div id="customerNameHint" class="input-hint">Minimum 2 characters, letters only</div>
@error('customer_name')
<div class="text-danger mt-1">{{ $message }}</div>
@enderror
</div>
<!-- Company Name -->
<div class="col-12 col-md-6 compact-field">
<label for="company_name" class="form-label">Company Name</label>
<input type="text"
id="company_name"
name="company_name"
class="form-control"
placeholder="Enter company name"
aria-label="Company Name"
value="{{ old('company_name') }}">
@error('company_name')
<div class="text-danger mt-1">{{ $message }}</div>
@enderror
</div>
<!-- Designation -->
<div class="col-12 col-md-6 compact-field">
<label for="designation" class="form-label">Designation</label>
<input type="text"
id="designation"
name="designation"
class="form-control"
placeholder="Enter job title"
aria-label="Designation"
value="{{ old('designation') }}">
@error('designation')
<div class="text-danger mt-1">{{ $message }}</div>
@enderror
</div>
<!-- Email -->
<div class="col-12 col-md-6 compact-field">
<label for="email" class="form-label required-field">Email Address</label>
<input type="email"
id="email"
name="email"
class="form-control"
placeholder="Enter email address"
required
pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$"
aria-label="Email Address"
aria-describedby="emailHint"
value="{{ old('email') }}">
<div id="emailHint" class="input-hint">Valid email format required</div>
@error('email')
<div class="text-danger mt-1">{{ $message }}</div>
@enderror
</div>
<!-- Mobile -->
<div class="col-12 col-md-6 compact-field">
<label for="mobile_no" class="form-label required-field">Mobile Number</label>
<input type="tel"
id="mobile_no"
name="mobile_no"
class="form-control"
placeholder="Enter mobile number"
required
pattern="[0-9]{10}"
aria-label="Mobile Number"
aria-describedby="mobileHint"
value="{{ old('mobile_no') }}">
<div id="mobileHint" class="input-hint">10 digits without spaces</div>
@error('mobile_no')
<div class="text-danger mt-1">{{ $message }}</div>
@enderror
</div>
<!-- Pincode -->
<div class="col-12 col-md-6 compact-field">
<label for="pincode" class="form-label">Pincode</label>
<input type="text"
id="pincode"
name="pincode"
class="form-control"
placeholder="Enter pincode"
pattern="[0-9]{6}"
aria-label="Pincode"
aria-describedby="pincodeHint"
value="{{ old('pincode') }}">
<div id="pincodeHint" class="input-hint">6-digit pincode</div>
@error('pincode')
<div class="text-danger mt-1">{{ $message }}</div>
@enderror
</div>
<!-- Address -->
<div class="col-12 compact-field">
<label for="address" class="form-label">Address</label>
<textarea id="address"
name="address"
class="form-control"
rows="2"
placeholder="Enter complete address"
aria-label="Address">{{ old('address') }}</textarea>
@error('address')
<div class="text-danger mt-1">{{ $message }}</div>
@enderror
</div>
<!-- Customer Type -->
<div class="col-12 col-md-6 compact-field">
<label for="customer_type" class="form-label required-field">Customer Type</label>
<select id="customer_type"
name="customer_type"
class="form-select"
required
aria-label="Customer Type"
aria-describedby="customerTypeHint">
<option value="">Select type</option>
<option value="regular" {{ old('customer_type') == 'regular' ? 'selected' : '' }}>Regular</option>
<option value="premium" {{ old('customer_type') == 'premium' ? 'selected' : '' }}>Premium</option>
</select>
<div id="customerTypeHint" class="input-hint">Premium customers get special benefits</div>
@error('customer_type')
<div class="text-danger mt-1">{{ $message }}</div>
@enderror
</div>
<!-- Status -->
<div class="col-12 col-md-6 compact-field">
<label for="status" class="form-label required-field">Status</label>
<select id="status"
name="status"
class="form-select"
required
aria-label="Status"
aria-describedby="statusHint">
<option value="">Select status</option>
<option value="active" {{ old('status') == 'active' ? 'selected' : '' }}>Active</option>
<option value="inactive" {{ old('status') == 'inactive' ? 'selected' : '' }}>Inactive</option>
</select>
<div id="statusHint" class="input-hint">Active customers can place orders</div>
@error('status')
<div class="text-danger mt-1">{{ $message }}</div>
@enderror
</div>
</div>
<!-- Form Actions -->
<div class="d-flex flex-column flex-md-row justify-content-md-end align-items-stretch align-items-md-center gap-2 gap-md-3 mt-4">
<!-- Cancel Button -->
<a href="{{ route('admin.customers.index') }}" class="btn btn-secondary">
<i class="bi bi-arrow-left me-2"></i>
Cancel
</a>
<!-- Create Customer Button -->
<button type="submit" class="btn-success" id="submitBtn">
<i class="bi bi-person-plus me-2"></i>
Create Customer
</button>
</div>
</form>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const form = document.getElementById('customerForm');
const submitBtn = document.getElementById('submitBtn');
const successMessage = document.getElementById('successMessage');
const errorMessage = document.getElementById('errorMessage');
const errorText = document.getElementById('errorText');
// Store original button content
const originalBtnContent = submitBtn.innerHTML;
// Form submission handler
form.addEventListener('submit', function(e) {
// Add loading state
submitBtn.classList.add('loading');
submitBtn.innerHTML = '<i class="bi bi-hourglass-split me-2"></i>Creating...';
// Simulate form processing
setTimeout(() => {
submitBtn.classList.remove('loading');
submitBtn.classList.add('success-animation');
submitBtn.innerHTML = '<i class="bi bi-check-circle me-2"></i>Customer Created!';
setTimeout(() => {
submitBtn.classList.remove('success-animation');
submitBtn.innerHTML = '<i class="bi bi-person-plus me-2"></i>Create Customer';
}, 1500);
}, 1000);
});
// Traditional form submission fallback (if JavaScript fails)
form.setAttribute('onsubmit', 'return validateFormOnSubmit()');
function validateForm() {
let isValid = true;
const requiredFields = form.querySelectorAll('[required]');
// Clear previous error highlights
form.querySelectorAll('.is-invalid').forEach(el => {
el.classList.remove('is-invalid');
});
// Validate each required field
requiredFields.forEach(field => {
if (!field.value.trim()) {
field.classList.add('is-invalid');
isValid = false;
} else if (field.type === 'email' && !validateEmail(field.value)) {
field.classList.add('is-invalid');
isValid = false;
} else if (field.pattern && !new RegExp(field.pattern).test(field.value)) {
field.classList.add('is-invalid');
isValid = false;
}
});
return isValid;
}
function validateEmail(email) {
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return re.test(email);
}
function resetSubmitButton() {
submitBtn.classList.remove('loading');
submitBtn.innerHTML = originalBtnContent;
submitBtn.disabled = false;
}
// Real-time validation with improved UX
const inputs = form.querySelectorAll('input[required], select[required]');
inputs.forEach(input => {
input.addEventListener('blur', function() {
validateField(this);
});
input.addEventListener('input', function() {
// Remove validation styling while user is typing
if (this.value) {
this.classList.remove('is-invalid');
this.style.borderLeft = '';
}
});
});
function validateField(field) {
const hint = field.nextElementSibling?.nextElementSibling;
if (field.value.trim() === '') {
field.classList.add('is-invalid');
field.style.borderLeft = '1px solid #ef4444';
if (hint && hint.classList.contains('input-hint')) {
hint.innerHTML = `<span class="text-danger">❌ This field is required</span>`;
}
} else if (field.checkValidity()) {
field.classList.remove('is-invalid');
field.style.borderLeft = '1px solid #10b981';
if (hint && hint.classList.contains('input-hint')) {
hint.innerHTML = hint.getAttribute('data-original-hint') || hint.innerHTML;
}
} else {
field.classList.add('is-invalid');
field.style.borderLeft = '1px solid #ef4444';
if (hint && hint.classList.contains('input-hint')) {
const errorMsg = getErrorMessage(field);
hint.innerHTML = `<span class="text-danger">❌ ${errorMsg}</span>`;
}
}
}
function getErrorMessage(field) {
if (field.type === 'email') {
return 'Please enter a valid email address';
}
if (field.pattern === '[0-9]{10}') {
return 'Please enter 10 digits only';
}
if (field.pattern === '[A-Za-z\\s]{2,}') {
return 'Minimum 2 letters, no numbers or special characters';
}
return 'Please check this field';
}
// Store original hint text
document.querySelectorAll('.input-hint').forEach(hint => {
hint.setAttribute('data-original-hint', hint.innerHTML);
});
// Enhanced input interactions
const formControls = form.querySelectorAll('.form-control, .form-select');
formControls.forEach(control => {
control.addEventListener('focus', function() {
this.style.transform = 'translateY(-1px)';
this.parentElement.style.zIndex = '1';
});
control.addEventListener('blur', function() {
this.style.transform = 'translateY(0)';
this.parentElement.style.zIndex = '0';
});
});
// Auto-format mobile number
const mobileInput = document.getElementById('mobile_no');
if (mobileInput) {
mobileInput.addEventListener('input', function(e) {
let value = e.target.value.replace(/\D/g, '');
if (value.length > 10) {
value = value.substring(0, 10);
}
e.target.value = value;
});
}
// Auto-format pincode
const pincodeInput = document.getElementById('pincode');
if (pincodeInput) {
pincodeInput.addEventListener('input', function(e) {
let value = e.target.value.replace(/\D/g, '');
if (value.length > 6) {
value = value.substring(0, 6);
}
e.target.value = value;
});
}
// Handle zoom level detection and adjustment
function checkZoomLevel() {
const visualViewport = window.visualViewport || window;
const layoutViewport = window;
// Calculate zoom level (approximate)
const zoom = Math.round((visualViewport.width / layoutViewport.innerWidth) * 100);
// Adjust spacing for different zoom levels
const root = document.documentElement;
if (zoom > 110) {
// High zoom - reduce spacing further
root.style.setProperty('--space-sm', '0.1875rem');
root.style.setProperty('--input-padding', '0.25rem');
root.style.setProperty('--card-padding', '0.5rem');
} else if (zoom < 80) {
// Low zoom - slightly increase spacing
root.style.setProperty('--space-sm', '0.375rem');
root.style.setProperty('--input-padding', '0.5rem');
root.style.setProperty('--card-padding', '0.75rem');
} else {
// Normal zoom - reset to defaults
root.style.setProperty('--space-sm', '0.25rem');
root.style.setProperty('--input-padding', 'clamp(0.375rem, 0.75vw, 0.5rem)');
root.style.setProperty('--card-padding', 'clamp(0.375rem, 1vw, 0.75rem)');
}
}
// Initial check
checkZoomLevel();
// Check on resize and zoom
window.addEventListener('resize', checkZoomLevel);
if (window.visualViewport) {
window.visualViewport.addEventListener('resize', checkZoomLevel);
}
});
// Global validation function for traditional form submission
function validateFormOnSubmit() {
const form = document.getElementById('customerForm');
const requiredFields = form.querySelectorAll('[required]');
let isValid = true;
requiredFields.forEach(field => {
if (!field.value.trim()) {
field.classList.add('is-invalid');
isValid = false;
}
});
return isValid;
}
</script>
@endsection