Optimize CSS architecture with SCSS modular system

## Major CSS/SCSS Improvements
-  Set up modular SCSS architecture with variables, mixins, and components
-  Created organized directory structure: src/scss/ with variables, mixins, pages/
-  Removed ~300+ lines of inline CSS from admin.html
-  Added comprehensive design system with consistent spacing, colors, typography
-  Created reusable mixins for buttons, cards, tables, forms, and layouts
-  Implemented responsive breakpoint mixins for mobile/tablet/desktop
-  Added utility classes for common layouts and spacing

## Build System
-  Added sass and concurrently as dev dependencies
-  Created npm scripts: build-css, watch-css, dev-with-css
-  Automated SCSS compilation to compressed CSS
-  Set up development workflow with CSS watching

## Admin Panel Enhancements
-  Added complete tab navigation system (Location Reports + Profanity Filter)
-  Integrated profanity management UI with forms and tables
-  Consistent styling across all components using SCSS mixins
-  Improved responsive design for mobile devices

## Benefits
- 🎯 Maintainable: All styles centralized in modular SCSS files
- 📱 Responsive: Better mobile experience with consistent breakpoints
- 🎨 Consistent: Design system ensures visual consistency
-  Efficient: Compressed CSS output, no inline styles
- 🔧 Developer-friendly: Easy to extend and modify styles

The application now has professional-grade CSS architecture that's easy to maintain and extend.
This commit is contained in:
Deco Vander 2025-07-04 11:30:34 -04:00
parent 3b4db2b8d1
commit f83e087541
9 changed files with 1414 additions and 931 deletions

700
package-lock.json generated
View file

@ -16,7 +16,9 @@
"sqlite3": "^5.1.6"
},
"devDependencies": {
"nodemon": "^3.0.1"
"concurrently": "^9.2.0",
"nodemon": "^3.0.1",
"sass": "^1.89.2"
}
},
"node_modules/@gar/promisify": {
@ -52,6 +54,330 @@
"node": ">=10"
}
},
"node_modules/@parcel/watcher": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz",
"integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"dependencies": {
"detect-libc": "^1.0.3",
"is-glob": "^4.0.3",
"micromatch": "^4.0.5",
"node-addon-api": "^7.0.0"
},
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"optionalDependencies": {
"@parcel/watcher-android-arm64": "2.5.1",
"@parcel/watcher-darwin-arm64": "2.5.1",
"@parcel/watcher-darwin-x64": "2.5.1",
"@parcel/watcher-freebsd-x64": "2.5.1",
"@parcel/watcher-linux-arm-glibc": "2.5.1",
"@parcel/watcher-linux-arm-musl": "2.5.1",
"@parcel/watcher-linux-arm64-glibc": "2.5.1",
"@parcel/watcher-linux-arm64-musl": "2.5.1",
"@parcel/watcher-linux-x64-glibc": "2.5.1",
"@parcel/watcher-linux-x64-musl": "2.5.1",
"@parcel/watcher-win32-arm64": "2.5.1",
"@parcel/watcher-win32-ia32": "2.5.1",
"@parcel/watcher-win32-x64": "2.5.1"
}
},
"node_modules/@parcel/watcher-android-arm64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz",
"integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-darwin-arm64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz",
"integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-darwin-x64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz",
"integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-freebsd-x64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz",
"integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm-glibc": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz",
"integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm-musl": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz",
"integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm64-glibc": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz",
"integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm64-musl": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz",
"integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-x64-glibc": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz",
"integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-x64-musl": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz",
"integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-arm64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz",
"integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-ia32": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz",
"integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-x64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz",
"integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher/node_modules/detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
"dev": true,
"license": "Apache-2.0",
"optional": true,
"bin": {
"detect-libc": "bin/detect-libc.js"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/@tootallnate/once": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
@ -151,12 +477,28 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"devOptional": true,
"license": "MIT",
"optional": true,
"engines": {
"node": ">=8"
}
},
"node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
@ -399,6 +741,46 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/chalk/node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/chalk/node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/chokidar": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
@ -443,6 +825,41 @@
"node": ">=6"
}
},
"node_modules/cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
"dev": true,
"license": "ISC",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.1",
"wrap-ansi": "^7.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"license": "MIT"
},
"node_modules/color-support": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
@ -460,6 +877,58 @@
"devOptional": true,
"license": "MIT"
},
"node_modules/concurrently": {
"version": "9.2.0",
"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.0.tgz",
"integrity": "sha512-IsB/fiXTupmagMW4MNp2lx2cdSN2FfZq78vF90LBB+zZHArbIQZjQtzXCiXnvTxCZSvXanTqFLWBjw2UkLx1SQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"chalk": "^4.1.2",
"lodash": "^4.17.21",
"rxjs": "^7.8.1",
"shell-quote": "^1.8.1",
"supports-color": "^8.1.1",
"tree-kill": "^1.2.2",
"yargs": "^17.7.2"
},
"bin": {
"conc": "dist/bin/concurrently.js",
"concurrently": "dist/bin/concurrently.js"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/open-cli-tools/concurrently?sponsor=1"
}
},
"node_modules/concurrently/node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/concurrently/node_modules/supports-color": {
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
"node_modules/console-control-strings": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
@ -620,8 +1089,8 @@
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"license": "MIT",
"optional": true
"devOptional": true,
"license": "MIT"
},
"node_modules/encodeurl": {
"version": "2.0.0",
@ -711,6 +1180,16 @@
"node": ">= 0.4"
}
},
"node_modules/escalade": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@ -906,6 +1385,16 @@
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
}
},
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true,
"license": "ISC",
"engines": {
"node": "6.* || 8.* || >= 10.*"
}
},
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
@ -1195,6 +1684,13 @@
"dev": true,
"license": "ISC"
},
"node_modules/immutable": {
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz",
"integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==",
"dev": true,
"license": "MIT"
},
"node_modules/imurmurhash": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
@ -1296,8 +1792,8 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"devOptional": true,
"license": "MIT",
"optional": true,
"engines": {
"node": ">=8"
}
@ -1346,6 +1842,13 @@
"license": "MIT",
"optional": true
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true,
"license": "MIT"
},
"node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
@ -1423,6 +1926,21 @@
"node": ">= 0.6"
}
},
"node_modules/micromatch": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"braces": "^3.0.3",
"picomatch": "^2.3.1"
},
"engines": {
"node": ">=8.6"
}
},
"node_modules/mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
@ -2030,6 +2548,16 @@
"node": ">=8.10.0"
}
},
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/retry": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
@ -2057,6 +2585,16 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/rxjs": {
"version": "7.8.2",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
"integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"tslib": "^2.1.0"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@ -2083,6 +2621,57 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"license": "MIT"
},
"node_modules/sass": {
"version": "1.89.2",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.89.2.tgz",
"integrity": "sha512-xCmtksBKd/jdJ9Bt9p7nPKiuqrlBMBuuGkQlkhZjjQk3Ty48lv93k5Dq6OPkKt4XwxDJ7tvlfrTa1MPA9bf+QA==",
"dev": true,
"license": "MIT",
"dependencies": {
"chokidar": "^4.0.0",
"immutable": "^5.0.2",
"source-map-js": ">=0.6.2 <2.0.0"
},
"bin": {
"sass": "sass.js"
},
"engines": {
"node": ">=14.0.0"
},
"optionalDependencies": {
"@parcel/watcher": "^2.4.1"
}
},
"node_modules/sass/node_modules/chokidar": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
"dev": true,
"license": "MIT",
"dependencies": {
"readdirp": "^4.0.1"
},
"engines": {
"node": ">= 14.16.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/sass/node_modules/readdirp": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
"integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 14.18.0"
},
"funding": {
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/semver": {
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
@ -2162,6 +2751,19 @@
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
"license": "ISC"
},
"node_modules/shell-quote": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz",
"integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/side-channel": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
@ -2365,6 +2967,16 @@
"license": "MIT",
"optional": true
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/sprintf-js": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
@ -2431,8 +3043,8 @@
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"devOptional": true,
"license": "MIT",
"optional": true,
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
@ -2446,8 +3058,8 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"devOptional": true,
"license": "MIT",
"optional": true,
"dependencies": {
"ansi-regex": "^5.0.1"
},
@ -2569,6 +3181,23 @@
"nodetouch": "bin/nodetouch.js"
}
},
"node_modules/tree-kill": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
"integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
"dev": true,
"license": "MIT",
"bin": {
"tree-kill": "cli.js"
}
},
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"dev": true,
"license": "0BSD"
},
"node_modules/tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
@ -2689,17 +3318,74 @@
"string-width": "^1.0.2 || 2 || 3 || 4"
}
},
"node_modules/wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"license": "ISC"
},
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=10"
}
},
"node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"license": "ISC"
},
"node_modules/yargs": {
"version": "17.7.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
"dev": true,
"license": "MIT",
"dependencies": {
"cliui": "^8.0.1",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.3",
"y18n": "^5.0.5",
"yargs-parser": "^21.1.1"
},
"engines": {
"node": ">=12"
}
},
"node_modules/yargs-parser": {
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=12"
}
}
}
}

View file

@ -5,7 +5,11 @@
"main": "server.js",
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js"
"dev": "nodemon server.js",
"build-css": "sass src/scss/main.scss public/style.css --style=compressed",
"watch-css": "sass src/scss/main.scss public/style.css --watch",
"dev-with-css": "concurrently \"npm run watch-css\" \"npm run dev\"",
"build": "npm run build-css"
},
"dependencies": {
"cors": "^2.8.5",
@ -15,7 +19,9 @@
"sqlite3": "^5.1.6"
},
"devDependencies": {
"nodemon": "^3.0.1"
"concurrently": "^9.2.0",
"nodemon": "^3.0.1",
"sass": "^1.89.2"
},
"keywords": [
"ice",

View file

@ -7,313 +7,6 @@
<link rel="icon" type="image/svg+xml" href="https://iceymi.b-cdn.net/favicon.svg">
<link rel="icon" type="image/x-icon" href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMiAzMiIgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIj4KICA8ZGVmcz4KICAgIDxzdHlsZT4KICAgICAgLnNub3dmbGFrZSB7IGZpbGw6ICMyMTk2RjM7IH0KICAgICAgLmNlbnRlciB7IGZpbGw6ICMxOTc2RDI7IH0KICAgIDwvc3R5bGU+CiAgPC9kZWZzPgogIAogIDxnIGNsYXNzPSJzbm93Zmxha2UiPgogICAgPHJlY3QgeD0iMTUiIHk9IjIiIHdpZHRoPSIyIiBoZWlnaHQ9IjI4IiAvPgogICAgPHJlY3QgeD0iMiIgeT0iMTUiIHdpZHRoPSIyOCIgaGVpZ2h0PSIyIiAvPgogICAgPHJlY3QgeD0iMTUiIHk9IjIiIHdpZHRoPSIyIiBoZWlnaHQ9IjI4IiB0cmFuc2Zvcm09InJvdGF0ZSg0NSAxNiAxNikiIC8+CiAgICA8cmVjdCB4PSIxNSIgeT0iMiIgd2lkdGg9IjIiIGhlaWdodD0iMjgiIHRyYW5zZm9ybT0icm90YXRlKC00NSAxNiAxNikiIC8+CiAgICA8cG9seWdvbiBwb2ludHM9IjE2LDIgMTQsNiAxOCw2IiAvPgogICAgPHBvbHlnb24gcG9pbnRzPSIxNiwzMCAxNCwyNiAxOCwyNiIgLz4KICAgIDxwb2x5Z29uIHBvaW50cz0iMiwxNiA2LDE0IDYsMTgiIC8+CiAgICA8cG9seWdvbiBwb2ludHM9IjMwLDE2IDI2LDE0IDI2LDE4IiAvPgogICAgPHBvbHlnb24gcG9pbnRzPSI2LjMsNi4zIDguNiw0IDkuOSw3LjciIHRyYW5zZm9ybT0icm90YXRlKDQ1IDE2IDE2KSIgLz4KICAgIDxwb2x5Z29uIHBvaW50cz0iMjUuNywyNS43IDIzLjQsMjggMjIuMSwyNC4zIiB0cmFuc2Zvcm09InJvdGF0ZSg0NSAxNiAxNikiIC8+CiAgICA8cG9seWdvbiBwb2ludHM9IjYuMywyNS43IDguNiwyOCA5LjksMjQuMyIgdHJhbnNmb3JtPSJyb3RhdGUoLTQ1IDE2IDE2KSIgLz4KICAgIDxwb2x5Z29uIHBvaW50cz0iMjUuNyw2LjMgMjMuNCw0IDIyLjEsNy43IiB0cmFuc2Zvcm09InJvdGF0ZSgtNDUgMTYgMTYpIiAvPgogIDwvZz4KICA8Y2lyY2xlIGN4PSIxNiIgY3k9IjE2IiByPSIzIiBjbGFzcz0iY2VudGVyIiAvPgo8L3N2Zz4K">
<link rel="stylesheet" href="style.css">
<style>
.admin-container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.login-section {
background: var(--card-bg);
color: var(--text-color);
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 4px var(--shadow);
max-width: 400px;
margin: 50px auto;
}
.admin-section {
display: none;
}
.locations-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
background: var(--card-bg);
color: var(--text-color);
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 4px var(--shadow);
}
.locations-table th,
.locations-table td {
padding: 12px;
text-align: left;
border-bottom: 1px solid var(--border-color);
color: var(--text-color);
}
.locations-table th {
background-color: var(--table-header-bg);
color: var(--text-color);
font-weight: bold;
}
.locations-table tr:hover {
background-color: var(--table-hover);
}
.action-buttons {
display: flex;
gap: 5px;
}
.btn {
padding: 5px 10px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
text-decoration: none;
display: inline-block;
}
.btn-edit {
background-color: #007bff;
color: white;
}
.btn-delete {
background-color: #dc3545;
color: white;
}
.btn-save {
background-color: #28a745;
color: white;
}
.btn-cancel {
background-color: #6c757d;
color: white;
}
.edit-row {
background-color: #fff3cd !important;
}
.edit-input {
width: 100%;
padding: 4px;
border: 1px solid var(--input-border);
border-radius: 4px;
font-size: 12px;
background-color: var(--input-bg);
color: var(--text-color);
}
.status-indicator {
padding: 2px 6px;
border-radius: 12px;
font-size: 11px;
font-weight: bold;
}
.status-active {
background-color: #d4edda;
color: #155724;
}
.status-expired {
background-color: #f8d7da;
color: #721c24;
}
.admin-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.header-buttons {
display: flex;
gap: 10px;
align-items: center;
}
.header-btn {
background-color: #6c757d;
color: white;
padding: 10px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
text-decoration: none;
display: inline-block;
transition: background-color 0.2s;
}
.theme-toggle-admin {
background: var(--card-bg) !important;
color: var(--text-color) !important;
border: 2px solid var(--border-color) !important;
width: 40px;
height: 40px;
border-radius: 50% !important;
display: flex;
align-items: center;
justify-content: center;
padding: 0 !important;
}
.header-btn:hover {
opacity: 0.9;
}
.header-btn.btn-refresh {
background-color: #007bff;
}
.header-btn.btn-home {
background-color: #28a745;
}
.header-btn.btn-logout {
background-color: #dc3545;
}
.stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 20px;
}
.stat-card {
background: var(--card-bg);
color: var(--text-color);
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px var(--shadow);
text-align: center;
}
.stat-number {
font-size: 2em;
font-weight: bold;
color: #007bff;
}
.address-cell {
max-width: 300px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Mobile responsiveness for admin panel */
@media (max-width: 768px) {
.admin-container {
padding: 10px;
}
.login-section {
margin: 20px auto;
padding: 20px;
}
.admin-header {
flex-direction: column;
gap: 15px;
text-align: center;
}
.admin-header h1 {
font-size: 1.5em;
margin: 0;
}
.header-buttons {
flex-wrap: wrap;
justify-content: center;
}
.header-btn {
font-size: 12px;
padding: 8px 12px;
}
.stats {
grid-template-columns: repeat(2, 1fr);
gap: 10px;
}
.stat-card {
padding: 15px;
}
.stat-number {
font-size: 1.5em;
}
.locations-table {
font-size: 12px;
}
.locations-table th,
.locations-table td {
padding: 6px 4px;
}
.address-cell {
max-width: 120px;
font-size: 11px;
}
.action-buttons {
flex-direction: column;
gap: 2px;
}
.btn {
padding: 4px 6px;
font-size: 10px;
}
.edit-input {
font-size: 12px;
padding: 2px;
}
}
@media (max-width: 480px) {
.stats {
grid-template-columns: 1fr;
}
.header-buttons {
flex-direction: column;
gap: 8px;
width: 100%;
}
.header-btn {
font-size: 11px;
padding: 6px 10px;
text-align: center;
}
.locations-table th,
.locations-table td {
padding: 4px 2px;
}
.address-cell {
max-width: 100px;
}
.btn {
padding: 3px 4px;
font-size: 9px;
}
}
</style>
</head>
<body>
<div class="admin-container">
@ -363,26 +56,79 @@
</div>
</div>
<div class="form-section">
<h3>All Location Reports</h3>
<table class="locations-table">
<thead>
<tr>
<th>ID</th>
<th>Status</th>
<th>Address</th>
<th>Description</th>
<th>Persistent</th>
<th>Reported</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="locations-tbody">
<tr>
<td colspan="7">Loading...</td>
</tr>
</tbody>
</table>
<!-- Tab Navigation -->
<div class="tab-navigation">
<button class="tab-btn active" data-tab="locations">📍 Location Reports</button>
<button class="tab-btn" data-tab="profanity">🔒 Profanity Filter</button>
</div>
<!-- Locations Tab -->
<div id="locations-tab" class="tab-content active">
<div class="form-section">
<h3>All Location Reports</h3>
<table class="locations-table">
<thead>
<tr>
<th>ID</th>
<th>Status</th>
<th>Address</th>
<th>Description</th>
<th>Persistent</th>
<th>Reported</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="locations-tbody">
<tr>
<td colspan="7">Loading...</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Profanity Filter Tab -->
<div id="profanity-tab" class="tab-content profanity-management">
<div class="management-section">
<h4>🔒 Custom Profanity Words</h4>
<form id="add-profanity-form" class="profanity-form">
<input type="text" id="new-word" placeholder="Enter word to filter" required>
<select id="new-severity">
<option value="low">Low</option>
<option value="medium" selected>Medium</option>
<option value="high">High</option>
</select>
<input type="text" id="new-category" placeholder="Category" value="custom">
<button type="submit">Add Word</button>
</form>
<table class="locations-table">
<thead>
<tr>
<th>ID</th>
<th>Word</th>
<th>Severity</th>
<th>Category</th>
<th>Added</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="profanity-tbody">
<tr>
<td colspan="6">Loading...</td>
</tr>
</tbody>
</table>
</div>
<div class="management-section test-section">
<h4>🧪 Test Profanity Filter</h4>
<form id="test-profanity-form" class="test-form">
<textarea id="test-text" placeholder="Enter text to test for profanity..."></textarea>
<button type="submit">Test Text</button>
</form>
<div id="test-results" class="test-results empty">Enter text above to test profanity detection</div>
</div>
</div>
</div>
</div>

File diff suppressed because one or more lines are too long

1
public/style.css.map Normal file
View file

@ -0,0 +1 @@
{"version":3,"sourceRoot":"","sources":["../src/scss/pages/_admin.scss","../src/scss/_variables.scss","../src/scss/_mixins.scss","../src/scss/main.scss"],"names":[],"mappings":"AAKA,iBACE,iBACA,cACA,QCeW,KDZb,eE4BE,0BACA,wBACA,QDjBW,KCkBX,cDbiB,ICcjB,WDRU,yBDtBV,gBACA,iBAGF,eACE,aAIF,cEgCE,aACA,mBACA,8BFhCA,cCDW,KDIb,gBACE,aACA,ICRW,IDSX,mBAGF,YE5BE,YACA,cDsBiB,ICrBjB,eACA,YD+BY,wEC9BZ,qBACA,qBACA,wBASE,iBACA,UDoBW,KCjBb,iBFU2B,QET3B,MArBoD,KAuBpD,kBACE,WACA,2BAGF,mBACE,wBFGF,wBE/BA,YACA,cDsBiB,ICrBjB,eACA,YD+BY,wEC9BZ,qBACA,qBACA,wBASE,iBACA,UDoBW,KCjBb,iBDvBc,QCwBd,MArBoD,KAuBpD,8BACE,WACA,2BAGF,+BACE,wBFOF,qBEnCA,YACA,cDsBiB,ICrBjB,eACA,YD+BY,wEC9BZ,qBACA,qBACA,wBASE,iBACA,UDoBW,KCjBb,iBDrBc,QCsBd,MArBoD,KAuBpD,2BACE,WACA,2BAGF,4BACE,wBFWF,uBEvCA,YACA,cDsBiB,ICrBjB,eACA,YD+BY,wEC9BZ,qBACA,qBACA,wBASE,iBACA,UDoBW,KCjBb,iBDnBa,QCoBb,MArBoD,KAuBpD,6BACE,WACA,2BAGF,8BACE,wBFgBJ,oBACE,qCACA,mCACA,gDACA,WACA,YACA,6BEPA,aACA,mBACA,uBFOA,qBAIF,OACE,aACA,2DACA,ICzCW,KD0CX,cC1CW,KD6Cb,WE7BE,0BACA,wBACA,QDlBW,KCmBX,cDbiB,ICcjB,WDRU,yBDmCV,kBAGF,aACE,cACA,iBACA,MC3Ec,QD+EhB,iBEdE,WACA,yBACA,0BACA,wBACA,cDzCiB,IC0CjB,gBACA,WDrCU,yBD+CV,WC3DW,KCmDX,wCACE,iBACA,gBACA,4CACA,wBAGF,oBACE,wCACA,iBAGF,0BACE,oCFDJ,gBACE,aACA,QAGF,KEtFE,YACA,cDsBiB,ICrBjB,eACA,YD+BY,wEC9BZ,qBACA,qBACA,wBAGE,gBACA,UDyBW,KChBb,iBDvBc,QCwBd,MArBoD,KAuBpD,WACE,WACA,2BAGF,YACE,wBF6DF,UEzFA,YACA,cDsBiB,ICrBjB,eACA,YD+BY,wEC9BZ,qBACA,qBACA,wBAGE,gBACA,UDyBW,KChBb,iBDvBc,QCwBd,MArBoD,KAuBpD,gBACE,WACA,2BAGF,iBACE,wBFiEF,YE7FA,YACA,cDsBiB,ICrBjB,eACA,YD+BY,wEC9BZ,qBACA,qBACA,wBAGE,gBACA,UDyBW,KChBb,iBDnBa,QCoBb,MArBoD,KAuBpD,kBACE,WACA,2BAGF,mBACE,wBFqEF,UEjGA,YACA,cDsBiB,ICrBjB,eACA,YD+BY,wEC9BZ,qBACA,qBACA,wBAGE,gBACA,UDyBW,KChBb,iBDrBc,QCsBd,MArBoD,KAuBpD,gBACE,WACA,2BAGF,iBACE,wBFyEF,YErGA,YACA,cDsBiB,ICrBjB,eACA,YD+BY,wEC9BZ,qBACA,qBACA,wBAGE,gBACA,UDyBW,KChBb,iBFmF6B,QElF7B,MArBoD,KAuBpD,kBACE,WACA,2BAGF,mBACE,wBF+EJ,UACE,oCAGF,YEvBE,iBACA,qCACA,cDnEiB,ICoEjB,UDvDa,KCwDb,YD1DY,wEC2DZ,iCACA,wBACA,iCFkBA,WACA,UC/Ea,KC8Db,kBACE,aACA,aDvGY,QCwGZ,yCFkBJ,kBEZE,gBACA,cDlFiB,KCmFjB,UDzEa,KC0Eb,iBACA,iBFS0B,cER1B,MFQuC,QAGzC,eEhBE,gBACA,cDlFiB,KCmFjB,UDzEa,KC0Eb,iBACA,iBFa0B,QEZ1B,MFYmC,QAGrC,gBEpBE,gBACA,cDlFiB,KCmFjB,UDzEa,KC0Eb,iBACA,iBFiB0B,QEhB1B,MFgBmC,QAIrC,gBACE,aACA,cCnHW,KDoHX,4CAGF,SEzIE,YACA,cDsBiB,ICrBjB,eACA,YD+BY,wEC9BZ,qBACA,qBACA,wBASE,iBACA,UDoBW,KCjBb,iBFuH2B,cEtH3B,MFsHqD,kBACrD,sCACA,gBEtHA,eACE,WACA,2BAGF,gBACE,wBFkHF,gBACE,oBCnJY,QDoJZ,MCpJY,QDuJd,eACE,oCACA,eAIJ,aACE,aAEA,oBACE,cAMF,0CEjIA,0BACA,wBACA,QDlBW,KCmBX,cDbiB,ICcjB,WDRU,yBDuIR,cCnJS,KDqJT,6CACE,aACA,MC7KU,QDiLd,sCACE,aACA,uCACA,IChKS,IDiKT,gBACA,cChKS,KDkKT,yFE5FF,iBACA,qCACA,cDnEiB,ICoEjB,UDvDa,KCwDb,YD1DY,wEC2DZ,iCACA,wBACA,iCAEA,qGACE,aACA,aDvGY,QCwGZ,yCFsFA,+CACE,aACA,+BACA,IC7KO,ID8KP,gBAEA,wDExGJ,iBACA,qCACA,cDnEiB,ICoEjB,UDvDa,KCwDb,YD1DY,wEC2DZ,iCACA,wBACA,iCFmGM,gBACA,gBElGN,8DACE,aACA,aDvGY,QCwGZ,yCFqGJ,cACE,WCzLW,KD0LX,QC1LW,KD2LX,cCrLiB,IDuLjB,sBACE,yBACA,cACA,yBAGF,oBACE,yBACA,cACA,yBAGF,oBACE,yBACA,cACA,yBAIJ,cEvHE,gBACA,cDlFiB,KCmFjB,UDzEa,KC0Eb,iBACA,iBFoH0B,QEnH1B,MFmHmC,QAGrC,iBE3HE,gBACA,cDlFiB,KCmFjB,UDzEa,KC0Eb,iBACA,iBFwH0B,QEvH1B,MFuHmC,QAGrC,eE/HE,gBACA,cDlFiB,KCmFjB,UDzEa,KC0Eb,iBACA,iBF4H0B,QE3H1B,MF2HmC,QAGrC,YE7OE,YACA,cDsBiB,ICrBjB,eACA,YD+BY,wEC9BZ,qBACA,qBACA,wBAGE,gBACA,UDyBW,KChBb,iBDvBc,QCwBd,MArBoD,KAuBpD,kBACE,WACA,2BAGF,mBACE,wBFoNF,mBEhPA,YACA,cDsBiB,ICrBjB,eACA,YD+BY,wEC9BZ,qBACA,qBACA,wBAGE,gBACA,UDyBW,KChBb,iBDnBa,QCoBb,MArBoD,KAuBpD,yBACE,WACA,2BAGF,0BACE,wBF2NF,0BEvPA,YACA,cDsBiB,ICrBjB,eACA,YD+BY,wEC9BZ,qBACA,qBACA,wBASE,iBACA,UDoBW,KCjBb,iBDpBc,QCqBd,MFoO0D,KElO1D,gCACE,WACA,2BAGF,iCACE,wBF+NF,4BE3PA,YACA,cDsBiB,ICrBjB,eACA,YD+BY,wEC9BZ,qBACA,qBACA,wBASE,iBACA,UDoBW,KCjBb,iBFyO6B,QExO7B,MArBoD,KAuBpD,kCACE,WACA,2BAGF,mCACE,wBAwFF,yBF8IA,iBACE,QClPS,KDqPX,cACE,sBACA,ICvPS,KDwPT,oBAGF,gBACE,uBACA,eAGF,OACE,8BAGF,iBACE,UCnPW,KDqPX,wCACE,gBAGF,+BACE,gBAIJ,KACE,gBACA,cAGF,gBACE,0BACA,ICxRS,KEZb,MAEE,4BACA,sBACA,mBACA,wBACA,oBACA,wBACA,2BACA,uBACA,6BAGF,kBAEE,4BACA,sBACA,mBACA,wBACA,oBACA,wBACA,2BACA,uBACA,6BAGF,EACE,SACA,UACA,sBAGF,KACE,YFHY,wEEIZ,yCACA,wBACA,gBACA,iBACA,oDAIF,YACE,cF9BW,KEgCX,kBACE,cACA,cFpCS,IEqCT,gBAGF,0DDiCA,iBACA,qCACA,cDnEiB,ICoEjB,UDvDa,KCwDb,YD1DY,wEC2DZ,iCACA,wBACA,iCCtCE,WDwCF,4EACE,aACA,aDvGY,QCwGZ,yCCvCJ,OD7DE,YACA,cDsBiB,ICrBjB,eACA,YD+BY,wEC9BZ,qBACA,qBACA,wBASE,iBACA,UDoBW,KCjBb,iBDvBc,QCwBd,MArBoD,KAuBpD,aACE,WACA,2BAGF,cACE,wBCoCF,gBACE,WACA,mBAKJ,SACE,QFvDW,KEwDX,cFlDiB,IEmDjB,cACA,aAEA,eACE,yBACA,cACA,yBAGF,iBACE,yBACA,cACA,yBAGF,cACE,yBACA,cACA,yBAOJ,cDnGE,YACA,cDsBiB,ICrBjB,eACA,YD+BY,wEC9BZ,qBACA,qBACA,wBASE,iBACA,UDoBW,KCjBb,iBCiF2B,cDhF3B,MArBoD,KCsGpD,qCACA,cF5EmB,IE6EnB,WACA,YD7DA,aACA,mBACA,uBAvBA,oBACE,WACA,2BAGF,qBACE,wBC+EF,oBACE,oCACA,eAKJ,+BACA,2BACA,6BAEA,qBFtGa,IEuGb,qBFtGa,KEuGb,qBFtGa,KEwGb,kBF1Ga,IE2Gb,kBF1Ga,KE2Gb,kBF1Ga,KE4Gb,cF9Ga,IE+Gb,cF9Ga,KE+Gb,cF9Ga,KEgHb,qBACA,aDxFE,aACA,mBACA,uBCuFF,cDnFE,aACA,mBACA,8BCkFF,aD9EE,aACA,sBC+EF,kBACA","file":"style.css"}

137
src/scss/_mixins.scss Normal file
View file

@ -0,0 +1,137 @@
// Import variables
@use 'variables' as *;
// Button mixin
@mixin button($bg-color: $primary-color, $text-color: white, $size: md) {
border: none;
border-radius: $border-radius-sm;
cursor: pointer;
font-family: $font-family;
text-decoration: none;
display: inline-block;
transition: all 0.2s ease;
@if $size == sm {
padding: $spacing-xs $spacing-sm;
font-size: $font-size-sm;
} @else if $size == lg {
padding: $spacing-md $spacing-lg;
font-size: $font-size-lg;
} @else {
padding: $spacing-sm $spacing-md;
font-size: $font-size-md;
}
background-color: $bg-color;
color: $text-color;
&:hover {
opacity: 0.9;
transform: translateY(-1px);
}
&:active {
transform: translateY(0);
}
}
// Card mixin
@mixin card($padding: $spacing-lg) {
background: var(--card-bg);
color: var(--text-color);
padding: $padding;
border-radius: $border-radius-md;
box-shadow: $shadow-md;
}
// Flex layout mixins
@mixin flex-center {
display: flex;
align-items: center;
justify-content: center;
}
@mixin flex-between {
display: flex;
align-items: center;
justify-content: space-between;
}
@mixin flex-column {
display: flex;
flex-direction: column;
}
// Table styling mixin
@mixin table-base {
width: 100%;
border-collapse: collapse;
background: var(--card-bg);
color: var(--text-color);
border-radius: $border-radius-md;
overflow: hidden;
box-shadow: $shadow-md;
th, td {
padding: $spacing-sm $spacing-md;
text-align: left;
border-bottom: 1px solid var(--border-color);
color: var(--text-color);
}
th {
background-color: var(--table-header-bg);
font-weight: bold;
}
tr:hover {
background-color: var(--table-hover);
}
}
// Form input mixin
@mixin input-base {
padding: $spacing-sm $spacing-md;
border: 1px solid var(--input-border);
border-radius: $border-radius-sm;
font-size: $font-size-md;
font-family: $font-family;
background-color: var(--input-bg);
color: var(--text-color);
transition: border-color 0.2s ease;
&:focus {
outline: none;
border-color: $primary-color;
box-shadow: 0 0 0 2px rgba($primary-color, 0.2);
}
}
// Status indicator mixin
@mixin status-indicator($bg-color, $text-color) {
padding: $spacing-xs $spacing-sm;
border-radius: $border-radius-lg;
font-size: $font-size-sm;
font-weight: bold;
background-color: $bg-color;
color: $text-color;
}
// Responsive breakpoint mixins
@mixin mobile {
@media (max-width: $mobile) {
@content;
}
}
@mixin tablet {
@media (max-width: $tablet) {
@content;
}
}
@mixin desktop {
@media (min-width: $desktop) {
@content;
}
}

55
src/scss/_variables.scss Normal file
View file

@ -0,0 +1,55 @@
// Color Palette
$primary-color: #2196F3;
$secondary-color: #1976D2;
$success-color: #28a745;
$warning-color: #ffc107;
$danger-color: #dc3545;
$info-color: #17a2b8;
// Theme Colors (CSS Variables will be generated)
$light-bg: #ffffff;
$light-text: #333333;
$light-card-bg: #f8f9fa;
$light-border: #dee2e6;
$dark-bg: #1a1a1a;
$dark-text: #ffffff;
$dark-card-bg: #2d2d2d;
$dark-border: #444444;
// Spacing
$spacing-xs: 4px;
$spacing-sm: 8px;
$spacing-md: 16px;
$spacing-lg: 24px;
$spacing-xl: 32px;
$spacing-xxl: 48px;
// Border Radius
$border-radius-sm: 4px;
$border-radius-md: 8px;
$border-radius-lg: 12px;
$border-radius-full: 50%;
// Shadows
$shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1);
$shadow-md: 0 2px 4px rgba(0, 0, 0, 0.1);
$shadow-lg: 0 4px 8px rgba(0, 0, 0, 0.15);
// Typography
$font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
$font-size-sm: 12px;
$font-size-md: 14px;
$font-size-lg: 16px;
$font-size-xl: 18px;
$font-size-xxl: 24px;
// Z-Index
$z-index-modal: 1000;
$z-index-dropdown: 100;
$z-index-tooltip: 200;
// Breakpoints
$mobile: 768px;
$tablet: 1024px;
$desktop: 1200px;

142
src/scss/main.scss Normal file
View file

@ -0,0 +1,142 @@
// Import variables and mixins first
@use 'variables' as *;
@use 'mixins' as *;
@use 'pages/admin';
// Import existing styles from style.css (converted to SCSS)
// We'll keep the existing theme variables and base styles
// Base styles and theme variables (from existing style.css)
:root {
/* Light theme (default) */
--background-color: #{$light-bg};
--text-color: #{$light-text};
--card-bg: #{$light-card-bg};
--border-color: #{$light-border};
--input-bg: #{$light-bg};
--input-border: #{$light-border};
--table-header-bg: #e9ecef;
--table-hover: #f5f5f5;
--shadow: rgba(0, 0, 0, 0.1);
}
[data-theme="dark"] {
/* Dark theme */
--background-color: #{$dark-bg};
--text-color: #{$dark-text};
--card-bg: #{$dark-card-bg};
--border-color: #{$dark-border};
--input-bg: #{$dark-card-bg};
--input-border: #{$dark-border};
--table-header-bg: #3d3d3d;
--table-hover: #3d3d3d;
--shadow: rgba(0, 0, 0, 0.3);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: $font-family;
background-color: var(--background-color);
color: var(--text-color);
line-height: 1.6;
min-height: 100vh;
transition: background-color 0.3s ease, color 0.3s ease;
}
// Base form styles
.form-group {
margin-bottom: $spacing-md;
label {
display: block;
margin-bottom: $spacing-xs;
font-weight: 500;
}
input, select, textarea {
@include input-base;
width: 100%;
}
}
button {
@include button;
&:disabled {
opacity: 0.6;
cursor: not-allowed;
}
}
// Message styles
.message {
padding: $spacing-md;
border-radius: $border-radius-sm;
margin: $spacing-md 0;
display: none;
&.error {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
&.success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
&.info {
background-color: #d1ecf1;
color: #0c5460;
border: 1px solid #bee5eb;
}
}
// Page-specific styles are imported at the top with other @use statements
// Theme toggle styles (common across pages)
.theme-toggle {
@include button($bg-color: transparent);
border: 2px solid var(--border-color);
border-radius: $border-radius-full;
width: 40px;
height: 40px;
@include flex-center;
&:hover {
background-color: var(--table-hover);
transform: none;
}
}
// Utility classes
.text-center { text-align: center; }
.text-left { text-align: left; }
.text-right { text-align: right; }
.mb-sm { margin-bottom: $spacing-sm; }
.mb-md { margin-bottom: $spacing-md; }
.mb-lg { margin-bottom: $spacing-lg; }
.mt-sm { margin-top: $spacing-sm; }
.mt-md { margin-top: $spacing-md; }
.mt-lg { margin-top: $spacing-lg; }
.p-sm { padding: $spacing-sm; }
.p-md { padding: $spacing-md; }
.p-lg { padding: $spacing-lg; }
.d-flex { display: flex; }
.flex-center { @include flex-center; }
.flex-between { @include flex-between; }
.flex-column { @include flex-column; }
.w-100 { width: 100%; }
.h-100 { height: 100%; }

304
src/scss/pages/_admin.scss Normal file
View file

@ -0,0 +1,304 @@
// Import variables and mixins
@use '../variables' as *;
@use '../mixins' as *;
// Admin Page Styles
.admin-container {
max-width: 1200px;
margin: 0 auto;
padding: $spacing-lg;
}
.login-section {
@include card($spacing-xl);
max-width: 400px;
margin: 50px auto;
}
.admin-section {
display: none;
}
// Admin Header
.admin-header {
@include flex-between;
margin-bottom: $spacing-lg;
}
.header-buttons {
display: flex;
gap: $spacing-sm;
align-items: center;
}
.header-btn {
@include button($bg-color: #6c757d);
&.btn-refresh {
@include button($bg-color: $primary-color);
}
&.btn-home {
@include button($bg-color: $success-color);
}
&.btn-logout {
@include button($bg-color: $danger-color);
}
}
.theme-toggle-admin {
background: var(--card-bg) !important;
color: var(--text-color) !important;
border: 2px solid var(--border-color) !important;
width: 40px;
height: 40px;
border-radius: $border-radius-full !important;
@include flex-center;
padding: 0 !important;
}
// Stats Cards
.stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: $spacing-lg;
margin-bottom: $spacing-lg;
}
.stat-card {
@include card;
text-align: center;
}
.stat-number {
font-size: 2em;
font-weight: bold;
color: $primary-color;
}
// Tables
.locations-table {
@include table-base;
margin-top: $spacing-lg;
}
// Buttons
.action-buttons {
display: flex;
gap: 5px;
}
.btn {
@include button($size: sm);
&-edit {
@include button($bg-color: $primary-color, $size: sm);
}
&-delete {
@include button($bg-color: $danger-color, $size: sm);
}
&-save {
@include button($bg-color: $success-color, $size: sm);
}
&-cancel {
@include button($bg-color: #6c757d, $size: sm);
}
}
// Edit State
.edit-row {
background-color: #fff3cd !important;
}
.edit-input {
@include input-base;
width: 100%;
font-size: $font-size-sm;
}
// Status Indicators
.status-indicator {
@include status-indicator(transparent, inherit);
}
.status-active {
@include status-indicator(#d4edda, #155724);
}
.status-expired {
@include status-indicator(#f8d7da, #721c24);
}
// Tabs
.tab-navigation {
display: flex;
margin-bottom: $spacing-lg;
border-bottom: 2px solid var(--border-color);
}
.tab-btn {
@include button($bg-color: transparent, $text-color: var(--text-color));
border-bottom: 3px solid transparent;
border-radius: 0;
&.active {
border-bottom-color: $primary-color;
color: $primary-color;
}
&:hover {
background-color: var(--table-hover);
transform: none;
}
}
.tab-content {
display: none;
&.active {
display: block;
}
}
// Profanity Management
.profanity-management {
.management-section {
@include card;
margin-bottom: $spacing-lg;
h4 {
margin-top: 0;
color: $primary-color;
}
}
.profanity-form {
display: grid;
grid-template-columns: 2fr 1fr 1fr auto;
gap: $spacing-sm;
align-items: end;
margin-bottom: $spacing-lg;
input, select {
@include input-base;
}
}
.test-section {
.test-form {
display: grid;
grid-template-columns: 1fr auto;
gap: $spacing-sm;
align-items: end;
textarea {
@include input-base;
resize: vertical;
min-height: 60px;
}
}
}
}
.test-results {
margin-top: $spacing-md;
padding: $spacing-md;
border-radius: $border-radius-sm;
&.profane {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
&.clean {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
&.empty {
background-color: #f8f9fa;
color: #6c757d;
border: 1px solid #dee2e6;
}
}
.severity-low {
@include status-indicator(#d1ecf1, #0c5460);
}
.severity-medium {
@include status-indicator(#fff3cd, #856404);
}
.severity-high {
@include status-indicator(#f8d7da, #721c24);
}
.action-btn {
@include button($size: sm);
&.danger {
@include button($bg-color: $danger-color, $size: sm);
}
}
// Persistent Toggle
.persistent-toggle {
&.active {
@include button($bg-color: $warning-color, $text-color: #000);
}
&.inactive {
@include button($bg-color: #6c757d);
}
}
// Responsive Design
@include mobile {
.admin-container {
padding: $spacing-md;
}
.admin-header {
flex-direction: column;
gap: $spacing-md;
align-items: stretch;
}
.header-buttons {
justify-content: center;
flex-wrap: wrap;
}
.stats {
grid-template-columns: 1fr 1fr;
}
.locations-table {
font-size: $font-size-sm;
th, td {
padding: $spacing-xs $spacing-xs;
}
.address-cell {
max-width: 100px;
}
}
.btn {
padding: 3px 4px;
font-size: 9px;
}
.profanity-form {
grid-template-columns: 1fr;
gap: $spacing-sm;
}
}