Compare commits

..

2 Commits
v1.1.0 ... main

Author SHA1 Message Date
Primakov Alexandr Alexandrovich
19c50693e4 1.1.1 2025-11-17 20:21:20 +03:00
Primakov Alexandr Alexandrovich
0187796388 Add CigaretteLoader component for improved loading experience in Dashboard
- Introduced a new CigaretteLoader component to replace the default loading indicator in the Dashboard.
- Enhanced the loading experience with a custom animated SVG representation of a cigarette.
2025-11-17 20:21:11 +03:00
4 changed files with 169 additions and 4 deletions

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "smoke-tracker", "name": "smoke-tracker",
"version": "1.1.0", "version": "1.1.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "smoke-tracker", "name": "smoke-tracker",
"version": "1.1.0", "version": "1.1.1",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@brojs/cli": "^1.10.0", "@brojs/cli": "^1.10.0",

View File

@ -1,6 +1,6 @@
{ {
"name": "smoke-tracker", "name": "smoke-tracker",
"version": "1.1.0", "version": "1.1.1",
"description": "", "description": "",
"main": "./src/index.tsx", "main": "./src/index.tsx",
"scripts": { "scripts": {

View File

@ -0,0 +1,162 @@
import React from 'react'
export const CigaretteLoader = () => (
<div style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '100vh',
backgroundColor: '#1a1a1a'
}}>
<svg width="200" height="60" viewBox="0 0 200 60" xmlns="http://www.w3.org/2000/svg">
<defs>
{/* Градиент для сигареты */}
<linearGradient id="cigaretteGradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style={{ stopColor: '#f5f5f5', stopOpacity: 1 }} />
<stop offset="95%" style={{ stopColor: '#e0e0e0', stopOpacity: 1 }} />
<stop offset="100%" style={{ stopColor: '#d4a574', stopOpacity: 1 }} />
</linearGradient>
{/* Градиент для тлеющей части */}
<linearGradient id="burningGradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style={{ stopColor: '#ff6b35', stopOpacity: 1 }} />
<stop offset="50%" style={{ stopColor: '#ff8c42', stopOpacity: 1 }} />
<stop offset="100%" style={{ stopColor: '#ffa600', stopOpacity: 1 }} />
</linearGradient>
{/* Маска для эффекта сгорания */}
<mask id="burnMask">
<rect x="0" y="0" width="200" height="60" fill="white">
<animate
attributeName="width"
from="0"
to="200"
dur="2s"
repeatCount="indefinite"
/>
</rect>
</mask>
</defs>
{/* Тело сигареты (не сгоревшая часть) */}
<rect x="0" y="22" width="180" height="16" rx="2" fill="url(#cigaretteGradient)" />
{/* Фильтр */}
<rect x="175" y="20" width="20" height="20" rx="2" fill="#d4a574" />
<line x1="180" y1="20" x2="180" y2="40" stroke="#b8956a" strokeWidth="1" />
<line x1="185" y1="20" x2="185" y2="40" stroke="#b8956a" strokeWidth="1" />
<line x1="190" y1="20" x2="190" y2="40" stroke="#b8956a" strokeWidth="1" />
{/* Сгоревшая часть с маской */}
<g mask="url(#burnMask)">
<rect x="0" y="22" width="180" height="16" fill="#333" opacity="0.8" />
{/* Тлеющий край */}
<rect x="0" y="24" width="5" height="12" fill="url(#burningGradient)">
<animate
attributeName="x"
from="0"
to="180"
dur="2s"
repeatCount="indefinite"
/>
</rect>
{/* Искры */}
<circle r="2" fill="#ff6b35">
<animate
attributeName="cx"
from="5"
to="185"
dur="2s"
repeatCount="indefinite"
/>
<animate
attributeName="cy"
values="20;18;20"
dur="0.3s"
repeatCount="indefinite"
/>
<animate
attributeName="opacity"
values="1;0.5;1"
dur="0.3s"
repeatCount="indefinite"
/>
</circle>
<circle r="1.5" fill="#ffa600">
<animate
attributeName="cx"
from="8"
to="188"
dur="2s"
repeatCount="indefinite"
/>
<animate
attributeName="cy"
values="40;42;40"
dur="0.4s"
repeatCount="indefinite"
/>
<animate
attributeName="opacity"
values="0.8;0.3;0.8"
dur="0.4s"
repeatCount="indefinite"
/>
</circle>
</g>
{/* Дым */}
<g opacity="0.6">
<ellipse rx="3" ry="3" fill="#888">
<animate
attributeName="cx"
from="5"
to="185"
dur="2s"
repeatCount="indefinite"
/>
<animate
attributeName="cy"
from="18"
to="8"
dur="1s"
repeatCount="indefinite"
/>
<animate
attributeName="opacity"
from="0.6"
to="0"
dur="1s"
repeatCount="indefinite"
/>
</ellipse>
<ellipse rx="4" ry="4" fill="#999">
<animate
attributeName="cx"
from="3"
to="183"
dur="2s"
repeatCount="indefinite"
/>
<animate
attributeName="cy"
from="18"
to="5"
dur="1.2s"
repeatCount="indefinite"
/>
<animate
attributeName="opacity"
from="0.5"
to="0"
dur="1.2s"
repeatCount="indefinite"
/>
</ellipse>
</g>
</svg>
</div>
)

View File

@ -7,9 +7,12 @@ import { SignInPage, SignUpPage } from './pages/auth'
import { TrackerPage } from './pages/tracker' import { TrackerPage } from './pages/tracker'
import { StatsPage } from './pages/stats' import { StatsPage } from './pages/stats'
import { ProtectedRoute } from './components/ProtectedRoute' import { ProtectedRoute } from './components/ProtectedRoute'
import { CigaretteLoader } from './components/CigaretteLoader'
const PageWrapper = ({ children }: React.PropsWithChildren) => ( const PageWrapper = ({ children }: React.PropsWithChildren) => (
<Suspense fallback={<div>Loading...</div>}>{children}</Suspense> <Suspense fallback={<CigaretteLoader />}>
{children}
</Suspense>
) )
export const Dashboard = () => { export const Dashboard = () => {