Files
Goa-gel-fullstack/frontend/src/app/layouts/main-layout/main-layout.component.scss
Mahi 80566bf0a2 feat: Goa GEL Blockchain e-Licensing Platform - Full Stack Implementation
Complete implementation of the Goa Government e-Licensing platform with:

Backend:
- NestJS API with JWT authentication
- PostgreSQL database with Knex ORM
- Redis caching and session management
- MinIO document storage
- Hyperledger Besu blockchain integration
- Multi-department workflow system
- Comprehensive API tests (266/282 passing)

Frontend:
- Angular 21 with standalone components
- Angular Material + TailwindCSS UI
- Visual workflow builder
- Document upload with progress tracking
- Blockchain explorer integration
- Role-based dashboards (Admin, Department, Citizen)
- E2E tests with Playwright (37 tests)

Infrastructure:
- Docker Compose orchestration
- Blockscout blockchain explorer
- Development and production configurations
2026-02-07 10:23:29 -04:00

752 lines
13 KiB
SCSS

// =============================================================================
// MAIN LAYOUT - World-Class Government Blockchain Platform
// DBIM v3.0 Compliant | GIGW 3.0 Accessible
// =============================================================================
// Variables
$sidebar-width: 280px;
$sidebar-collapsed-width: 72px;
$header-height: 64px;
$footer-height: 48px;
$transition-speed: 250ms;
// =============================================================================
// APP SHELL
// =============================================================================
.app-shell {
display: flex;
min-height: 100vh;
background-color: var(--dbim-white);
}
// =============================================================================
// SIDEBAR
// =============================================================================
.sidebar {
position: fixed;
top: 0;
left: 0;
height: 100vh;
width: $sidebar-width;
background: linear-gradient(180deg, var(--dbim-blue-dark) 0%, #130640 100%);
display: flex;
flex-direction: column;
z-index: 100;
transition: width $transition-speed ease;
box-shadow: 4px 0 24px rgba(29, 10, 105, 0.15);
&.sidebar-collapsed {
width: $sidebar-collapsed-width;
.sidebar-header {
padding: 16px 12px;
}
.logo-section {
justify-content: center;
}
.emblem-container {
width: 40px;
height: 40px;
}
.nav-item {
padding: 12px;
justify-content: center;
.nav-icon {
margin-right: 0;
}
}
.nav-section-title {
display: none;
}
}
}
// Sidebar Header
.sidebar-header {
padding: 20px 20px 16px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.logo-section {
display: flex;
align-items: center;
gap: 12px;
}
.emblem-container {
width: 48px;
height: 48px;
background: rgba(255, 255, 255, 0.1);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
transition: all $transition-speed ease;
.goa-emblem {
width: 36px;
height: 36px;
object-fit: contain;
filter: brightness(0) invert(1);
}
.emblem-fallback {
mat-icon {
font-size: 28px;
width: 28px;
height: 28px;
color: white;
}
}
}
.logo-text {
display: flex;
flex-direction: column;
overflow: hidden;
.govt-text {
font-size: 14px;
font-weight: 600;
color: white;
white-space: nowrap;
}
.platform-text {
font-size: 11px;
color: rgba(255, 255, 255, 0.7);
white-space: nowrap;
}
}
// Sidebar Navigation
.sidebar-nav {
flex: 1;
padding: 16px 12px;
overflow-y: auto;
&::-webkit-scrollbar {
width: 4px;
}
&::-webkit-scrollbar-track {
background: transparent;
}
&::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.2);
border-radius: 2px;
}
}
.nav-section {
margin-bottom: 24px;
&:last-child {
margin-bottom: 0;
}
}
.nav-section-title {
display: block;
padding: 8px 16px;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
color: rgba(255, 255, 255, 0.4);
}
.nav-item {
display: flex;
align-items: center;
padding: 12px 16px;
margin-bottom: 4px;
border-radius: 12px;
color: rgba(255, 255, 255, 0.8);
text-decoration: none;
transition: all 150ms ease;
cursor: pointer;
&:hover {
background: rgba(255, 255, 255, 0.08);
color: white;
.nav-icon {
transform: scale(1.05);
}
}
&.active {
background: linear-gradient(135deg, rgba(99, 102, 241, 0.3) 0%, rgba(139, 92, 246, 0.2) 100%);
color: white;
box-shadow: 0 4px 12px rgba(99, 102, 241, 0.25);
.nav-icon {
background: linear-gradient(135deg, var(--crypto-indigo) 0%, var(--crypto-purple) 100%);
mat-icon {
color: white;
}
}
}
.nav-icon {
width: 36px;
height: 36px;
border-radius: 10px;
background: rgba(255, 255, 255, 0.08);
display: flex;
align-items: center;
justify-content: center;
margin-right: 12px;
transition: all 150ms ease;
mat-icon {
font-size: 20px;
width: 20px;
height: 20px;
color: rgba(255, 255, 255, 0.9);
}
}
.nav-label {
flex: 1;
font-size: 14px;
font-weight: 500;
white-space: nowrap;
}
.nav-badge {
min-width: 20px;
height: 20px;
padding: 0 6px;
border-radius: 10px;
background: var(--dbim-error);
color: white;
font-size: 11px;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
&.pulse {
animation: pulse 2s infinite;
}
}
}
// Sidebar Footer
.sidebar-footer {
padding: 16px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.blockchain-status {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 16px;
background: rgba(255, 255, 255, 0.05);
border-radius: 12px;
}
.blockchain-status-compact {
display: flex;
justify-content: center;
padding: 12px;
}
.status-indicator {
width: 10px;
height: 10px;
border-radius: 50%;
flex-shrink: 0;
&.online {
background: var(--dbim-success);
box-shadow: 0 0 8px var(--dbim-success);
animation: pulse 2s infinite;
}
&.offline {
background: var(--dbim-error);
}
}
.status-text {
display: flex;
flex-direction: column;
.status-label {
font-size: 11px;
color: rgba(255, 255, 255, 0.5);
}
.status-value {
font-size: 13px;
font-weight: 500;
color: white;
}
}
// =============================================================================
// MAIN WRAPPER
// =============================================================================
.main-wrapper {
flex: 1;
margin-left: $sidebar-width;
display: flex;
flex-direction: column;
min-height: 100vh;
transition: margin-left $transition-speed ease;
.sidebar-collapsed + & {
margin-left: $sidebar-collapsed-width;
}
}
// =============================================================================
// TOP HEADER
// =============================================================================
.top-header {
height: $header-height;
background: var(--dbim-white);
border-bottom: 1px solid rgba(29, 10, 105, 0.08);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
position: sticky;
top: 0;
z-index: 50;
backdrop-filter: blur(8px);
background: rgba(255, 255, 255, 0.95);
}
.header-left {
display: flex;
align-items: center;
gap: 16px;
}
.menu-toggle {
color: var(--dbim-grey-3);
&:hover {
color: var(--dbim-blue-dark);
}
}
.breadcrumb {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
color: var(--dbim-grey-2);
}
.header-right {
display: flex;
align-items: center;
gap: 8px;
}
.header-action {
color: var(--dbim-grey-2);
&:hover {
color: var(--dbim-blue-dark);
background: rgba(29, 10, 105, 0.05);
}
}
// User Profile
.user-profile {
margin-left: 8px;
}
.user-button {
display: flex;
align-items: center;
gap: 12px;
padding: 6px 12px 6px 6px;
border-radius: 50px;
background: transparent;
&:hover {
background: rgba(29, 10, 105, 0.05);
}
}
.user-avatar {
width: 36px;
height: 36px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 13px;
font-weight: 600;
color: white;
flex-shrink: 0;
}
.user-avatar-large {
width: 48px;
height: 48px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
font-weight: 600;
color: white;
flex-shrink: 0;
}
.user-info {
display: flex;
flex-direction: column;
align-items: flex-start;
text-align: left;
.user-name {
font-size: 14px;
font-weight: 500;
color: var(--dbim-brown);
line-height: 1.2;
}
.user-role {
font-size: 11px;
color: var(--dbim-grey-2);
}
}
.dropdown-arrow {
font-size: 18px;
color: var(--dbim-grey-2);
}
// User Menu
.user-menu-header {
display: flex;
align-items: center;
gap: 16px;
padding: 16px;
}
.user-details {
display: flex;
flex-direction: column;
.user-name {
font-size: 15px;
font-weight: 600;
color: var(--dbim-brown);
}
.user-email {
font-size: 12px;
color: var(--dbim-grey-2);
margin-top: 2px;
}
.user-badge {
display: inline-block;
padding: 2px 8px;
margin-top: 6px;
background: rgba(29, 10, 105, 0.1);
color: var(--dbim-blue-dark);
font-size: 10px;
font-weight: 600;
border-radius: 4px;
text-transform: uppercase;
letter-spacing: 0.02em;
width: fit-content;
}
}
.logout-btn {
color: var(--dbim-error) !important;
}
// Notification Menu
.notification-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
}
.notification-title {
font-size: 15px;
font-weight: 600;
color: var(--dbim-brown);
}
.mark-read-btn {
font-size: 12px;
}
.notification-list {
max-height: 300px;
overflow-y: auto;
}
.notification-item {
display: flex;
align-items: flex-start;
gap: 12px;
padding: 12px 16px;
cursor: pointer;
transition: background 150ms ease;
&:hover {
background: rgba(29, 10, 105, 0.03);
}
&.unread {
background: rgba(13, 110, 253, 0.05);
}
}
.notification-icon {
width: 36px;
height: 36px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
&.success {
background: rgba(25, 135, 84, 0.1);
color: var(--dbim-success);
}
&.info {
background: rgba(13, 110, 253, 0.1);
color: var(--dbim-info);
}
&.warning {
background: rgba(255, 193, 7, 0.15);
color: #B8860B;
}
&.error {
background: rgba(220, 53, 69, 0.1);
color: var(--dbim-error);
}
mat-icon {
font-size: 18px;
width: 18px;
height: 18px;
}
}
.notification-content {
flex: 1;
display: flex;
flex-direction: column;
.notification-text {
font-size: 13px;
color: var(--dbim-brown);
line-height: 1.4;
}
.notification-time {
font-size: 11px;
color: var(--dbim-grey-2);
margin-top: 4px;
}
}
.view-all-btn {
text-align: center;
font-size: 13px;
color: var(--dbim-info) !important;
}
// =============================================================================
// MAIN CONTENT
// =============================================================================
.main-content {
flex: 1;
padding: 24px;
background: #f8f9fc;
min-height: calc(100vh - #{$header-height} - #{$footer-height});
}
// =============================================================================
// FOOTER - DBIM Compliant
// =============================================================================
.main-footer {
height: $footer-height;
background: var(--dbim-blue-dark);
color: white;
display: flex;
align-items: center;
}
.footer-content {
width: 100%;
padding: 0 24px;
display: flex;
align-items: center;
justify-content: space-between;
}
.footer-left {
display: flex;
align-items: center;
gap: 8px;
}
.footer-text {
font-size: 12px;
color: rgba(255, 255, 255, 0.8);
}
.footer-divider {
color: rgba(255, 255, 255, 0.3);
}
.footer-right {
display: flex;
align-items: center;
gap: 16px;
}
.footer-link {
font-size: 12px;
color: rgba(255, 255, 255, 0.8);
text-decoration: none;
transition: color 150ms ease;
&:hover {
color: white;
}
}
// =============================================================================
// ANIMATIONS
// =============================================================================
@keyframes pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.6;
}
}
// =============================================================================
// RESPONSIVE
// =============================================================================
@media (max-width: 1024px) {
.sidebar {
width: $sidebar-collapsed-width;
.sidebar-header {
padding: 16px 12px;
}
.logo-section {
justify-content: center;
}
.emblem-container {
width: 40px;
height: 40px;
}
.logo-text,
.nav-label,
.nav-section-title {
display: none;
}
.nav-item {
padding: 12px;
justify-content: center;
.nav-icon {
margin-right: 0;
}
}
.blockchain-status {
padding: 12px;
justify-content: center;
.status-text {
display: none;
}
}
}
.main-wrapper {
margin-left: $sidebar-collapsed-width;
}
}
@media (max-width: 768px) {
.sidebar {
transform: translateX(-100%);
width: $sidebar-width;
&:not(.sidebar-collapsed) {
transform: translateX(0);
}
.logo-text,
.nav-label,
.nav-section-title {
display: block;
}
.nav-item {
padding: 12px 16px;
justify-content: flex-start;
.nav-icon {
margin-right: 12px;
}
}
}
.main-wrapper {
margin-left: 0;
}
.top-header {
padding: 0 16px;
}
.main-content {
padding: 16px;
}
.footer-content {
flex-direction: column;
gap: 8px;
padding: 8px 16px;
text-align: center;
}
.footer-right {
gap: 12px;
}
}