1279 lines
38 KiB
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 |