diff --git a/TaskTracker.Web/index.html b/TaskTracker.Web/index.html index 22fe403..f27f58a 100644 --- a/TaskTracker.Web/index.html +++ b/TaskTracker.Web/index.html @@ -7,7 +7,7 @@ - tasktracker-web + TaskTracker
diff --git a/TaskTracker.Web/package-lock.json b/TaskTracker.Web/package-lock.json index 6aec30f..9b4d73b 100644 --- a/TaskTracker.Web/package-lock.json +++ b/TaskTracker.Web/package-lock.json @@ -13,6 +13,7 @@ "@dnd-kit/utilities": "^3.2.2", "@tanstack/react-query": "^5.90.21", "axios": "^1.13.5", + "framer-motion": "^12.34.3", "lucide-react": "^0.575.0", "react": "^19.2.0", "react-dom": "^19.2.0", @@ -3195,6 +3196,33 @@ "node": ">= 6" } }, + "node_modules/framer-motion": { + "version": "12.34.3", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.34.3.tgz", + "integrity": "sha512-v81ecyZKYO/DfpTwHivqkxSUBzvceOpoI+wLfgCgoUIKxlFKEXdg0oR9imxwXumT4SFy8vRk9xzJ5l3/Du/55Q==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.34.3", + "motion-utils": "^12.29.2", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -3469,7 +3497,6 @@ "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "jiti": "lib/jiti-cli.mjs" } @@ -3571,7 +3598,6 @@ "integrity": "sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==", "dev": true, "license": "MPL-2.0", - "peer": true, "dependencies": { "detect-libc": "^2.0.3" }, @@ -3922,6 +3948,21 @@ "node": "*" } }, + "node_modules/motion-dom": { + "version": "12.34.3", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.34.3.tgz", + "integrity": "sha512-sYgFe+pR9aIM7o4fhs2aXtOI+oqlUd33N9Yoxcgo1Fv7M20sRkHtCmzE/VRNIcq7uNJ+qio+Xubt1FXH3pQ+eQ==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.29.2" + } + }, + "node_modules/motion-utils": { + "version": "12.29.2", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.29.2.tgz", + "integrity": "sha512-G3kc34H2cX2gI63RqU+cZq+zWRRPSsNIOjpdl9TN4AQwC4sgwYPl/Q/Obf/d53nOm569T0fYK+tcoSV50BWx8A==", + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", diff --git a/TaskTracker.Web/package.json b/TaskTracker.Web/package.json index 3c4bab5..d47d204 100644 --- a/TaskTracker.Web/package.json +++ b/TaskTracker.Web/package.json @@ -15,6 +15,7 @@ "@dnd-kit/utilities": "^3.2.2", "@tanstack/react-query": "^5.90.21", "axios": "^1.13.5", + "framer-motion": "^12.34.3", "lucide-react": "^0.575.0", "react": "^19.2.0", "react-dom": "^19.2.0", diff --git a/TaskTracker.Web/src/index.css b/TaskTracker.Web/src/index.css index 9540225..a9661ed 100644 --- a/TaskTracker.Web/src/index.css +++ b/TaskTracker.Web/src/index.css @@ -2,22 +2,81 @@ @theme { --font-sans: 'Inter', system-ui, sans-serif; + --color-page: #0a0a0f; + --color-surface: #12131a; + --color-elevated: #1a1b26; + --color-border: rgba(255, 255, 255, 0.06); + --color-border-hover: rgba(255, 255, 255, 0.12); + --color-text-primary: #e2e8f0; + --color-text-secondary: #64748b; + --color-text-tertiary: #334155; + --color-accent: #8b5cf6; + --color-accent-end: #6366f1; + --color-status-active: #3b82f6; + --color-status-paused: #eab308; + --color-status-completed: #22c55e; + --color-status-pending: #64748b; } +/* Noise grain texture */ +body::before { + content: ''; + position: fixed; + inset: 0; + z-index: 9999; + pointer-events: none; + opacity: 0.03; + background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E"); + background-repeat: repeat; + background-size: 256px 256px; +} + +/* Active task pulse */ @keyframes pulse-glow { 0%, 100% { - box-shadow: 0 0 8px rgba(6, 182, 212, 0.3); + box-shadow: 0 0 6px rgba(59, 130, 246, 0.3); } 50% { - box-shadow: 0 0 20px rgba(6, 182, 212, 0.5); + box-shadow: 0 0 16px rgba(59, 130, 246, 0.5); } } .animate-pulse-glow { - animation: pulse-glow 2s ease-in-out infinite; + animation: pulse-glow 2.5s ease-in-out infinite; } -/* Custom scrollbar for dark theme */ +/* Live dot pulse */ +@keyframes live-dot { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.4; } +} + +.animate-live-dot { + animation: live-dot 1.5s ease-in-out infinite; +} + +/* Card hover glow border */ +.card-glow { + position: relative; +} + +.card-glow::before { + content: ''; + position: absolute; + inset: -1px; + border-radius: inherit; + background: linear-gradient(135deg, rgba(139, 92, 246, 0.2), rgba(99, 102, 241, 0.1), transparent); + opacity: 0; + transition: opacity 0.2s ease; + z-index: -1; + pointer-events: none; +} + +.card-glow:hover::before { + opacity: 1; +} + +/* Custom scrollbar */ ::-webkit-scrollbar { width: 6px; } @@ -27,10 +86,15 @@ } ::-webkit-scrollbar-thumb { - background: #2a2d37; + background: #1a1b26; border-radius: 3px; } ::-webkit-scrollbar-thumb:hover { - background: #3a3d47; + background: #2a2d37; +} + +/* Selection color */ +::selection { + background: rgba(139, 92, 246, 0.3); } diff --git a/TaskTracker.Web/src/lib/constants.ts b/TaskTracker.Web/src/lib/constants.ts index 291eb95..f2f1e57 100644 --- a/TaskTracker.Web/src/lib/constants.ts +++ b/TaskTracker.Web/src/lib/constants.ts @@ -1,8 +1,8 @@ export const COLUMN_CONFIG = [ - { status: 'Pending' as const, label: 'Pending', color: '#94a3b8' }, - { status: 'Active' as const, label: 'Active', color: '#06b6d4' }, - { status: 'Paused' as const, label: 'Paused', color: '#f59e0b' }, - { status: 'Completed' as const, label: 'Completed', color: '#10b981' }, + { status: 'Pending' as const, label: 'Pending', color: '#64748b' }, + { status: 'Active' as const, label: 'Active', color: '#3b82f6' }, + { status: 'Paused' as const, label: 'Paused', color: '#eab308' }, + { status: 'Completed' as const, label: 'Completed', color: '#22c55e' }, ] as const export const CATEGORY_COLORS: Record = { @@ -12,5 +12,10 @@ export const CATEGORY_COLORS: Record = { DevOps: '#f97316', Documentation: '#14b8a6', Design: '#ec4899', - Unknown: '#64748b', + Testing: '#3b82f6', + General: '#64748b', + Email: '#f59e0b', + Engineering: '#6366f1', + LaserCutting: '#ef4444', + Unknown: '#475569', }