From fbb87c8b0e0760768ab3d8c83009d14df8eb2ce0 Mon Sep 17 00:00:00 2001 From: PEDZEO Date: Tue, 20 Jan 2026 01:11:42 +0300 Subject: [PATCH 1/7] Refactor PromoDiscountBadge component: simplify button styling, remove unnecessary animations, and adjust layout for improved user experience --- src/components/PromoDiscountBadge.tsx | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/components/PromoDiscountBadge.tsx b/src/components/PromoDiscountBadge.tsx index a210fe0..cf53f41 100644 --- a/src/components/PromoDiscountBadge.tsx +++ b/src/components/PromoDiscountBadge.tsx @@ -88,19 +88,12 @@ export default function PromoDiscountBadge() { {/* Badge button */} {/* Dropdown - mobile: fixed centered, desktop: absolute right */} From 6048d90e46dd6a30a1e62580b75027ac644aa1c5 Mon Sep 17 00:00:00 2001 From: PEDZEO Date: Tue, 20 Jan 2026 01:21:40 +0300 Subject: [PATCH 2/7] Enhance Layout and useTelegramWebApp hook: add contentSafeAreaInset state and adjust header padding for fullscreen mode to improve layout responsiveness. --- src/components/layout/Layout.tsx | 10 ++++++++-- src/hooks/useTelegramWebApp.ts | 14 ++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/components/layout/Layout.tsx b/src/components/layout/Layout.tsx index e7af97f..f4c76bb 100644 --- a/src/components/layout/Layout.tsx +++ b/src/components/layout/Layout.tsx @@ -142,7 +142,7 @@ export default function Layout({ children }: LayoutProps) { const [mobileMenuOpen, setMobileMenuOpen] = useState(false) const { toggleTheme, isDark } = useTheme() const [userPhotoUrl, setUserPhotoUrl] = useState(null) - const { isTelegramWebApp, isFullscreen, isFullscreenSupported, toggleFullscreen } = useTelegramWebApp() + const { isTelegramWebApp, isFullscreen, isFullscreenSupported, toggleFullscreen, safeAreaInset, contentSafeAreaInset } = useTelegramWebApp() // Fetch enabled themes from API - same source of truth as AdminSettings const { data: enabledThemes } = useQuery({ @@ -300,7 +300,13 @@ export default function Layout({ children }: LayoutProps) { {/* Header */} -
+
{/* Logo */} diff --git a/src/hooks/useTelegramWebApp.ts b/src/hooks/useTelegramWebApp.ts index 6f2b9a0..e8402b4 100644 --- a/src/hooks/useTelegramWebApp.ts +++ b/src/hooks/useTelegramWebApp.ts @@ -8,6 +8,7 @@ export function useTelegramWebApp() { const [isFullscreen, setIsFullscreen] = useState(false) const [isTelegramWebApp, setIsTelegramWebApp] = useState(false) const [safeAreaInset, setSafeAreaInset] = useState({ top: 0, bottom: 0, left: 0, right: 0 }) + const [contentSafeAreaInset, setContentSafeAreaInset] = useState({ top: 0, bottom: 0, left: 0, right: 0 }) const webApp = typeof window !== 'undefined' ? window.Telegram?.WebApp : undefined @@ -20,6 +21,7 @@ export function useTelegramWebApp() { setIsTelegramWebApp(true) setIsFullscreen(webApp.isFullscreen || false) setSafeAreaInset(webApp.safeAreaInset || { top: 0, bottom: 0, left: 0, right: 0 }) + setContentSafeAreaInset(webApp.contentSafeAreaInset || { top: 0, bottom: 0, left: 0, right: 0 }) // Expand WebApp to full height webApp.expand() @@ -29,12 +31,23 @@ export function useTelegramWebApp() { const handleFullscreenChanged = () => { setIsFullscreen(webApp.isFullscreen || false) setSafeAreaInset(webApp.safeAreaInset || { top: 0, bottom: 0, left: 0, right: 0 }) + setContentSafeAreaInset(webApp.contentSafeAreaInset || { top: 0, bottom: 0, left: 0, right: 0 }) + } + + // Listen for safe area changes + const handleSafeAreaChanged = () => { + setSafeAreaInset(webApp.safeAreaInset || { top: 0, bottom: 0, left: 0, right: 0 }) + setContentSafeAreaInset(webApp.contentSafeAreaInset || { top: 0, bottom: 0, left: 0, right: 0 }) } webApp.onEvent('fullscreenChanged', handleFullscreenChanged) + webApp.onEvent('safeAreaChanged', handleSafeAreaChanged) + webApp.onEvent('contentSafeAreaChanged', handleSafeAreaChanged) return () => { webApp.offEvent('fullscreenChanged', handleFullscreenChanged) + webApp.offEvent('safeAreaChanged', handleSafeAreaChanged) + webApp.offEvent('contentSafeAreaChanged', handleSafeAreaChanged) } }, [webApp]) @@ -86,6 +99,7 @@ export function useTelegramWebApp() { isFullscreen, isFullscreenSupported, safeAreaInset, + contentSafeAreaInset, requestFullscreen, exitFullscreen, toggleFullscreen, From 3a98fdff7d7a5438b80f1c8d56e650e5d864b067 Mon Sep 17 00:00:00 2001 From: PEDZEO Date: Tue, 20 Jan 2026 01:27:29 +0300 Subject: [PATCH 3/7] Update Layout component: add right padding in fullscreen mode for Telegram native controls to enhance layout usability. --- src/components/layout/Layout.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/layout/Layout.tsx b/src/components/layout/Layout.tsx index f4c76bb..ec4bac8 100644 --- a/src/components/layout/Layout.tsx +++ b/src/components/layout/Layout.tsx @@ -307,7 +307,13 @@ export default function Layout({ children }: LayoutProps) { paddingTop: isFullscreen ? `${Math.max(safeAreaInset.top, contentSafeAreaInset.top)}px` : undefined, }} > -
+
{/* Logo */} From 05e969a3aa11a45c432ae8aca58568f2eab67d3d Mon Sep 17 00:00:00 2001 From: PEDZEO Date: Tue, 20 Jan 2026 01:32:56 +0300 Subject: [PATCH 4/7] Fix Layout component: change padding from right to left for Telegram native controls in fullscreen mode to improve layout consistency. --- src/components/layout/Layout.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/layout/Layout.tsx b/src/components/layout/Layout.tsx index ec4bac8..ba327ae 100644 --- a/src/components/layout/Layout.tsx +++ b/src/components/layout/Layout.tsx @@ -310,8 +310,8 @@ export default function Layout({ children }: LayoutProps) {
From 13f6380763ebaa4c0536db48818f7f5bc0ad2fd0 Mon Sep 17 00:00:00 2001 From: PEDZEO Date: Tue, 20 Jan 2026 01:35:22 +0300 Subject: [PATCH 5/7] Update Layout component: adjust padding for safe area in fullscreen mode to accommodate Telegram native controls, enhancing layout functionality. --- src/components/layout/Layout.tsx | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/components/layout/Layout.tsx b/src/components/layout/Layout.tsx index ba327ae..0c82aee 100644 --- a/src/components/layout/Layout.tsx +++ b/src/components/layout/Layout.tsx @@ -303,17 +303,11 @@ export default function Layout({ children }: LayoutProps) {
-
+
{/* Logo */} From d77d3ffd24311742d52adb6aacc6a2ce6f5ee53c Mon Sep 17 00:00:00 2001 From: PEDZEO Date: Tue, 20 Jan 2026 01:39:20 +0300 Subject: [PATCH 6/7] Optimize CSS for mobile and desktop: implement smooth scrolling for desktop only, enhance card and glass component styles for better performance on mobile, and ensure backdrop-blur effects are applied conditionally based on screen size. --- src/styles/globals.css | 44 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/src/styles/globals.css b/src/styles/globals.css index 2deee88..6aca2cc 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -102,19 +102,34 @@ html { overflow-x: hidden; - scroll-behavior: smooth; + } + + /* Smooth scroll only on desktop - mobile uses native */ + @media (min-width: 1024px) { + html { + scroll-behavior: smooth; + } } /* Dark theme (default) */ body, .dark body { @apply bg-dark-950 text-dark-100 font-sans antialiased; overscroll-behavior-y: contain; + /* iOS smooth scrolling */ + -webkit-overflow-scrolling: touch; + } + + /* Optimize main scrollable content */ + main { + /* Prevent layout shifts during scroll */ + contain: layout style; } /* Light theme - Champagne */ .light body { @apply bg-champagne-200 text-champagne-900 font-sans antialiased; overscroll-behavior-y: contain; + -webkit-overflow-scrolling: touch; } /* Light theme text color overrides - convert dark-* text colors to readable colors */ @@ -310,10 +325,19 @@ @layer components { /* ========== DARK THEME COMPONENTS (default) ========== */ - /* Cards - Dark */ + /* Cards - Dark (optimized for mobile) */ .card { - @apply bg-dark-900/50 backdrop-blur-sm rounded-2xl border border-dark-800/50 p-5 sm:p-6 + @apply bg-dark-900/70 rounded-2xl border border-dark-800/50 p-5 sm:p-6 transition-all duration-300 ease-smooth; + /* GPU acceleration for smooth scroll */ + transform: translateZ(0); + } + + /* Enable backdrop-blur only on desktop */ + @media (min-width: 1024px) { + .card { + @apply bg-dark-900/50 backdrop-blur-sm; + } } .card-hover { @@ -329,9 +353,19 @@ @apply card animate-slide-up; } - /* Glass effect - Dark */ + /* Glass effect - Dark (optimized for mobile) */ .glass { - @apply bg-dark-900/30 backdrop-blur-xl border border-dark-700/30; + @apply bg-dark-900/80 border border-dark-700/30; + /* GPU acceleration */ + transform: translateZ(0); + will-change: transform; + } + + /* Enable backdrop-blur only on desktop where it's performant */ + @media (min-width: 1024px) { + .glass { + @apply bg-dark-900/30 backdrop-blur-xl; + } } /* Buttons - Dark */ From 9343522c1d5c8cf589a06048c8117d4331c86cb6 Mon Sep 17 00:00:00 2001 From: PEDZEO Date: Tue, 20 Jan 2026 01:57:06 +0300 Subject: [PATCH 7/7] Enhance ColorPicker component: add RGB slider functionality, implement Telegram WebApp compatibility, and optimize color conversion methods. Update styles for preset colors and improve performance with memoization. --- src/components/ColorPicker.tsx | 165 +++++++++++++++++----- src/components/TicketNotificationBell.tsx | 14 +- src/styles/globals.css | 77 +++++++++- 3 files changed, 218 insertions(+), 38 deletions(-) diff --git a/src/components/ColorPicker.tsx b/src/components/ColorPicker.tsx index e841b7a..6b90673 100644 --- a/src/components/ColorPicker.tsx +++ b/src/components/ColorPicker.tsx @@ -1,4 +1,4 @@ -import { useState, useRef, useEffect } from 'react' +import { useState, useRef, useEffect, useMemo, useCallback } from 'react' interface ColorPickerProps { value: string @@ -8,6 +8,28 @@ interface ColorPickerProps { disabled?: boolean } +// Check if running in Telegram WebApp (native color picker causes crash) +const isTelegramWebApp = (): boolean => { + return !!(window as unknown as { Telegram?: { WebApp?: unknown } }).Telegram?.WebApp +} + +// Convert hex to RGB +const hexToRgb = (hex: string): { r: number; g: number; b: number } => { + const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex) + return result + ? { + r: parseInt(result[1], 16), + g: parseInt(result[2], 16), + b: parseInt(result[3], 16), + } + : { r: 0, g: 0, b: 0 } +} + +// Convert RGB to hex +const rgbToHex = (r: number, g: number, b: number): string => { + return '#' + [r, g, b].map(x => x.toString(16).padStart(2, '0')).join('') +} + const PRESET_COLORS = [ '#3b82f6', // Blue '#ef4444', // Red @@ -21,18 +43,36 @@ const PRESET_COLORS = [ '#f97316', // Orange '#6366f1', // Indigo '#a855f7', // Purple + '#ffffff', // White + '#64748b', // Slate + '#1e293b', // Dark + '#000000', // Black ] export function ColorPicker({ value, onChange, label, description, disabled }: ColorPickerProps) { const [isOpen, setIsOpen] = useState(false) const [localValue, setLocalValue] = useState(value) + const [rgb, setRgb] = useState(() => hexToRgb(value)) const containerRef = useRef(null) const colorInputRef = useRef(null) + // Memoize Telegram check to avoid recalculating + const isTelegram = useMemo(() => isTelegramWebApp(), []) + useEffect(() => { setLocalValue(value) + setRgb(hexToRgb(value)) }, [value]) + // Handle RGB slider change + const handleRgbChange = useCallback((channel: 'r' | 'g' | 'b', val: number) => { + const newRgb = { ...rgb, [channel]: val } + setRgb(newRgb) + const hex = rgbToHex(newRgb.r, newRgb.g, newRgb.b) + setLocalValue(hex) + onChange(hex) + }, [rgb, onChange]) + // Close on outside click useEffect(() => { function handleClickOutside(event: MouseEvent) { @@ -103,46 +143,107 @@ export function ColorPicker({ value, onChange, label, description, disabled }: C maxLength={7} /> - {/* Hidden native color picker for direct access */} - - - {/* Native picker button - min 44px for touch accessibility */} - + + {/* Native picker button - min 44px for touch accessibility */} + + + )}
- {/* Dropdown with presets */} + {/* Dropdown with presets and RGB sliders */} {isOpen && ( -
+
+ {/* RGB Sliders - shown in Telegram instead of native picker */} + {isTelegram && ( +
+
RGB
+
+ {/* Red */} +
+ R + handleRgbChange('r', parseInt(e.target.value))} + className="flex-1 h-2 rounded-full appearance-none cursor-pointer" + style={{ + background: `linear-gradient(to right, rgb(0,${rgb.g},${rgb.b}), rgb(255,${rgb.g},${rgb.b}))`, + }} + /> + {rgb.r} +
+ {/* Green */} +
+ G + handleRgbChange('g', parseInt(e.target.value))} + className="flex-1 h-2 rounded-full appearance-none cursor-pointer" + style={{ + background: `linear-gradient(to right, rgb(${rgb.r},0,${rgb.b}), rgb(${rgb.r},255,${rgb.b}))`, + }} + /> + {rgb.g} +
+ {/* Blue */} +
+ B + handleRgbChange('b', parseInt(e.target.value))} + className="flex-1 h-2 rounded-full appearance-none cursor-pointer" + style={{ + background: `linear-gradient(to right, rgb(${rgb.r},${rgb.g},0), rgb(${rgb.r},${rgb.g},255))`, + }} + /> + {rgb.b} +
+
+
+ )} +
Preset colors
-
+
{PRESET_COLORS.map((preset) => (