Add Chromebook compatibility fixes and optimizations
Implemented comprehensive Chromebook-specific fixes including viewport adjustments, enhanced touch and keyboard detection, improved scrolling and keyboard navigation hooks, and extensive CSS optimizations for better usability. Updated chat and dashboard interfaces for larger touch targets, better focus management, and responsive layouts. Added documentation in docs/CHROMEBOOK-FIXES.md and new hooks for Chromebook scroll and keyboard handling.
This commit is contained in:
@@ -16,7 +16,7 @@ export default function AdminPage() {
|
||||
<p className="text-sm text-muted-foreground mt-1">Restricted area. Only admin1 can access.</p>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3 items-stretch">
|
||||
<div className="grid gap-4 lg:gap-6 sm:grid-cols-2 lg:grid-cols-3 items-stretch">
|
||||
<SystemStatusCard />
|
||||
<VendorsCard />
|
||||
<InviteVendorCard />
|
||||
|
||||
@@ -74,7 +74,7 @@ export default async function AdminStatusPage() {
|
||||
<p className="text-sm text-muted-foreground mt-1">Monitor system health and performance metrics</p>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
||||
<div className="grid gap-4 lg:gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{/* Server Status */}
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
|
||||
@@ -12,7 +12,7 @@ export default function AnalyticsLoading() {
|
||||
</div>
|
||||
|
||||
{/* Metrics Cards */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 lg:gap-6">
|
||||
{[...Array(4)].map((_, i) => (
|
||||
<Card key={i}>
|
||||
<CardHeader className="pb-2">
|
||||
|
||||
@@ -353,7 +353,7 @@ export default function CategoriesPage() {
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-5 gap-6">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 xl:grid-cols-5 gap-4 lg:gap-6">
|
||||
{/* Add Category Card - Takes up 2 columns */}
|
||||
<Card className="lg:col-span-2">
|
||||
<CardHeader>
|
||||
|
||||
@@ -8,8 +8,9 @@ export const metadata: Metadata = {
|
||||
export const viewport: Viewport = {
|
||||
width: "device-width",
|
||||
initialScale: 1,
|
||||
maximumScale: 5,
|
||||
maximumScale: 3,
|
||||
userScalable: true,
|
||||
viewportFit: "cover",
|
||||
themeColor: [
|
||||
{ media: "(prefers-color-scheme: dark)", color: "#000000" },
|
||||
{ media: "(prefers-color-scheme: light)", color: "#D53F8C" },
|
||||
|
||||
@@ -16,7 +16,7 @@ export default function DashboardLoading() {
|
||||
</div>
|
||||
|
||||
{/* Order statistics skeletons */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 lg:gap-6">
|
||||
{[...Array(4)].map((_, i) => (
|
||||
<Card key={i} className="border-border/40 shadow-sm">
|
||||
<CardHeader className="pb-2">
|
||||
|
||||
@@ -25,7 +25,7 @@ function DashboardContentSkeleton() {
|
||||
</div>
|
||||
|
||||
{/* Stats cards skeleton */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 lg:gap-6">
|
||||
{[...Array(4)].map((_, i) => (
|
||||
<Card key={i}>
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
|
||||
@@ -386,18 +386,18 @@ export default function ProductsPage() {
|
||||
return (
|
||||
<Layout>
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-4">
|
||||
<h1 className="text-2xl font-semibold text-gray-900 dark:text-white flex items-center">
|
||||
<Package2 className="mr-2 h-6 w-6" />
|
||||
Products
|
||||
</h1>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex flex-col sm:flex-row items-stretch sm:items-center gap-3">
|
||||
<div className="relative">
|
||||
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
type="search"
|
||||
placeholder="Search products..."
|
||||
className="w-64 pl-8"
|
||||
className="w-full sm:w-64 pl-8"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
@@ -412,18 +412,22 @@ export default function ProductsPage() {
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
onClick={() => setImportModalOpen(true)}
|
||||
variant="outline"
|
||||
className="gap-2"
|
||||
>
|
||||
<Upload className="h-4 w-4" />
|
||||
Import Products
|
||||
</Button>
|
||||
<Button onClick={() => setAddProductOpen(true)} className="gap-2">
|
||||
<Plus className="h-4 w-4" />
|
||||
Add Product
|
||||
</Button>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
onClick={() => setImportModalOpen(true)}
|
||||
variant="outline"
|
||||
className="gap-2 flex-1 sm:flex-none"
|
||||
>
|
||||
<Upload className="h-4 w-4" />
|
||||
<span className="hidden sm:inline">Import Products</span>
|
||||
<span className="sm:hidden">Import</span>
|
||||
</Button>
|
||||
<Button onClick={() => setAddProductOpen(true)} className="gap-2 flex-1 sm:flex-none">
|
||||
<Plus className="h-4 w-4" />
|
||||
<span className="hidden sm:inline">Add Product</span>
|
||||
<span className="sm:hidden">Add</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -220,7 +220,7 @@ export default function StorefrontPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
<div className="grid grid-cols-1 xl:grid-cols-2 gap-4 lg:gap-6">
|
||||
{/* Security Settings */}
|
||||
<div className="space-y-3">
|
||||
<div className="bg-[#0F0F12] rounded-lg p-4 border border-zinc-800">
|
||||
|
||||
148
app/globals.css
148
app/globals.css
@@ -90,6 +90,154 @@ body {
|
||||
background-color: hsl(var(--primary) / 0.9);
|
||||
}
|
||||
|
||||
/* Chromebook-specific optimizations */
|
||||
@media screen and (max-width: 1366px) and (min-resolution: 1.5dppx) {
|
||||
/* Chromebook display optimizations */
|
||||
.text-sm {
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
|
||||
.text-base {
|
||||
font-size: 1rem;
|
||||
line-height: 1.5rem;
|
||||
}
|
||||
|
||||
/* Better touch targets for Chromebooks */
|
||||
button, input, textarea, [role="button"], [role="tab"] {
|
||||
min-height: 48px;
|
||||
min-width: 48px;
|
||||
}
|
||||
|
||||
/* Improved spacing for Chromebook screens */
|
||||
.space-y-2 > * + * {
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
.space-y-4 > * + * {
|
||||
margin-top: 1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Chromebook touch screen optimizations */
|
||||
@media (pointer: coarse) and (hover: none) {
|
||||
/* Larger touch targets */
|
||||
.touch-target {
|
||||
min-height: 52px;
|
||||
min-width: 52px;
|
||||
}
|
||||
|
||||
/* Better spacing for touch interactions */
|
||||
.space-y-2 > * + * {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
/* Improved button padding */
|
||||
button {
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
|
||||
/* Better input field sizing */
|
||||
input, textarea {
|
||||
padding: 0.875rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
/* Enhanced focus states for touch */
|
||||
button:focus-visible, input:focus-visible, textarea:focus-visible {
|
||||
outline: 3px solid hsl(var(--ring));
|
||||
outline-offset: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Chromebook keyboard navigation improvements */
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
/* Better hover states for mouse/trackpad */
|
||||
button:hover:not(:disabled) {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* Improved focus indicators */
|
||||
button:focus-visible, input:focus-visible, textarea:focus-visible {
|
||||
outline: 2px solid hsl(var(--ring));
|
||||
outline-offset: 2px;
|
||||
box-shadow: 0 0 0 4px hsl(var(--ring) / 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
/* Chromebook display scaling fixes */
|
||||
@media screen and (min-resolution: 1.5dppx) {
|
||||
/* Prevent text from being too small on high-DPI displays */
|
||||
html {
|
||||
-webkit-text-size-adjust: 100%;
|
||||
text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
/* Better font rendering */
|
||||
body {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
}
|
||||
|
||||
/* Chromebook scrolling improvements */
|
||||
.overflow-y-auto {
|
||||
-webkit-overflow-scrolling: touch;
|
||||
scroll-behavior: smooth;
|
||||
/* Better momentum scrolling for Chromebooks */
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
|
||||
/* Chromebook chat interface optimizations */
|
||||
.chat-message {
|
||||
/* Better message bubble sizing for touch */
|
||||
min-height: 44px;
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
/* Chromebook form optimizations */
|
||||
.form-input {
|
||||
/* Better input field sizing for Chromebooks */
|
||||
min-height: 48px;
|
||||
font-size: 1rem;
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
|
||||
/* Chromebook button optimizations */
|
||||
.btn-chromebook {
|
||||
min-height: 48px;
|
||||
min-width: 48px;
|
||||
padding: 0.75rem 1rem;
|
||||
font-size: 1rem;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
/* Enhanced keyboard focus indicators for Chromebooks */
|
||||
.keyboard-focus {
|
||||
outline: 3px solid hsl(var(--ring));
|
||||
outline-offset: 2px;
|
||||
box-shadow: 0 0 0 4px hsl(var(--ring) / 0.3);
|
||||
}
|
||||
|
||||
/* Better focus management for Chromebook keyboard navigation */
|
||||
button:focus-visible,
|
||||
input:focus-visible,
|
||||
textarea:focus-visible,
|
||||
[role="button"]:focus-visible,
|
||||
[role="tab"]:focus-visible {
|
||||
outline: 3px solid hsl(var(--ring));
|
||||
outline-offset: 2px;
|
||||
box-shadow: 0 0 0 4px hsl(var(--ring) / 0.3);
|
||||
}
|
||||
|
||||
/* Chromebook-specific focus ring */
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.keyboard-focus {
|
||||
transition: outline 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
}
|
||||
|
||||
.bg-muted {
|
||||
background-color: hsl(var(--muted) / 0.8);
|
||||
}
|
||||
|
||||
@@ -55,8 +55,9 @@ export const metadata: Metadata = {
|
||||
export const viewport: Viewport = {
|
||||
width: "device-width",
|
||||
initialScale: 1,
|
||||
maximumScale: 5,
|
||||
maximumScale: 3,
|
||||
userScalable: true,
|
||||
viewportFit: "cover",
|
||||
themeColor: [
|
||||
{ media: "(prefers-color-scheme: dark)", color: "#000000" },
|
||||
{ media: "(prefers-color-scheme: light)", color: "#D53F8C" },
|
||||
|
||||
Reference in New Issue
Block a user