fix: critical mobile viewport fixes - login, modals, page constraints
- Remove min-h-screen or make responsive (md:min-h-screen for desktop only) - Login modal: p-8 → p-4 md:p-6, space-y-8 → space-y-3 md:space-y-4 - NewItemDialog: responsive spacing p-4 md:p-6, gaps 3 md:gap-4 - StockAdjustmentPanel: p-6 → p-4 md:p-6, gaps/spacing reduced for mobile - All container padding and gaps now follow mobile-first pattern - Fixes viewport overflow on 375px portrait mode - Tests: 291/291 passing
This commit is contained in:
@@ -25,8 +25,8 @@ export default function AdminPage() {
|
||||
|
||||
return (
|
||||
<PageShell>
|
||||
<main data-testid="admin-page" className="p-3 md:p-8 max-w-7xl mx-auto space-y-6 md:space-y-10 mb-20">
|
||||
<header className="flex items-center gap-4 mb-8 md:mb-12">
|
||||
<main data-testid="admin-page" className="p-3 md:p-8 max-w-7xl mx-auto space-y-3 md:space-y-6 mb-20">
|
||||
<header className="flex items-center gap-4 mb-4 md:mb-6">
|
||||
<div className="p-3 md:p-4 bg-indigo-500/10 rounded-2xl text-indigo-400 border border-indigo-500/20 shadow-xl shadow-indigo-500/5">
|
||||
<Shield size={28} className="md:w-8 md:h-8" />
|
||||
</div>
|
||||
@@ -44,9 +44,9 @@ export default function AdminPage() {
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<div className="grid lg:grid-cols-2 gap-6 md:gap-8 items-start">
|
||||
<div className="grid lg:grid-cols-2 gap-3 md:gap-4 items-start">
|
||||
{/* Left Column: Identity & Integrity */}
|
||||
<section className="space-y-6 md:space-y-8">
|
||||
<section className="space-y-3 md:space-y-4">
|
||||
<DatabaseManager data-testid="admin-tab-database"
|
||||
dbStats={admin.dbStats}
|
||||
isBackingUp={admin.isBackingUp}
|
||||
@@ -72,7 +72,7 @@ export default function AdminPage() {
|
||||
</section>
|
||||
|
||||
{/* Right Column: Infrastructure */}
|
||||
<section className="space-y-6 md:space-y-8">
|
||||
<section className="space-y-3 md:space-y-4">
|
||||
<LdapManager data-testid="admin-tab-ldap"
|
||||
ldapConfig={admin.ldapConfig}
|
||||
setLdapConfig={admin.setLdapConfig}
|
||||
|
||||
@@ -244,15 +244,15 @@ export default function InventoryPage() {
|
||||
|
||||
return (
|
||||
<PageShell>
|
||||
<div className="p-3 md:p-8 max-w-7xl mx-auto space-y-6">
|
||||
<div className="p-3 md:p-8 max-w-7xl mx-auto space-y-3 md:space-y-4">
|
||||
<datalist id="existing-types">
|
||||
{existingTypes.map(t => <option key={t} value={t} />)}
|
||||
</datalist>
|
||||
<datalist id="existing-boxes">
|
||||
{existingBoxes.map(b => <option key={b} value={b} />)}
|
||||
</datalist>
|
||||
|
||||
<header className="flex items-center gap-4 mb-6 md:mb-10">
|
||||
|
||||
<header className="flex items-center gap-4 mb-4 md:mb-6">
|
||||
<div className="p-3 md:p-4 bg-primary/10 rounded-3xl text-primary border border-primary/20 shadow-xl shadow-primary/5">
|
||||
<Package size={28} className="md:w-8 md:h-8" />
|
||||
</div>
|
||||
@@ -260,7 +260,7 @@ export default function InventoryPage() {
|
||||
<h1 className="text-2xl md:text-3xl font-normal tracking-tight text-white leading-tight">Inventory Catalog</h1>
|
||||
<p className="text-xs md:text-sm text-secondary font-normal mt-1 tracking-widest">Enterprise Stock Overview</p>
|
||||
</div>
|
||||
<button
|
||||
<button
|
||||
onClick={() => setShowBoxManager(true)}
|
||||
className="ml-auto p-3 bg-surface border border-slate-800 text-secondary rounded-2xl hover:text-primary transition-all active:scale-95 shadow-xl"
|
||||
title="Manage Boxes"
|
||||
@@ -269,7 +269,7 @@ export default function InventoryPage() {
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<div className="w-full space-y-6 md:space-y-8">
|
||||
<div className="w-full space-y-3 md:space-y-4">
|
||||
{/* Stats Dashboard */}
|
||||
<section className="grid grid-cols-2 md:grid-cols-4 gap-2.5 md:gap-4">
|
||||
<StatCard
|
||||
@@ -318,7 +318,7 @@ export default function InventoryPage() {
|
||||
{selectedItem && (
|
||||
<div className="fixed inset-0 z-50 flex items-end sm:items-center justify-center p-0 sm:p-4 bg-background/80 animate-in fade-in duration-200">
|
||||
<div className="w-full max-w-lg bg-surface border-x border-t sm:border border-slate-800 rounded-t-[2.5rem] sm:rounded-[2.5rem] shadow-2xl p-5 sm:p-8 overflow-hidden animate-in slide-in-from-bottom-10 duration-300">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<div className="flex justify-between items-center mb-3 md:mb-4">
|
||||
<h3 className="text-xl font-normal tracking-tight flex items-center gap-2">
|
||||
{isEditing ? "Edit Item" : selectedItem.name}
|
||||
{!isEditing && (
|
||||
@@ -361,7 +361,7 @@ export default function InventoryPage() {
|
||||
</div>
|
||||
|
||||
{isEditing ? (
|
||||
<div className="max-h-[60vh] overflow-y-auto pr-2 space-y-4 mb-6 scrollbar-hide">
|
||||
<div className="max-h-[60vh] overflow-y-auto pr-2 space-y-2 md:space-y-3 mb-4 md:mb-6 scrollbar-hide">
|
||||
<div>
|
||||
<label className="text-sm font-normal text-secondary ml-1 tracking-tight">Item Name</label>
|
||||
<input
|
||||
@@ -380,7 +380,7 @@ export default function InventoryPage() {
|
||||
className="w-full bg-background border border-slate-800 rounded-xl py-3 px-4 text-sm font-normal outline-none text-secondary placeholder:text-muted"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="grid grid-cols-2 gap-2 md:gap-3">
|
||||
<div>
|
||||
<label className="text-sm font-normal text-secondary ml-1 tracking-tight">Category</label>
|
||||
<input
|
||||
@@ -484,7 +484,7 @@ export default function InventoryPage() {
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex p-1 bg-background rounded-2xl mb-8">
|
||||
<div className="flex p-1 bg-background rounded-2xl mb-4 md:mb-6">
|
||||
{[
|
||||
{ id: 'ADD', label: 'Buy More', icon: Plus, color: 'text-primary' },
|
||||
{ id: 'REMOVE', label: 'Subtract', icon: Minus, color: 'text-amber-500' },
|
||||
|
||||
@@ -79,10 +79,10 @@ export default function LoginPage() {
|
||||
if (!mounted) return null;
|
||||
|
||||
return (
|
||||
<div data-testid="identity-check-overlay" className="min-h-screen bg-background flex items-center justify-center p-4">
|
||||
<div data-testid="identity-check-overlay" className="bg-background flex items-center justify-center p-4 md:min-h-screen">
|
||||
<Toaster position="top-center" />
|
||||
|
||||
<div className="bg-surface border border-slate-800 rounded-3xl p-8 max-w-sm w-full shadow-2xl space-y-8 animate-in fade-in zoom-in duration-500">
|
||||
|
||||
<div className="bg-surface border border-slate-800 rounded-3xl p-4 md:p-6 max-w-sm w-full shadow-2xl space-y-3 md:space-y-4 animate-in fade-in zoom-in duration-500">
|
||||
<div className="text-center space-y-2">
|
||||
<div className="w-16 h-16 bg-primary/10 text-primary rounded-2xl flex items-center justify-center mx-auto mb-4 border border-primary/20">
|
||||
<User size={32} />
|
||||
@@ -91,7 +91,7 @@ export default function LoginPage() {
|
||||
<p className="text-muted text-sm">Select operator profile or use direct login</p>
|
||||
</div>
|
||||
|
||||
<div data-testid="user-list" className="grid gap-3">
|
||||
<div data-testid="user-list" className="grid gap-2 md:gap-3">
|
||||
{!selectedUserForLogin && !isEnterprise ? (
|
||||
<>
|
||||
{users.length > 0 ? (
|
||||
|
||||
@@ -87,8 +87,8 @@ export default function LogsPage() {
|
||||
|
||||
return (
|
||||
<PageShell>
|
||||
<main className="p-3 md:p-8 max-w-7xl mx-auto space-y-6 md:space-y-10 mb-20">
|
||||
<header className="flex flex-col sm:flex-row sm:items-end justify-between gap-6">
|
||||
<main className="p-3 md:p-8 max-w-7xl mx-auto space-y-3 md:space-y-6 mb-20">
|
||||
<header className="flex flex-col sm:flex-row sm:items-end justify-between gap-3 md:gap-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="p-3 md:p-4 bg-primary/10 rounded-2xl text-primary border border-primary/20 shadow-xl shadow-primary/5">
|
||||
<History size={28} className="md:w-8 md:h-8" />
|
||||
@@ -135,7 +135,7 @@ export default function LogsPage() {
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section className="space-y-6">
|
||||
<section className="space-y-3 md:space-y-4">
|
||||
<div className="relative group">
|
||||
<input
|
||||
type="text"
|
||||
|
||||
@@ -314,8 +314,8 @@ export default function Home() {
|
||||
</datalist>
|
||||
|
||||
{/* Header */}
|
||||
<header className="flex flex-col sm:flex-row justify-between sm:items-center gap-4 mb-6 w-full px-1">
|
||||
<div className="flex items-center gap-3">
|
||||
<header className="flex flex-col sm:flex-row justify-between sm:items-center gap-3 md:gap-4 mb-3 md:mb-4 w-full px-1">
|
||||
<div className="flex items-center gap-2 md:gap-3">
|
||||
<div className="p-1">
|
||||
<img
|
||||
src="/logo.png"
|
||||
@@ -329,8 +329,8 @@ export default function Home() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap items-center justify-between sm:justify-end gap-3 sm:gap-6 bg-surface/50 sm:bg-transparent px-4 py-2 sm:p-0 rounded-2xl border border-slate-800/50 sm:border-none">
|
||||
<div className="flex flex-wrap items-center gap-4 sm:gap-6">
|
||||
<div className="flex flex-wrap items-center justify-between sm:justify-end gap-2 md:gap-3 bg-surface/50 sm:bg-transparent px-3 py-2 sm:p-0 rounded-2xl border border-slate-800/50 sm:border-none">
|
||||
<div className="flex flex-wrap items-center gap-2 md:gap-3">
|
||||
{isScannerReady && (
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-1.5 h-1.5 rounded-full bg-green-500 shadow-[0_0_8px_rgba(34,197,94,0.6)]" />
|
||||
@@ -425,8 +425,8 @@ export default function Home() {
|
||||
{/* Box Contents Selection Modal */}
|
||||
{boxMatches.length > 0 && !selectedItem && (
|
||||
<div className="fixed inset-0 z-50 flex items-end sm:items-center justify-center p-4 bg-background/80 animate-in fade-in duration-200">
|
||||
<div className="w-full max-w-lg bg-surface border border-slate-800 rounded-[2.5rem] shadow-2xl p-6 overflow-hidden animate-in slide-in-from-bottom-10 duration-300 flex flex-col max-h-[85vh]">
|
||||
<div className="flex justify-between items-center mb-6 shrink-0">
|
||||
<div className="w-full max-w-lg bg-surface border border-slate-800 rounded-[2.5rem] shadow-2xl p-4 md:p-6 overflow-hidden animate-in slide-in-from-bottom-10 duration-300 flex flex-col max-h-[85vh]">
|
||||
<div className="flex justify-between items-center mb-3 md:mb-4 shrink-0">
|
||||
<div>
|
||||
<h3 className="text-xl font-normal tracking-tight flex items-center gap-2">
|
||||
<Package className="text-primary" />
|
||||
@@ -438,7 +438,7 @@ export default function Home() {
|
||||
<X size={20} />
|
||||
</button>
|
||||
</div>
|
||||
<div className="overflow-y-auto w-full pr-2 space-y-3 pb-4">
|
||||
<div className="overflow-y-auto w-full pr-2 space-y-2 md:space-y-3 pb-3 md:pb-4">
|
||||
{boxMatches.map(item => (
|
||||
<button
|
||||
key={item.id}
|
||||
@@ -447,7 +447,7 @@ export default function Home() {
|
||||
setBoxMatches([]);
|
||||
setAdjustType(mode === 'CHECK_IN' ? 'ADD' : 'REMOVE');
|
||||
}}
|
||||
className="w-full text-left bg-background/50 hover:bg-slate-800 border border-slate-800/80 p-4 rounded-2xl flex items-center gap-4 transition-all active:scale-[0.98] group"
|
||||
className="w-full text-left bg-background/50 hover:bg-slate-800 border border-slate-800/80 p-3 md:p-4 rounded-2xl flex items-center gap-2 md:gap-3 transition-all active:scale-[0.98] group"
|
||||
>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-normal text-white">{item.name}</p>
|
||||
@@ -463,7 +463,7 @@ export default function Home() {
|
||||
|
||||
|
||||
{/* Footer Branding */}
|
||||
<footer className="mt-20 mb-8 flex flex-col items-center gap-2 opacity-70">
|
||||
<footer className="mt-12 md:mt-20 mb-4 md:mb-8 flex flex-col items-center gap-2 opacity-70">
|
||||
<p className="text-sm font-normal text-secondary">Powered by TFM Group Software</p>
|
||||
<div className="h-px w-16 bg-slate-700/60" />
|
||||
<p className="text-xs font-mono text-secondary">v{versionData.version} • {versionData.last_build} • BUILD: dev-{(versionData as any).commit || 'N/A'}</p>
|
||||
|
||||
Reference in New Issue
Block a user