Enhance user interface with mobile optimizations and visual improvements

Adds CSS keyframes and mobile-first responsive utilities in index.css, adjusts styles in NewSaleForm.tsx and SaleDetailsModal.tsx and SalesHistory.tsx.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 91318273-c764-4fd4-be04-bdc12c38af32
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/0715008c-7244-40f4-befc-26c014852236/b77c9129-16e4-4c03-a447-09edfb2aa967.jpg
This commit is contained in:
michaelschal committed 2025-07-09 21:32:33 +00:00
1 parent f64493d9bb
commit d3e263bf05
4 files changed
+377 -70

No files matched your search

+4 -4
View File
@@ -177,9 +177,9 @@ export default function NewSaleForm() {
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="mobile-form-responsive">
{/* Seller Information */}
<div className="modern-card bg-muted/30 mobile-form-section rounded-xl border-l-4 border-primary">
<div className="modern-card bg-muted bg-opacity-30 mobile-form-section rounded-xl border-l-4 border-primary">
<h3 className="mobile-header-responsive font-semibold text-foreground mb-4 sm:mb-6 flex items-center gap-2 sm:gap-3">
<div className="p-1.5 sm:p-2 bg-primary/10 rounded-lg">
<div className="p-1.5 sm:p-2 bg-primary bg-opacity-10 rounded-lg">
<User className="h-4 w-4 sm:h-5 sm:w-5 text-primary" />
</div>
<span className="truncate">Informations Vendeur</span>
@@ -215,9 +215,9 @@ export default function NewSaleForm() {
</div>
{/* Product Information */}
<div className="modern-card bg-blue-50/50 mobile-form-section rounded-xl border-l-4 border-blue-500">
<div className="modern-card bg-blue-50 bg-opacity-50 mobile-form-section rounded-xl border-l-4 border-blue-500">
<h3 className="mobile-header-responsive font-semibold text-foreground mb-4 sm:mb-6 flex items-center gap-2 sm:gap-3">
<div className="p-1.5 sm:p-2 bg-blue-500/10 rounded-lg">
<div className="p-1.5 sm:p-2 bg-blue-500 bg-opacity-10 rounded-lg">
<Package className="h-4 w-4 sm:h-5 sm:w-5 text-blue-600" />
</div>
<span className="truncate">Informations Produit</span>
+19 -19
View File
@@ -16,25 +16,25 @@ export default function SaleDetailsModal({ isOpen, sale, onClose }: SaleDetailsM
return (
<Dialog open={isOpen} onOpenChange={onClose}>
<DialogContent className="max-w-4xl max-h-[90vh] overflow-y-auto">
<DialogHeader>
<DialogTitle>Détails de la Vente</DialogTitle>
<DialogContent className="mobile-modal-content max-w-4xl max-h-[90vh] overflow-y-auto slide-in-up">
<DialogHeader className="mobile-padding">
<DialogTitle className="responsive-title">Détails de la Vente</DialogTitle>
</DialogHeader>
<div className="space-y-6">
<div className="mobile-spacing mobile-padding">
{/* Seller Information */}
<div className="border-l-4 border-primary pl-4">
<h4 className="font-medium text-gray-900 mb-2">Informations Vendeur</h4>
<div className="grid grid-cols-2 gap-4 text-sm">
<div className="mobile-form-section border-l-4 border-primary pl-4">
<h4 className="responsive-subtitle text-gray-900 mb-3">Informations Vendeur</h4>
<div className="mobile-form-row responsive-body">
<div><span className="font-medium">Vendeur:</span> {sale.vendeur}</div>
<div><span className="font-medium">Date de vente:</span> {formatDate(sale.dateVente)}</div>
</div>
</div>
{/* Product Information */}
<div className="border-l-4 border-info pl-4">
<h4 className="font-medium text-gray-900 mb-2">Informations Produit</h4>
<div className="grid grid-cols-2 gap-4 text-sm">
<div className="mobile-form-section border-l-4 border-blue-500 pl-4">
<h4 className="responsive-subtitle text-gray-900 mb-3">Informations Produit</h4>
<div className="mobile-form-row responsive-body">
<div><span className="font-medium">Type d'article:</span> {sale.typeArticle}</div>
<div>
<span className="font-medium">Catégorie:</span>{' '}
@@ -48,9 +48,9 @@ export default function SaleDetailsModal({ isOpen, sale, onClose }: SaleDetailsM
</div>
{/* Customer Information */}
<div className="border-l-4 border-success pl-4">
<h4 className="font-medium text-gray-900 mb-2">Informations Client</h4>
<div className="grid grid-cols-2 gap-4 text-sm">
<div className="mobile-form-section border-l-4 border-green-500 pl-4">
<h4 className="responsive-subtitle text-gray-900 mb-3">Informations Client</h4>
<div className="mobile-form-row responsive-body">
<div><span className="font-medium">Nom:</span> {sale.nom}</div>
<div><span className="font-medium">Prénom:</span> {sale.prenom}</div>
<div>
@@ -63,9 +63,9 @@ export default function SaleDetailsModal({ isOpen, sale, onClose }: SaleDetailsM
</div>
{/* Identity Document */}
<div className="border-l-4 border-yellow-500 pl-4">
<h4 className="font-medium text-gray-900 mb-2">Pièce d'Identité</h4>
<div className="grid grid-cols-2 gap-4 text-sm">
<div className="mobile-form-section border-l-4 border-yellow-500 pl-4">
<h4 className="responsive-subtitle text-gray-900 mb-3">Pièce d'Identité</h4>
<div className="mobile-form-row responsive-body">
<div><span className="font-medium">Type:</span> {sale.typeIdentite || 'Non spécifié'}</div>
<div><span className="font-medium">Numéro:</span> {sale.numeroIdentite || 'Non spécifié'}</div>
<div><span className="font-medium">Autorité de délivrance:</span> {sale.autoriteDelivrance || 'Non spécifiée'}</div>
@@ -77,9 +77,9 @@ export default function SaleDetailsModal({ isOpen, sale, onClose }: SaleDetailsM
</div>
{/* Photos */}
<div className="border-l-4 border-purple-500 pl-4">
<h4 className="font-medium text-gray-900 mb-2">Photos de la Pièce d'Identité</h4>
<div className="grid grid-cols-2 gap-4">
<div className="mobile-form-section border-l-4 border-purple-500 pl-4">
<h4 className="responsive-subtitle text-gray-900 mb-3">Photos de la Pièce d'Identité</h4>
<div className="mobile-form-row">
<div>
<span className="text-sm font-medium">Photo Recto:</span>
{sale.photoRecto ? (
+1 -1
View File
@@ -264,7 +264,7 @@ export default function SalesHistory({ canDelete = false }: SalesHistoryProps) {
</CardHeader>
<CardContent className="mobile-card-content mobile-form-responsive">
{/* Filters */}
<div className="flex flex-col sm:flex-row gap-3 sm:gap-4 items-start sm:items-end mobile-form-section bg-muted/30 rounded-xl border border-border/50">
<div className="flex flex-col sm:flex-row gap-3 sm:gap-4 items-start sm:items-end mobile-form-section bg-muted bg-opacity-30 rounded-xl border border-border border-opacity-50">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4 flex-1 w-full">
<div>
<label className="responsive-caption font-medium">Date de début</label>
+353 -46
View File
@@ -82,6 +82,124 @@
--gradient-accent: hsl(282, 100%, 65%);
}
/* Enhanced keyframes for mobile animations */
@keyframes slideInRight {
from {
opacity: 0;
transform: translateX(100%);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes slideInLeft {
from {
opacity: 0;
transform: translateX(-100%);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes slideInUp {
from {
opacity: 0;
transform: translateY(100%);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideInDown {
from {
opacity: 0;
transform: translateY(-100%);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes scaleIn {
from {
opacity: 0;
transform: scale(0.9);
}
to {
opacity: 1;
transform: scale(1);
}
}
@keyframes cardAppear {
from {
opacity: 0;
transform: translateY(20px) scale(0.95);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
@keyframes mobileModalEnter {
from {
opacity: 0;
transform: translateY(100%);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes mobileModalExit {
from {
opacity: 1;
transform: translateY(0);
}
to {
opacity: 0;
transform: translateY(100%);
}
}
@keyframes pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
@layer base {
* {
@apply border-border;
@@ -91,6 +209,42 @@
@apply font-sans antialiased bg-background text-foreground;
font-feature-settings: "rlig" 1, "calt" 1;
letter-spacing: -0.01em;
/* Optimize for mobile */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
/* Prevent horizontal scroll on mobile */
overflow-x: hidden;
}
/* Enhanced mobile viewport handling */
html {
/* Prevent zoom on double tap */
touch-action: manipulation;
/* Smooth scrolling */
scroll-behavior: smooth;
}
/* Disable text selection for UI elements */
.select-none {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* Improve touch interactions */
button,
[role="button"],
input[type="submit"],
input[type="button"] {
touch-action: manipulation;
-webkit-tap-highlight-color: transparent;
}
/* Focus styles for accessibility */
*:focus-visible {
outline: 2px solid hsl(var(--ring));
outline-offset: 2px;
}
/* Smooth scrollbar */
@@ -113,7 +267,52 @@
}
@layer components {
/* Modern animations */
/* Mobile-first responsive utilities */
.mobile-container {
@apply w-full max-w-7xl mx-auto px-4 sm:px-6 lg:px-8;
}
.mobile-grid {
@apply grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 sm:gap-6;
}
.mobile-stack {
@apply flex flex-col space-y-4 sm:space-y-6;
}
.mobile-row {
@apply flex flex-col sm:flex-row gap-4 sm:gap-6 items-start sm:items-center;
}
/* Responsive spacing */
.mobile-spacing {
@apply space-y-4 sm:space-y-6 lg:space-y-8;
}
.mobile-padding {
@apply p-4 sm:p-6 lg:p-8;
}
.mobile-margin {
@apply m-4 sm:m-6 lg:m-8;
}
/* Touch-friendly interactions */
.touch-target {
@apply min-h-[44px] min-w-[44px] flex items-center justify-center;
}
.touch-button {
@apply touch-target px-4 py-2 sm:px-6 sm:py-3 rounded-lg font-medium transition-all duration-200 ease-out;
-webkit-tap-highlight-color: transparent;
}
.touch-input {
@apply h-12 sm:h-10 px-4 text-base sm:text-sm transition-all duration-200 ease-out;
-webkit-appearance: none;
}
/* Modern animations with mobile optimization */
.slide-in-right {
animation: slideInRight 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
@@ -126,10 +325,18 @@
animation: slideInUp 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.slide-in-down {
animation: slideInDown 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.fade-in {
animation: fadeIn 0.3s ease-out;
}
.fade-in-up {
animation: fadeInUp 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.scale-in {
animation: scaleIn 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
@@ -137,6 +344,40 @@
.smooth-transition {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.smooth-transform {
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.mobile-smooth {
transition: all 0.2s ease-out;
}
/* Mobile-optimized states */
.mobile-hover {
@apply transition-all duration-200 ease-out;
}
.mobile-hover:hover {
@apply transform scale-[1.02];
}
.mobile-active {
@apply transition-all duration-100 ease-out;
}
.mobile-active:active {
@apply transform scale-[0.98];
}
/* Card animations */
.card-appear {
animation: cardAppear 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.card-hover {
@apply transition-all duration-300 ease-out hover:shadow-lg hover:scale-[1.01];
}
/* Modern gradients */
.gradient-bg {
@@ -150,6 +391,85 @@
background-clip: text;
}
/* Mobile navigation */
.mobile-nav {
@apply fixed bottom-0 left-0 right-0 bg-white bg-opacity-95 backdrop-blur-sm border-t border-border z-50;
@apply flex items-center justify-around py-2 sm:hidden;
}
.mobile-nav-item {
@apply flex flex-col items-center space-y-1 px-3 py-2 rounded-lg transition-all duration-200;
}
.mobile-nav-item.active {
@apply bg-primary bg-opacity-10 text-primary;
}
/* Typography responsive */
.responsive-title {
@apply text-lg sm:text-xl lg:text-2xl xl:text-3xl font-bold;
}
.responsive-subtitle {
@apply text-base sm:text-lg lg:text-xl font-semibold;
}
.responsive-body {
@apply text-sm sm:text-base;
}
.responsive-caption {
@apply text-xs sm:text-sm;
}
/* Form responsive styles */
.mobile-form {
@apply space-y-4 sm:space-y-6;
}
.mobile-form-group {
@apply space-y-2 sm:space-y-3;
}
.mobile-form-row {
@apply grid grid-cols-1 sm:grid-cols-2 gap-4 sm:gap-6;
}
.mobile-form-section {
@apply p-4 sm:p-6 bg-muted bg-opacity-30 rounded-xl space-y-4 sm:space-y-6;
}
/* Table responsive */
.mobile-table {
@apply overflow-x-auto -mx-4 sm:mx-0;
}
.mobile-table table {
@apply min-w-full;
}
.mobile-table-cell {
@apply px-2 sm:px-4 py-2 sm:py-3 text-xs sm:text-sm;
}
/* Modal responsive */
.mobile-modal {
@apply fixed inset-0 z-50 flex items-end sm:items-center justify-center;
}
.mobile-modal-content {
@apply w-full sm:w-auto sm:max-w-lg mx-4 sm:mx-0 bg-white rounded-t-xl sm:rounded-xl;
@apply max-h-[90vh] overflow-y-auto;
}
.mobile-modal-enter {
animation: mobileModalEnter 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.mobile-modal-exit {
animation: mobileModalExit 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Glass morphism */
.glass {
background: rgba(255, 255, 255, 0.05);
@@ -259,53 +579,40 @@
@apply p-4 sm:p-6 lg:p-8;
}
/* Animation keyframes */
@keyframes slideInRight {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slideInLeft {
from {
transform: translateX(-100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slideInUp {
from {
transform: translateY(30px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
/* Additional utility classes */
.mobile-form-responsive {
@apply space-y-6 sm:space-y-8;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
.mobile-gap-responsive {
@apply gap-4 sm:gap-6;
}
@keyframes scaleIn {
from {
opacity: 0;
transform: scale(0.95);
}
to {
opacity: 1;
transform: scale(1);
}
.mobile-header-responsive {
@apply text-base sm:text-lg lg:text-xl;
}
.touch-friendly-button {
@apply min-h-[44px] touch-target;
}
/* Modal responsive */
.mobile-modal-content {
@apply m-4 sm:m-6 max-w-[calc(100vw-2rem)] sm:max-w-[calc(100vw-3rem)];
}
/* Form group mobile */
.mobile-form-group {
@apply space-y-2;
}
/* Touch input */
.touch-input {
@apply min-h-[44px] px-3 py-2;
}
/* Responsive subtitle */
.responsive-subtitle {
@apply text-sm sm:text-base lg:text-lg font-semibold;
}
}