Update project structure and dependencies for PINEPIM Asset Management System
- Added Angular Material and PrimeNG components for UI enhancements. - Updated package dependencies including Angular core libraries and added new packages for animations and layout. - Enhanced README with detailed project features, installation instructions, and technology stack. - Implemented responsive design for sidebar and navigation. - Updated styles with CSS variables and Material theme integration. - Refactored app component for improved structure and functionality. - Added routing for main application features including dashboard, asset management, and admin settings. - Introduced analytics configuration in angular.json.
This commit is contained in:
parent
481f8d024d
commit
f77b6a647c
181
README.md
181
README.md
@ -1,59 +1,170 @@
|
|||||||
# PineAssetWebapp
|
# PINEPIM Asset Management System
|
||||||
|
|
||||||
This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 19.2.15.
|
A comprehensive enterprise-grade asset management system built with Angular 19 and Angular Material.
|
||||||
|
|
||||||
## Development server
|
## 🚀 Features
|
||||||
|
|
||||||
To start a local development server, run:
|
### Core Modules
|
||||||
|
- **Authentication System** - Secure login with role-based access
|
||||||
|
- **Dashboard** - Overview with statistics, recent activities, and maintenance alerts
|
||||||
|
- **Purchase Orders** - Track and manage purchase orders with filtering and export
|
||||||
|
- **Asset Registration** - Register and manage new assets with status tracking
|
||||||
|
- **Asset List** - Comprehensive asset inventory with search and filtering
|
||||||
|
- **Asset Transfer** - Track asset transfers between departments/branches
|
||||||
|
- **Asset Retirement** - Manage asset retirement and disposal
|
||||||
|
- **Admin Settings** - User management and system administration
|
||||||
|
|
||||||
```bash
|
### Design System
|
||||||
ng serve
|
- **Color Palette:**
|
||||||
|
- Primary Red: `#c80000`
|
||||||
|
- Dark Gray: `#1a1a1a`
|
||||||
|
- Medium Gray: `#2e2e2e`
|
||||||
|
- White: `#ffffff`
|
||||||
|
- Light Gray: `#b0b0b0`
|
||||||
|
- Accent Green: `#00cc66`
|
||||||
|
- Yellow/Orange: `#ffaa00`
|
||||||
|
- Red: `#ff4444`
|
||||||
|
|
||||||
|
- **Enterprise UI/UX:**
|
||||||
|
- Responsive design for desktop and tablet
|
||||||
|
- Material Design components
|
||||||
|
- Custom CSS with CSS variables
|
||||||
|
- Professional color scheme
|
||||||
|
- Smooth animations and transitions
|
||||||
|
|
||||||
|
## 🛠️ Technology Stack
|
||||||
|
|
||||||
|
- **Frontend:** Angular 19.2.0
|
||||||
|
- **UI Framework:** Angular Material
|
||||||
|
- **Styling:** Custom CSS with CSS Variables
|
||||||
|
- **Routing:** Angular Router with lazy loading
|
||||||
|
- **State Management:** Angular Services
|
||||||
|
- **Build Tool:** Angular CLI
|
||||||
|
|
||||||
|
## 📦 Installation
|
||||||
|
|
||||||
|
1. **Clone the repository:**
|
||||||
|
```bash
|
||||||
|
git clone <repository-url>
|
||||||
|
cd pine-asset-webapp
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Install dependencies:**
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Start the development server:**
|
||||||
|
```bash
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Open your browser:**
|
||||||
|
Navigate to `http://localhost:4200`
|
||||||
|
|
||||||
|
## 🔐 Demo Credentials
|
||||||
|
|
||||||
|
- **Employee ID:** `admin`
|
||||||
|
- **Password:** `password`
|
||||||
|
|
||||||
|
## 🏗️ Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
├── app/
|
||||||
|
│ ├── auth/
|
||||||
|
│ │ └── login/ # Authentication module
|
||||||
|
│ ├── dashboard/ # Dashboard overview
|
||||||
|
│ ├── purchase-orders/ # Purchase orders management
|
||||||
|
│ ├── asset/
|
||||||
|
│ │ ├── registration/ # Asset registration
|
||||||
|
│ │ ├── list/ # Asset inventory
|
||||||
|
│ │ ├── transfer/ # Asset transfers
|
||||||
|
│ │ └── retirement/ # Asset retirement
|
||||||
|
│ ├── admin/ # Admin settings
|
||||||
|
│ ├── app.component.* # Main layout
|
||||||
|
│ ├── app.routes.ts # Routing configuration
|
||||||
|
│ └── app.config.ts # App configuration
|
||||||
|
├── styles.css # Global styles
|
||||||
|
└── main.ts # Application entry point
|
||||||
```
|
```
|
||||||
|
|
||||||
Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files.
|
## 🎨 Design Features
|
||||||
|
|
||||||
## Code scaffolding
|
### Layout
|
||||||
|
- **Top Navigation Bar** - Logo, notifications, user menu
|
||||||
|
- **Left Sidebar** - Navigation menu with role-based visibility
|
||||||
|
- **Main Content Area** - Dynamic content based on route
|
||||||
|
- **Responsive Design** - Mobile and tablet friendly
|
||||||
|
|
||||||
Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
|
### Components
|
||||||
|
- **Data Tables** - Sortable, filterable, paginated
|
||||||
|
- **Forms** - Reactive forms with validation
|
||||||
|
- **Cards** - Information display with consistent styling
|
||||||
|
- **Charts & Statistics** - Dashboard overview widgets
|
||||||
|
- **Status Indicators** - Color-coded status chips
|
||||||
|
- **Action Buttons** - Consistent button styling
|
||||||
|
|
||||||
```bash
|
## 🔧 Development
|
||||||
ng generate component component-name
|
|
||||||
```
|
|
||||||
|
|
||||||
For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
|
### Available Scripts
|
||||||
|
- `npm start` - Start development server
|
||||||
|
- `npm run build` - Build for production
|
||||||
|
- `npm run test` - Run unit tests
|
||||||
|
- `npm run lint` - Run linting
|
||||||
|
|
||||||
```bash
|
### Code Style
|
||||||
ng generate --help
|
- TypeScript strict mode
|
||||||
```
|
- Angular Material components
|
||||||
|
- Custom CSS with CSS variables
|
||||||
|
- Responsive design principles
|
||||||
|
- Accessibility considerations
|
||||||
|
|
||||||
## Building
|
## 📱 Responsive Design
|
||||||
|
|
||||||
To build the project run:
|
The application is fully responsive with breakpoints:
|
||||||
|
- **Desktop:** Full layout with sidebar
|
||||||
|
- **Tablet:** Adjusted spacing and layout
|
||||||
|
- **Mobile:** Collapsible sidebar and mobile-optimized forms
|
||||||
|
|
||||||
```bash
|
## 🎯 Key Features
|
||||||
ng build
|
|
||||||
```
|
|
||||||
|
|
||||||
This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed.
|
### Authentication
|
||||||
|
- Employee ID and password login
|
||||||
|
- Remember me functionality
|
||||||
|
- Role-based access control
|
||||||
|
- Session management
|
||||||
|
|
||||||
## Running unit tests
|
### Asset Management
|
||||||
|
- Complete asset lifecycle tracking
|
||||||
|
- Status management (Active, Maintenance, Retired)
|
||||||
|
- Category-based organization
|
||||||
|
- Search and filtering capabilities
|
||||||
|
|
||||||
To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
|
### Purchase Orders
|
||||||
|
- Year/month/category filtering
|
||||||
|
- Excel export functionality
|
||||||
|
- Status tracking
|
||||||
|
- Detailed purchase information
|
||||||
|
|
||||||
```bash
|
### Admin Functions
|
||||||
ng test
|
- User management
|
||||||
```
|
- System settings
|
||||||
|
- Data export capabilities
|
||||||
|
- Permission management
|
||||||
|
|
||||||
## Running end-to-end tests
|
## 🚀 Deployment
|
||||||
|
|
||||||
For end-to-end (e2e) testing, run:
|
The application is ready for production deployment with:
|
||||||
|
- Optimized build process
|
||||||
|
- Lazy loading for performance
|
||||||
|
- Responsive design
|
||||||
|
- Enterprise-grade security
|
||||||
|
|
||||||
```bash
|
## 📄 License
|
||||||
ng e2e
|
|
||||||
```
|
|
||||||
|
|
||||||
Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
|
This project is proprietary software for PINEPIM Asset Management System.
|
||||||
|
|
||||||
## Additional Resources
|
---
|
||||||
|
|
||||||
For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
|
**PINEPIM Asset Management System** - Enterprise-grade asset management solution
|
||||||
|
|||||||
@ -97,5 +97,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"cli": {
|
||||||
|
"analytics": "07f1ca3b-4240-45d2-9d23-39cd6bd93beb"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
720
package-lock.json
generated
720
package-lock.json
generated
@ -8,16 +8,24 @@
|
|||||||
"name": "pine-asset-webapp",
|
"name": "pine-asset-webapp",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@angular/animations": "^19.2.14",
|
||||||
|
"@angular/cdk": "^19.2.19",
|
||||||
"@angular/common": "^19.2.0",
|
"@angular/common": "^19.2.0",
|
||||||
"@angular/compiler": "^19.2.0",
|
"@angular/compiler": "^19.2.0",
|
||||||
"@angular/core": "^19.2.0",
|
"@angular/core": "^19.2.0",
|
||||||
|
"@angular/flex-layout": "^15.0.0-beta.42",
|
||||||
"@angular/forms": "^19.2.0",
|
"@angular/forms": "^19.2.0",
|
||||||
|
"@angular/material": "^19.2.19",
|
||||||
"@angular/platform-browser": "^19.2.0",
|
"@angular/platform-browser": "^19.2.0",
|
||||||
"@angular/platform-browser-dynamic": "^19.2.0",
|
"@angular/platform-browser-dynamic": "^19.2.0",
|
||||||
"@angular/platform-server": "^19.2.0",
|
"@angular/platform-server": "^19.2.0",
|
||||||
"@angular/router": "^19.2.0",
|
"@angular/router": "^19.2.0",
|
||||||
"@angular/ssr": "^19.2.15",
|
"@angular/ssr": "^19.2.15",
|
||||||
|
"@types/chart.js": "^2.9.41",
|
||||||
|
"chart.js": "^4.5.0",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
|
"primeicons": "^7.0.0",
|
||||||
|
"primeng": "^17.18.15",
|
||||||
"rxjs": "~7.8.0",
|
"rxjs": "~7.8.0",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
"zone.js": "~0.15.0"
|
"zone.js": "~0.15.0"
|
||||||
@ -204,6 +212,73 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@angular-devkit/build-angular/node_modules/autoprefixer": {
|
||||||
|
"version": "10.4.20",
|
||||||
|
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz",
|
||||||
|
"integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/postcss/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tidelift",
|
||||||
|
"url": "https://tidelift.com/funding/github/npm/autoprefixer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"browserslist": "^4.23.3",
|
||||||
|
"caniuse-lite": "^1.0.30001646",
|
||||||
|
"fraction.js": "^4.3.7",
|
||||||
|
"normalize-range": "^0.1.2",
|
||||||
|
"picocolors": "^1.0.1",
|
||||||
|
"postcss-value-parser": "^4.2.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"autoprefixer": "bin/autoprefixer"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^10 || ^12 || >=14"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"postcss": "^8.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@angular-devkit/build-angular/node_modules/postcss": {
|
||||||
|
"version": "8.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.2.tgz",
|
||||||
|
"integrity": "sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/postcss/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tidelift",
|
||||||
|
"url": "https://tidelift.com/funding/github/npm/postcss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"nanoid": "^3.3.8",
|
||||||
|
"picocolors": "^1.1.1",
|
||||||
|
"source-map-js": "^1.2.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^10 || ^12 || >=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@angular-devkit/build-angular/node_modules/rxjs": {
|
"node_modules/@angular-devkit/build-angular/node_modules/rxjs": {
|
||||||
"version": "7.8.1",
|
"version": "7.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
|
||||||
@ -311,6 +386,22 @@
|
|||||||
"tslib": "^2.1.0"
|
"tslib": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@angular/animations": {
|
||||||
|
"version": "19.2.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-19.2.14.tgz",
|
||||||
|
"integrity": "sha512-xhl8fLto5HHJdVj8Nb6EoBEiTAcXuWDYn1q5uHcGxyVH3kiwENWy/2OQXgCr2CuWo2e6hNUGzSLf/cjbsMNqEA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/common": "19.2.14",
|
||||||
|
"@angular/core": "19.2.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@angular/build": {
|
"node_modules/@angular/build": {
|
||||||
"version": "19.2.15",
|
"version": "19.2.15",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/build/-/build-19.2.15.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/build/-/build-19.2.15.tgz",
|
||||||
@ -469,33 +560,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@angular/build/node_modules/vite/node_modules/postcss": {
|
"node_modules/@angular/cdk": {
|
||||||
"version": "8.5.6",
|
"version": "19.2.19",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-19.2.19.tgz",
|
||||||
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
|
"integrity": "sha512-PCpJagurPBqciqcq4Z8+3OtKLb7rSl4w/qBJoIMua8CgnrjvA1i+SWawhdtfI1zlY8FSwhzLwXV0CmWWfFzQPg==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/postcss/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "tidelift",
|
|
||||||
"url": "https://tidelift.com/funding/github/npm/postcss"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/ai"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.11",
|
"parse5": "^7.1.2",
|
||||||
"picocolors": "^1.1.1",
|
"tslib": "^2.3.0"
|
||||||
"source-map-js": "^1.2.1"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"peerDependencies": {
|
||||||
"node": "^10 || ^12 || >=14"
|
"@angular/common": "^19.0.0 || ^20.0.0",
|
||||||
|
"@angular/core": "^19.0.0 || ^20.0.0",
|
||||||
|
"rxjs": "^6.5.3 || ^7.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@angular/cli": {
|
"node_modules/@angular/cli": {
|
||||||
@ -653,6 +730,23 @@
|
|||||||
"zone.js": "~0.15.0"
|
"zone.js": "~0.15.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@angular/flex-layout": {
|
||||||
|
"version": "15.0.0-beta.42",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-15.0.0-beta.42.tgz",
|
||||||
|
"integrity": "sha512-cTAPVMMxnyIFwpZwdq0PL5mdP9Qh+R8MB7ZBezVaN3Rz2fRrkagzKpLvPX3TFzepXrvHBdpKsU4b8u+NxEC/6g==",
|
||||||
|
"deprecated": "This package has been deprecated. Please see https://blog.angular.io/modern-css-in-angular-layouts-4a259dca9127",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.3.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/cdk": ">=15.0.0",
|
||||||
|
"@angular/common": ">=15.0.2",
|
||||||
|
"@angular/core": ">=15.0.2",
|
||||||
|
"@angular/platform-browser": ">=15.0.2",
|
||||||
|
"rxjs": "^6.5.3 || ^7.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@angular/forms": {
|
"node_modules/@angular/forms": {
|
||||||
"version": "19.2.14",
|
"version": "19.2.14",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-19.2.14.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-19.2.14.tgz",
|
||||||
@ -671,6 +765,23 @@
|
|||||||
"rxjs": "^6.5.3 || ^7.4.0"
|
"rxjs": "^6.5.3 || ^7.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@angular/material": {
|
||||||
|
"version": "19.2.19",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular/material/-/material-19.2.19.tgz",
|
||||||
|
"integrity": "sha512-auIE6JUzTIA3LyYklh9J/T7u64crmphxUBgAa0zcOMDog6SYfwbNe9YeLQqua5ek4OUAOdK/BHHfVl5W5iaUoQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.3.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/cdk": "19.2.19",
|
||||||
|
"@angular/common": "^19.0.0 || ^20.0.0",
|
||||||
|
"@angular/core": "^19.0.0 || ^20.0.0",
|
||||||
|
"@angular/forms": "^19.0.0 || ^20.0.0",
|
||||||
|
"@angular/platform-browser": "^19.0.0 || ^20.0.0",
|
||||||
|
"rxjs": "^6.5.3 || ^7.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@angular/platform-browser": {
|
"node_modules/@angular/platform-browser": {
|
||||||
"version": "19.2.14",
|
"version": "19.2.14",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-19.2.14.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-19.2.14.tgz",
|
||||||
@ -3487,6 +3598,12 @@
|
|||||||
"tslib": "2"
|
"tslib": "2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@kurkle/color": {
|
||||||
|
"version": "0.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz",
|
||||||
|
"integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@leichtgewicht/ip-codec": {
|
"node_modules/@leichtgewicht/ip-codec": {
|
||||||
"version": "2.0.5",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz",
|
||||||
@ -4848,21 +4965,6 @@
|
|||||||
"linux"
|
"linux"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
|
|
||||||
"version": "4.46.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.2.tgz",
|
|
||||||
"integrity": "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==",
|
|
||||||
"cpu": [
|
|
||||||
"ppc64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"peer": true
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||||
"version": "4.34.8",
|
"version": "4.34.8",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.8.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.8.tgz",
|
||||||
@ -4877,21 +4979,6 @@
|
|||||||
"linux"
|
"linux"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
|
||||||
"version": "4.46.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.2.tgz",
|
|
||||||
"integrity": "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==",
|
|
||||||
"cpu": [
|
|
||||||
"riscv64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"peer": true
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||||
"version": "4.34.8",
|
"version": "4.34.8",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.8.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.8.tgz",
|
||||||
@ -5164,6 +5251,15 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/chart.js": {
|
||||||
|
"version": "2.9.41",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.9.41.tgz",
|
||||||
|
"integrity": "sha512-3dvkDvueckY83UyUXtJMalYoH6faOLkWQoaTlJgB4Djde3oORmNP0Jw85HtzTuXyliUHcdp704s0mZFQKio/KQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"moment": "^2.10.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/connect": {
|
"node_modules/@types/connect": {
|
||||||
"version": "3.4.38",
|
"version": "3.4.38",
|
||||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
|
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
|
||||||
@ -5808,44 +5904,6 @@
|
|||||||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
|
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/autoprefixer": {
|
|
||||||
"version": "10.4.20",
|
|
||||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz",
|
|
||||||
"integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==",
|
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/postcss/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "tidelift",
|
|
||||||
"url": "https://tidelift.com/funding/github/npm/autoprefixer"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/ai"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"browserslist": "^4.23.3",
|
|
||||||
"caniuse-lite": "^1.0.30001646",
|
|
||||||
"fraction.js": "^4.3.7",
|
|
||||||
"normalize-range": "^0.1.2",
|
|
||||||
"picocolors": "^1.0.1",
|
|
||||||
"postcss-value-parser": "^4.2.0"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"autoprefixer": "bin/autoprefixer"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^10 || ^12 || >=14"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"postcss": "^8.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/babel-loader": {
|
"node_modules/babel-loader": {
|
||||||
"version": "9.2.1",
|
"version": "9.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.2.1.tgz",
|
||||||
@ -6403,6 +6461,18 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/chart.js": {
|
||||||
|
"version": "4.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.0.tgz",
|
||||||
|
"integrity": "sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@kurkle/color": "^0.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"pnpm": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/chokidar": {
|
"node_modules/chokidar": {
|
||||||
"version": "4.0.3",
|
"version": "4.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
|
||||||
@ -10447,6 +10517,15 @@
|
|||||||
"mkdirp": "bin/cmd.js"
|
"mkdirp": "bin/cmd.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/moment": {
|
||||||
|
"version": "2.30.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
|
||||||
|
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/mrmime": {
|
"node_modules/mrmime": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
|
||||||
@ -11260,7 +11339,6 @@
|
|||||||
"version": "7.3.0",
|
"version": "7.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
|
||||||
"integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
|
"integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"entities": "^6.0.0"
|
"entities": "^6.0.0"
|
||||||
@ -11301,7 +11379,6 @@
|
|||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
|
||||||
"integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
|
"integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
|
||||||
"dev": true,
|
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.12"
|
"node": ">=0.12"
|
||||||
@ -11457,9 +11534,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.5.2",
|
"version": "8.5.6",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
|
||||||
"integrity": "sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==",
|
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -11477,7 +11554,7 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.8",
|
"nanoid": "^3.3.11",
|
||||||
"picocolors": "^1.1.1",
|
"picocolors": "^1.1.1",
|
||||||
"source-map-js": "^1.2.1"
|
"source-map-js": "^1.2.1"
|
||||||
},
|
},
|
||||||
@ -11608,6 +11685,28 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/primeicons": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/primeicons/-/primeicons-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-jK3Et9UzwzTsd6tzl2RmwrVY/b8raJ3QZLzoDACj+oTJ0oX7L9Hy+XnVwgo4QVKlKpnP/Ur13SXV/pVh4LzaDw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/primeng": {
|
||||||
|
"version": "17.18.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/primeng/-/primeng-17.18.15.tgz",
|
||||||
|
"integrity": "sha512-66iKLPBxuZguebSylKbAst5V3Qz+2dbzT+oCHQnCbv4Gu4JH6WqbBJWr283HacQB1mUNGvyxgcHVVPhQbnEXvA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.3.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/common": "^17.0.0 || ^18.0.0",
|
||||||
|
"@angular/core": "^17.0.0 || ^18.0.0",
|
||||||
|
"@angular/forms": "^17.0.0 || ^18.0.0",
|
||||||
|
"rxjs": "^6.0.0 || ^7.8.1",
|
||||||
|
"zone.js": "~0.14.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/proc-log": {
|
"node_modules/proc-log": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz",
|
||||||
@ -13726,431 +13825,6 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
|
||||||
"version": "6.3.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
|
|
||||||
"integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
|
||||||
"esbuild": "^0.25.0",
|
|
||||||
"fdir": "^6.4.4",
|
|
||||||
"picomatch": "^4.0.2",
|
|
||||||
"postcss": "^8.5.3",
|
|
||||||
"rollup": "^4.34.9",
|
|
||||||
"tinyglobby": "^0.2.13"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"vite": "bin/vite.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^18.0.0 || ^20.0.0 || >=22.0.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/vitejs/vite?sponsor=1"
|
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"fsevents": "~2.3.3"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
|
|
||||||
"jiti": ">=1.21.0",
|
|
||||||
"less": "*",
|
|
||||||
"lightningcss": "^1.21.0",
|
|
||||||
"sass": "*",
|
|
||||||
"sass-embedded": "*",
|
|
||||||
"stylus": "*",
|
|
||||||
"sugarss": "*",
|
|
||||||
"terser": "^5.16.0",
|
|
||||||
"tsx": "^4.8.1",
|
|
||||||
"yaml": "^2.4.2"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"@types/node": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"jiti": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"less": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"lightningcss": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"sass": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"sass-embedded": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"stylus": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"sugarss": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"terser": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"tsx": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"yaml": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/vite/node_modules/@rollup/rollup-android-arm-eabi": {
|
|
||||||
"version": "4.46.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.2.tgz",
|
|
||||||
"integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==",
|
|
||||||
"cpu": [
|
|
||||||
"arm"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"android"
|
|
||||||
],
|
|
||||||
"peer": true
|
|
||||||
},
|
|
||||||
"node_modules/vite/node_modules/@rollup/rollup-android-arm64": {
|
|
||||||
"version": "4.46.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.2.tgz",
|
|
||||||
"integrity": "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"android"
|
|
||||||
],
|
|
||||||
"peer": true
|
|
||||||
},
|
|
||||||
"node_modules/vite/node_modules/@rollup/rollup-darwin-arm64": {
|
|
||||||
"version": "4.46.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.2.tgz",
|
|
||||||
"integrity": "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"peer": true
|
|
||||||
},
|
|
||||||
"node_modules/vite/node_modules/@rollup/rollup-darwin-x64": {
|
|
||||||
"version": "4.46.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.2.tgz",
|
|
||||||
"integrity": "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"peer": true
|
|
||||||
},
|
|
||||||
"node_modules/vite/node_modules/@rollup/rollup-freebsd-arm64": {
|
|
||||||
"version": "4.46.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.2.tgz",
|
|
||||||
"integrity": "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"freebsd"
|
|
||||||
],
|
|
||||||
"peer": true
|
|
||||||
},
|
|
||||||
"node_modules/vite/node_modules/@rollup/rollup-freebsd-x64": {
|
|
||||||
"version": "4.46.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.2.tgz",
|
|
||||||
"integrity": "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"freebsd"
|
|
||||||
],
|
|
||||||
"peer": true
|
|
||||||
},
|
|
||||||
"node_modules/vite/node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
|
||||||
"version": "4.46.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.2.tgz",
|
|
||||||
"integrity": "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==",
|
|
||||||
"cpu": [
|
|
||||||
"arm"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"peer": true
|
|
||||||
},
|
|
||||||
"node_modules/vite/node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
|
||||||
"version": "4.46.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.2.tgz",
|
|
||||||
"integrity": "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==",
|
|
||||||
"cpu": [
|
|
||||||
"arm"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"peer": true
|
|
||||||
},
|
|
||||||
"node_modules/vite/node_modules/@rollup/rollup-linux-arm64-gnu": {
|
|
||||||
"version": "4.46.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.2.tgz",
|
|
||||||
"integrity": "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"peer": true
|
|
||||||
},
|
|
||||||
"node_modules/vite/node_modules/@rollup/rollup-linux-arm64-musl": {
|
|
||||||
"version": "4.46.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.2.tgz",
|
|
||||||
"integrity": "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"peer": true
|
|
||||||
},
|
|
||||||
"node_modules/vite/node_modules/@rollup/rollup-linux-loongarch64-gnu": {
|
|
||||||
"version": "4.46.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.2.tgz",
|
|
||||||
"integrity": "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==",
|
|
||||||
"cpu": [
|
|
||||||
"loong64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"peer": true
|
|
||||||
},
|
|
||||||
"node_modules/vite/node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
|
||||||
"version": "4.46.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.2.tgz",
|
|
||||||
"integrity": "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==",
|
|
||||||
"cpu": [
|
|
||||||
"riscv64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"peer": true
|
|
||||||
},
|
|
||||||
"node_modules/vite/node_modules/@rollup/rollup-linux-s390x-gnu": {
|
|
||||||
"version": "4.46.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.2.tgz",
|
|
||||||
"integrity": "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==",
|
|
||||||
"cpu": [
|
|
||||||
"s390x"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"peer": true
|
|
||||||
},
|
|
||||||
"node_modules/vite/node_modules/@rollup/rollup-linux-x64-gnu": {
|
|
||||||
"version": "4.46.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.2.tgz",
|
|
||||||
"integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"peer": true
|
|
||||||
},
|
|
||||||
"node_modules/vite/node_modules/@rollup/rollup-linux-x64-musl": {
|
|
||||||
"version": "4.46.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.2.tgz",
|
|
||||||
"integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"peer": true
|
|
||||||
},
|
|
||||||
"node_modules/vite/node_modules/@rollup/rollup-win32-arm64-msvc": {
|
|
||||||
"version": "4.46.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.2.tgz",
|
|
||||||
"integrity": "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
],
|
|
||||||
"peer": true
|
|
||||||
},
|
|
||||||
"node_modules/vite/node_modules/@rollup/rollup-win32-ia32-msvc": {
|
|
||||||
"version": "4.46.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.2.tgz",
|
|
||||||
"integrity": "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==",
|
|
||||||
"cpu": [
|
|
||||||
"ia32"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
],
|
|
||||||
"peer": true
|
|
||||||
},
|
|
||||||
"node_modules/vite/node_modules/@rollup/rollup-win32-x64-msvc": {
|
|
||||||
"version": "4.46.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.2.tgz",
|
|
||||||
"integrity": "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
],
|
|
||||||
"peer": true
|
|
||||||
},
|
|
||||||
"node_modules/vite/node_modules/@types/estree": {
|
|
||||||
"version": "1.0.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
|
|
||||||
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"peer": true
|
|
||||||
},
|
|
||||||
"node_modules/vite/node_modules/postcss": {
|
|
||||||
"version": "8.5.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
|
|
||||||
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
|
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/postcss/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "tidelift",
|
|
||||||
"url": "https://tidelift.com/funding/github/npm/postcss"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/ai"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
|
||||||
"nanoid": "^3.3.11",
|
|
||||||
"picocolors": "^1.1.1",
|
|
||||||
"source-map-js": "^1.2.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^10 || ^12 || >=14"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/vite/node_modules/rollup": {
|
|
||||||
"version": "4.46.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz",
|
|
||||||
"integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@types/estree": "1.0.8"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"rollup": "dist/bin/rollup"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.0.0",
|
|
||||||
"npm": ">=8.0.0"
|
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"@rollup/rollup-android-arm-eabi": "4.46.2",
|
|
||||||
"@rollup/rollup-android-arm64": "4.46.2",
|
|
||||||
"@rollup/rollup-darwin-arm64": "4.46.2",
|
|
||||||
"@rollup/rollup-darwin-x64": "4.46.2",
|
|
||||||
"@rollup/rollup-freebsd-arm64": "4.46.2",
|
|
||||||
"@rollup/rollup-freebsd-x64": "4.46.2",
|
|
||||||
"@rollup/rollup-linux-arm-gnueabihf": "4.46.2",
|
|
||||||
"@rollup/rollup-linux-arm-musleabihf": "4.46.2",
|
|
||||||
"@rollup/rollup-linux-arm64-gnu": "4.46.2",
|
|
||||||
"@rollup/rollup-linux-arm64-musl": "4.46.2",
|
|
||||||
"@rollup/rollup-linux-loongarch64-gnu": "4.46.2",
|
|
||||||
"@rollup/rollup-linux-ppc64-gnu": "4.46.2",
|
|
||||||
"@rollup/rollup-linux-riscv64-gnu": "4.46.2",
|
|
||||||
"@rollup/rollup-linux-riscv64-musl": "4.46.2",
|
|
||||||
"@rollup/rollup-linux-s390x-gnu": "4.46.2",
|
|
||||||
"@rollup/rollup-linux-x64-gnu": "4.46.2",
|
|
||||||
"@rollup/rollup-linux-x64-musl": "4.46.2",
|
|
||||||
"@rollup/rollup-win32-arm64-msvc": "4.46.2",
|
|
||||||
"@rollup/rollup-win32-ia32-msvc": "4.46.2",
|
|
||||||
"@rollup/rollup-win32-x64-msvc": "4.46.2",
|
|
||||||
"fsevents": "~2.3.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/void-elements": {
|
"node_modules/void-elements": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz",
|
||||||
|
|||||||
@ -11,16 +11,24 @@
|
|||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@angular/animations": "^19.2.14",
|
||||||
|
"@angular/cdk": "^19.2.19",
|
||||||
"@angular/common": "^19.2.0",
|
"@angular/common": "^19.2.0",
|
||||||
"@angular/compiler": "^19.2.0",
|
"@angular/compiler": "^19.2.0",
|
||||||
"@angular/core": "^19.2.0",
|
"@angular/core": "^19.2.0",
|
||||||
|
"@angular/flex-layout": "^15.0.0-beta.42",
|
||||||
"@angular/forms": "^19.2.0",
|
"@angular/forms": "^19.2.0",
|
||||||
|
"@angular/material": "^19.2.19",
|
||||||
"@angular/platform-browser": "^19.2.0",
|
"@angular/platform-browser": "^19.2.0",
|
||||||
"@angular/platform-browser-dynamic": "^19.2.0",
|
"@angular/platform-browser-dynamic": "^19.2.0",
|
||||||
"@angular/platform-server": "^19.2.0",
|
"@angular/platform-server": "^19.2.0",
|
||||||
"@angular/router": "^19.2.0",
|
"@angular/router": "^19.2.0",
|
||||||
"@angular/ssr": "^19.2.15",
|
"@angular/ssr": "^19.2.15",
|
||||||
|
"@types/chart.js": "^2.9.41",
|
||||||
|
"chart.js": "^4.5.0",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
|
"primeicons": "^7.0.0",
|
||||||
|
"primeng": "^17.18.15",
|
||||||
"rxjs": "~7.8.0",
|
"rxjs": "~7.8.0",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
"zone.js": "~0.15.0"
|
"zone.js": "~0.15.0"
|
||||||
|
|||||||
348
src/app/admin/admin.component.css
Normal file
348
src/app/admin/admin.component.css
Normal file
@ -0,0 +1,348 @@
|
|||||||
|
/* Admin Container */
|
||||||
|
.admin-container {
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Page Header */
|
||||||
|
.page-header {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-content h1 {
|
||||||
|
color: var(--white);
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-content p {
|
||||||
|
color: var(--light-gray);
|
||||||
|
font-size: 16px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Statistics Grid */
|
||||||
|
.stats-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||||
|
gap: 24px;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
background: var(--medium-gray) !important;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 12px;
|
||||||
|
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card:hover {
|
||||||
|
transform: translateY(-4px);
|
||||||
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-icon {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: rgba(200, 0, 0, 0.1);
|
||||||
|
color: var(--primary-red);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-icon.users {
|
||||||
|
background: rgba(0, 204, 102, 0.1);
|
||||||
|
color: var(--accent-green);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-icon.active {
|
||||||
|
background: rgba(0, 204, 102, 0.1);
|
||||||
|
color: var(--accent-green);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-icon.uptime {
|
||||||
|
background: rgba(255, 170, 0, 0.1);
|
||||||
|
color: var(--yellow-orange);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-icon.backup {
|
||||||
|
background: rgba(255, 68, 68, 0.1);
|
||||||
|
color: var(--red);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-icon mat-icon {
|
||||||
|
font-size: 28px;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-info h3 {
|
||||||
|
color: var(--white);
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-info p {
|
||||||
|
color: var(--light-gray);
|
||||||
|
font-size: 14px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tabs Card */
|
||||||
|
.tabs-card {
|
||||||
|
background: var(--medium-gray) !important;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs-card ::ng-deep .mat-mdc-card-content {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-content {
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-header h3 {
|
||||||
|
color: var(--white);
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-header p {
|
||||||
|
color: var(--light-gray);
|
||||||
|
font-size: 14px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-header button {
|
||||||
|
background-color: var(--primary-red) !important;
|
||||||
|
color: var(--white) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tab Styling */
|
||||||
|
::ng-deep .mat-mdc-tab-group {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-tab-header {
|
||||||
|
background-color: rgba(255, 255, 255, 0.02);
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-tab-label {
|
||||||
|
color: var(--light-gray) !important;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-tab-label.mat-mdc-tab-label-active {
|
||||||
|
color: var(--primary-red) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-tab-header-pagination-chevron {
|
||||||
|
border-color: var(--light-gray) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-tab-header-pagination-disabled .mat-mdc-tab-header-pagination-chevron {
|
||||||
|
border-color: rgba(176, 176, 176, 0.3) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-ink-bar {
|
||||||
|
background-color: var(--primary-red) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* User Table */
|
||||||
|
.table-container {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-table {
|
||||||
|
width: 100%;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-table ::ng-deep .mat-mdc-header-cell {
|
||||||
|
background-color: rgba(255, 255, 255, 0.05);
|
||||||
|
color: var(--white);
|
||||||
|
font-weight: 500;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-table ::ng-deep .mat-mdc-cell {
|
||||||
|
color: var(--light-gray);
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-table ::ng-deep .mat-mdc-row:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-table ::ng-deep .mat-mdc-sort-header {
|
||||||
|
color: var(--white) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-table ::ng-deep .mat-mdc-sort-header-arrow {
|
||||||
|
color: var(--light-gray) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Action Buttons */
|
||||||
|
.action-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons button {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons ::ng-deep .mat-mdc-icon-button {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons ::ng-deep .mat-mdc-icon-button.mat-accent {
|
||||||
|
color: var(--yellow-orange) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons ::ng-deep .mat-mdc-icon-button.mat-warn {
|
||||||
|
color: var(--red) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Settings Grid */
|
||||||
|
.settings-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-card {
|
||||||
|
background: rgba(255, 255, 255, 0.02) !important;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-card ::ng-deep .mat-mdc-card-header {
|
||||||
|
padding: 24px 24px 16px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-card ::ng-deep .mat-mdc-card-content {
|
||||||
|
padding: 0 24px 16px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-card ::ng-deep .mat-mdc-card-actions {
|
||||||
|
padding: 0 24px 24px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-card ::ng-deep .mat-mdc-card-title {
|
||||||
|
color: var(--white);
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-card ::ng-deep .mat-mdc-card-subtitle {
|
||||||
|
color: var(--light-gray);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-card ::ng-deep .mat-mdc-card-content p {
|
||||||
|
color: var(--light-gray);
|
||||||
|
font-size: 14px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-card button {
|
||||||
|
background-color: var(--primary-red) !important;
|
||||||
|
color: var(--white) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Chip Styling */
|
||||||
|
::ng-deep .mat-mdc-chip {
|
||||||
|
background-color: rgba(255, 255, 255, 0.1) !important;
|
||||||
|
color: var(--white) !important;
|
||||||
|
font-size: 12px !important;
|
||||||
|
height: 24px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-chip.accent-green {
|
||||||
|
background-color: rgba(0, 204, 102, 0.2) !important;
|
||||||
|
color: var(--accent-green) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-chip.yellow-orange {
|
||||||
|
background-color: rgba(255, 170, 0, 0.2) !important;
|
||||||
|
color: var(--yellow-orange) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-chip.red {
|
||||||
|
background-color: rgba(255, 68, 68, 0.2) !important;
|
||||||
|
color: var(--red) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Design */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.admin-container {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-content h1 {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-header {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-container {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-table {
|
||||||
|
min-width: 900px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Material Design Overrides */
|
||||||
|
::ng-deep .mat-mdc-tooltip {
|
||||||
|
background-color: var(--medium-gray) !important;
|
||||||
|
color: var(--white) !important;
|
||||||
|
font-size: 12px !important;
|
||||||
|
}
|
||||||
219
src/app/admin/admin.component.html
Normal file
219
src/app/admin/admin.component.html
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
<div class="admin-container">
|
||||||
|
<div class="page-header">
|
||||||
|
<div class="header-content">
|
||||||
|
<h1>Admin Settings</h1>
|
||||||
|
<p>Manage users, permissions, and system settings</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- System Statistics -->
|
||||||
|
<div class="stats-grid">
|
||||||
|
<mat-card class="stat-card">
|
||||||
|
<mat-card-content>
|
||||||
|
<div class="stat-content">
|
||||||
|
<div class="stat-icon users">
|
||||||
|
<mat-icon>people</mat-icon>
|
||||||
|
</div>
|
||||||
|
<div class="stat-info">
|
||||||
|
<h3>{{systemStats.totalUsers}}</h3>
|
||||||
|
<p>Total Users</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
|
||||||
|
<mat-card class="stat-card">
|
||||||
|
<mat-card-content>
|
||||||
|
<div class="stat-content">
|
||||||
|
<div class="stat-icon active">
|
||||||
|
<mat-icon>check_circle</mat-icon>
|
||||||
|
</div>
|
||||||
|
<div class="stat-info">
|
||||||
|
<h3>{{systemStats.activeUsers}}</h3>
|
||||||
|
<p>Active Users</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
|
||||||
|
<mat-card class="stat-card">
|
||||||
|
<mat-card-content>
|
||||||
|
<div class="stat-content">
|
||||||
|
<div class="stat-icon uptime">
|
||||||
|
<mat-icon>trending_up</mat-icon>
|
||||||
|
</div>
|
||||||
|
<div class="stat-info">
|
||||||
|
<h3>{{systemStats.systemUptime}}</h3>
|
||||||
|
<p>System Uptime</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
|
||||||
|
<mat-card class="stat-card">
|
||||||
|
<mat-card-content>
|
||||||
|
<div class="stat-content">
|
||||||
|
<div class="stat-icon backup">
|
||||||
|
<mat-icon>backup</mat-icon>
|
||||||
|
</div>
|
||||||
|
<div class="stat-info">
|
||||||
|
<h3>{{systemStats.lastBackup}}</h3>
|
||||||
|
<p>Last Backup</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Admin Tabs -->
|
||||||
|
<mat-card class="tabs-card">
|
||||||
|
<mat-card-content>
|
||||||
|
<mat-tab-group [selectedIndex]="selectedTabIndex">
|
||||||
|
<mat-tab label="User Management">
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="tab-header">
|
||||||
|
<h3>User Management</h3>
|
||||||
|
<p>Manage employee accounts and permissions</p>
|
||||||
|
<button mat-raised-button color="primary" (click)="createUser()">
|
||||||
|
<mat-icon>person_add</mat-icon>
|
||||||
|
Add User
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-container">
|
||||||
|
<table mat-table [dataSource]="userDataSource" matSort class="user-table">
|
||||||
|
|
||||||
|
<!-- Employee ID Column -->
|
||||||
|
<ng-container matColumnDef="employeeId">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Employee ID </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.employeeId}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Name Column -->
|
||||||
|
<ng-container matColumnDef="name">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Name </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.name}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Email Column -->
|
||||||
|
<ng-container matColumnDef="email">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Email </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.email}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Department Column -->
|
||||||
|
<ng-container matColumnDef="department">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Department </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.department}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Role Column -->
|
||||||
|
<ng-container matColumnDef="role">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Role </th>
|
||||||
|
<td mat-cell *matCellDef="let element">
|
||||||
|
<mat-chip [class]="getRoleColor(element.role)">
|
||||||
|
{{element.role}}
|
||||||
|
</mat-chip>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Status Column -->
|
||||||
|
<ng-container matColumnDef="status">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Status </th>
|
||||||
|
<td mat-cell *matCellDef="let element">
|
||||||
|
<mat-chip [class]="getStatusColor(element.status)">
|
||||||
|
{{element.status}}
|
||||||
|
</mat-chip>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Last Login Column -->
|
||||||
|
<ng-container matColumnDef="lastLogin">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Last Login </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.lastLogin}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Actions Column -->
|
||||||
|
<ng-container matColumnDef="actions">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Actions </th>
|
||||||
|
<td mat-cell *matCellDef="let element">
|
||||||
|
<div class="action-buttons">
|
||||||
|
<button mat-icon-button color="accent" (click)="editUser(element)" matTooltip="Edit User">
|
||||||
|
<mat-icon>edit</mat-icon>
|
||||||
|
</button>
|
||||||
|
<button mat-icon-button color="warn" (click)="deleteUser(element)" matTooltip="Delete User">
|
||||||
|
<mat-icon>delete</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="userDisplayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: userDisplayedColumns;"></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-tab>
|
||||||
|
|
||||||
|
<mat-tab label="System Settings">
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="tab-header">
|
||||||
|
<h3>System Settings</h3>
|
||||||
|
<p>Manage system configuration and maintenance</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-grid">
|
||||||
|
<mat-card class="setting-card">
|
||||||
|
<mat-card-header>
|
||||||
|
<mat-card-title>Data Export</mat-card-title>
|
||||||
|
<mat-card-subtitle>Export master data and reports</mat-card-subtitle>
|
||||||
|
</mat-card-header>
|
||||||
|
<mat-card-content>
|
||||||
|
<p>Export all system data including assets, users, and transactions.</p>
|
||||||
|
</mat-card-content>
|
||||||
|
<mat-card-actions>
|
||||||
|
<button mat-raised-button color="primary" (click)="exportMasterData()">
|
||||||
|
<mat-icon>download</mat-icon>
|
||||||
|
Export Data
|
||||||
|
</button>
|
||||||
|
</mat-card-actions>
|
||||||
|
</mat-card>
|
||||||
|
|
||||||
|
<mat-card class="setting-card">
|
||||||
|
<mat-card-header>
|
||||||
|
<mat-card-title>System Backup</mat-card-title>
|
||||||
|
<mat-card-subtitle>Create system backup</mat-card-subtitle>
|
||||||
|
</mat-card-header>
|
||||||
|
<mat-card-content>
|
||||||
|
<p>Create a complete backup of the system database and files.</p>
|
||||||
|
</mat-card-content>
|
||||||
|
<mat-card-actions>
|
||||||
|
<button mat-raised-button color="primary" (click)="backupSystem()">
|
||||||
|
<mat-icon>backup</mat-icon>
|
||||||
|
Backup System
|
||||||
|
</button>
|
||||||
|
</mat-card-actions>
|
||||||
|
</mat-card>
|
||||||
|
|
||||||
|
<mat-card class="setting-card">
|
||||||
|
<mat-card-header>
|
||||||
|
<mat-card-title>System Logs</mat-card-title>
|
||||||
|
<mat-card-subtitle>View system activity logs</mat-card-subtitle>
|
||||||
|
</mat-card-header>
|
||||||
|
<mat-card-content>
|
||||||
|
<p>Monitor system activity, user actions, and error logs.</p>
|
||||||
|
</mat-card-content>
|
||||||
|
<mat-card-actions>
|
||||||
|
<button mat-raised-button color="primary" (click)="viewSystemLogs()">
|
||||||
|
<mat-icon>list</mat-icon>
|
||||||
|
View Logs
|
||||||
|
</button>
|
||||||
|
</mat-card-actions>
|
||||||
|
</mat-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-tab>
|
||||||
|
</mat-tab-group>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
</div>
|
||||||
142
src/app/admin/admin.component.ts
Normal file
142
src/app/admin/admin.component.ts
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { MatCardModule } from '@angular/material/card';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatTabsModule } from '@angular/material/tabs';
|
||||||
|
import { MatTableModule, MatTableDataSource } from '@angular/material/table';
|
||||||
|
import { MatPaginatorModule, MatPaginator } from '@angular/material/paginator';
|
||||||
|
import { MatSortModule, MatSort } from '@angular/material/sort';
|
||||||
|
import { MatChipsModule } from '@angular/material/chips';
|
||||||
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-admin',
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
MatCardModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatTabsModule,
|
||||||
|
MatTableModule,
|
||||||
|
MatPaginatorModule,
|
||||||
|
MatSortModule,
|
||||||
|
MatChipsModule,
|
||||||
|
MatTooltipModule
|
||||||
|
],
|
||||||
|
templateUrl: './admin.component.html',
|
||||||
|
styleUrl: './admin.component.css'
|
||||||
|
})
|
||||||
|
export class AdminComponent implements OnInit {
|
||||||
|
|
||||||
|
selectedTabIndex = 0;
|
||||||
|
|
||||||
|
// Users data
|
||||||
|
users = [
|
||||||
|
{
|
||||||
|
employeeId: 'EMP001',
|
||||||
|
name: 'John Smith',
|
||||||
|
email: 'john.smith@pinepim.com',
|
||||||
|
department: 'IT Department',
|
||||||
|
role: 'Admin',
|
||||||
|
status: 'Active',
|
||||||
|
lastLogin: '2024-01-15 14:30'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
employeeId: 'EMP002',
|
||||||
|
name: 'Sarah Johnson',
|
||||||
|
email: 'sarah.johnson@pinepim.com',
|
||||||
|
department: 'HR Department',
|
||||||
|
role: 'User',
|
||||||
|
status: 'Active',
|
||||||
|
lastLogin: '2024-01-15 12:15'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
employeeId: 'EMP003',
|
||||||
|
name: 'Mike Wilson',
|
||||||
|
email: 'mike.wilson@pinepim.com',
|
||||||
|
department: 'Production',
|
||||||
|
role: 'User',
|
||||||
|
status: 'Active',
|
||||||
|
lastLogin: '2024-01-15 09:45'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
employeeId: 'EMP004',
|
||||||
|
name: 'David Brown',
|
||||||
|
email: 'david.brown@pinepim.com',
|
||||||
|
department: 'Sales Department',
|
||||||
|
role: 'User',
|
||||||
|
status: 'Inactive',
|
||||||
|
lastLogin: '2024-01-10 16:20'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
userDisplayedColumns: string[] = ['employeeId', 'name', 'email', 'department', 'role', 'status', 'lastLogin', 'actions'];
|
||||||
|
userDataSource: MatTableDataSource<any>;
|
||||||
|
|
||||||
|
// System statistics
|
||||||
|
systemStats = {
|
||||||
|
totalUsers: 45,
|
||||||
|
activeUsers: 42,
|
||||||
|
totalAssets: 1247,
|
||||||
|
totalValue: 28475000,
|
||||||
|
systemUptime: '99.8%',
|
||||||
|
lastBackup: '2024-01-15 02:00'
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.userDataSource = new MatTableDataSource(this.users);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
getStatusColor(status: string): string {
|
||||||
|
switch (status) {
|
||||||
|
case 'Active':
|
||||||
|
return 'accent-green';
|
||||||
|
case 'Inactive':
|
||||||
|
return 'red';
|
||||||
|
default:
|
||||||
|
return 'light-gray';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getRoleColor(role: string): string {
|
||||||
|
switch (role) {
|
||||||
|
case 'Admin':
|
||||||
|
return 'red';
|
||||||
|
case 'User':
|
||||||
|
return 'yellow-orange';
|
||||||
|
default:
|
||||||
|
return 'light-gray';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createUser() {
|
||||||
|
console.log('Create new user');
|
||||||
|
}
|
||||||
|
|
||||||
|
editUser(user: any) {
|
||||||
|
console.log('Edit user:', user.employeeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteUser(user: any) {
|
||||||
|
if (confirm(`Are you sure you want to delete user ${user.name}?`)) {
|
||||||
|
console.log('Delete user:', user.employeeId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exportMasterData() {
|
||||||
|
console.log('Export master data');
|
||||||
|
}
|
||||||
|
|
||||||
|
backupSystem() {
|
||||||
|
console.log('Backup system');
|
||||||
|
}
|
||||||
|
|
||||||
|
viewSystemLogs() {
|
||||||
|
console.log('View system logs');
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,517 @@
|
|||||||
|
@import "primeicons/primeicons.css";
|
||||||
|
|
||||||
|
/* CSS Variables for Color Palette */
|
||||||
|
:root {
|
||||||
|
--primary-red: #c80000;
|
||||||
|
--dark-gray: #1a1a1a;
|
||||||
|
--medium-gray: #2e2e2e;
|
||||||
|
--white: #ffffff;
|
||||||
|
--light-gray: #b0b0b0;
|
||||||
|
--accent-green: #00cc66;
|
||||||
|
--yellow-orange: #ffaa00;
|
||||||
|
--red: #ff4444;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main App Container */
|
||||||
|
.app-container {
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Top Navbar */
|
||||||
|
.top-navbar {
|
||||||
|
background-color: var(--dark-gray);
|
||||||
|
color: var(--white);
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
z-index: 1000;
|
||||||
|
height: 50px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
border-bottom: 2px solid var(--primary-red);
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-text-white {
|
||||||
|
color: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-text-red {
|
||||||
|
color: var(--primary-red);
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-btn {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: var(--white);
|
||||||
|
padding: 0.25rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-badge {
|
||||||
|
position: absolute;
|
||||||
|
top: -5px;
|
||||||
|
right: -5px;
|
||||||
|
background-color: var(--red);
|
||||||
|
color: var(--white);
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-menu-btn {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: var(--white);
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sidenav Container */
|
||||||
|
.sidenav-container {
|
||||||
|
flex: 1;
|
||||||
|
background-color: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sidebar - Full Width with Text */
|
||||||
|
.sidebar {
|
||||||
|
background-color: var(--dark-gray);
|
||||||
|
width: 250px;
|
||||||
|
border-right: 1px solid #444;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-menu {
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-section {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
color: var(--light-gray);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 500;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
padding: 0.5rem 1rem 0.25rem;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
color: var(--white);
|
||||||
|
text-decoration: none;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 0.125rem 0.5rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
min-height: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
color: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
|
.active-link {
|
||||||
|
background-color: var(--primary-red);
|
||||||
|
color: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
|
.active-link:hover {
|
||||||
|
background-color: #b00000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main Content */
|
||||||
|
.main-content {
|
||||||
|
background-color: var(--white);
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-wrapper {
|
||||||
|
max-width: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
background-color: var(--white);
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Content Header */
|
||||||
|
.content-header {
|
||||||
|
background-color: var(--light-gray);
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-header h1 {
|
||||||
|
color: var(--dark-gray);
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile Menu Toggle */
|
||||||
|
.mobile-menu-toggle {
|
||||||
|
display: none;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: var(--white);
|
||||||
|
padding: 0.25rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-close-btn {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0.5rem;
|
||||||
|
right: 0.5rem;
|
||||||
|
background: var(--primary-red);
|
||||||
|
border: none;
|
||||||
|
color: var(--white);
|
||||||
|
padding: 0.5rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 1001;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-overlay {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom Menu Styling */
|
||||||
|
.custom-menu {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item-custom {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 0.375rem 0.75rem;
|
||||||
|
/* color: var(--white); */
|
||||||
|
text-decoration: none;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 0.0625rem 0.375rem;
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
width: 200px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item-custom:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
color: var(--medium-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-icon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-success {
|
||||||
|
color: var(--accent-green);
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-warning {
|
||||||
|
color: var(--yellow-orange);
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-info {
|
||||||
|
color: var(--light-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-divider {
|
||||||
|
height: 2px;
|
||||||
|
background-color: var(--primary-red);
|
||||||
|
margin: 0.5rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PrimeIcons Styling */
|
||||||
|
.pi {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pi.pi-bars,
|
||||||
|
.pi.pi-times,
|
||||||
|
.pi.pi-bell,
|
||||||
|
.pi.pi-user,
|
||||||
|
.pi.pi-cog,
|
||||||
|
.pi.pi-shield,
|
||||||
|
.pi.pi-sign-out,
|
||||||
|
.pi.pi-check-circle,
|
||||||
|
.pi.pi-exclamation-triangle,
|
||||||
|
.pi.pi-info-circle,
|
||||||
|
.pi.pi-home,
|
||||||
|
.pi.pi-shopping-cart,
|
||||||
|
.pi.pi-question-circle,
|
||||||
|
.pi.pi-wrench {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Design */
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.sidebar {
|
||||||
|
width: 250px; /* Keep full width */
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.top-navbar {
|
||||||
|
padding: 0.25rem 0.75rem;
|
||||||
|
height: 45px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
font-size: 1.125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-menu-toggle {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
width: 250px; /* Keep full width even on mobile */
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 100vh;
|
||||||
|
z-index: 1000;
|
||||||
|
transform: translateX(-100%);
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar.open {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-close-btn {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-overlay {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
padding: 0;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item {
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
min-height: 48px;
|
||||||
|
color: var(--white) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.top-navbar {
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
font-size: 1rem;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-right {
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
width: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item {
|
||||||
|
padding: 0.625rem 0.875rem;
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
min-height: 44px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 360px) {
|
||||||
|
.top-navbar {
|
||||||
|
height: 35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
width: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item {
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
min-height: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animations */
|
||||||
|
.logo {
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo:hover {
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-btn:hover,
|
||||||
|
.user-menu-btn:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item {
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item:hover {
|
||||||
|
transform: translateX(4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compact spacing for better UX */
|
||||||
|
.sidenav-container {
|
||||||
|
height: calc(100vh - 50px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
min-height: calc(100vh - 50px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Optimize for content density */
|
||||||
|
.content-wrapper {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reduce unnecessary spacing */
|
||||||
|
.menu-section {
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
padding: 0.5rem 1rem 0.25rem;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item {
|
||||||
|
margin: 0.125rem 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Full width sidebar optimizations */
|
||||||
|
.sidebar {
|
||||||
|
width: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-menu {
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item {
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
min-height: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
padding: 0.5rem 1rem 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-icon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive adjustments for full width sidebar */
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.sidebar {
|
||||||
|
width: 250px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.sidebar {
|
||||||
|
width: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item {
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
min-height: 48px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,336 +1,135 @@
|
|||||||
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
|
<div class="app-container">
|
||||||
<!-- * * * * * * * * * * * The content below * * * * * * * * * * * -->
|
<!-- Top Navigation Bar -->
|
||||||
<!-- * * * * * * * * * * is only a placeholder * * * * * * * * * * -->
|
<mat-toolbar class="top-navbar">
|
||||||
<!-- * * * * * * * * * * and can be replaced. * * * * * * * * * * -->
|
<div class="navbar-left">
|
||||||
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
|
<!-- Mobile Menu Toggle -->
|
||||||
<!-- * * * * * * * * * Delete the template below * * * * * * * * * -->
|
<button mat-icon-button
|
||||||
<!-- * * * * * * * to get started with your project! * * * * * * * -->
|
class="mobile-menu-toggle"
|
||||||
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
|
(click)="toggleSidebar()"
|
||||||
|
[class.menu-open]="isSidebarOpen">
|
||||||
|
<!-- <i class="pi pi-bars"></i> -->
|
||||||
|
</button>
|
||||||
|
|
||||||
<style>
|
<div class="logo">
|
||||||
:host {
|
<span class="logo-text-white">PINE</span>
|
||||||
--bright-blue: oklch(51.01% 0.274 263.83);
|
<span class="logo-text-red">PIM</span>
|
||||||
--electric-violet: oklch(53.18% 0.28 296.97);
|
|
||||||
--french-violet: oklch(47.66% 0.246 305.88);
|
|
||||||
--vivid-pink: oklch(69.02% 0.277 332.77);
|
|
||||||
--hot-red: oklch(61.42% 0.238 15.34);
|
|
||||||
--orange-red: oklch(63.32% 0.24 31.68);
|
|
||||||
|
|
||||||
--gray-900: oklch(19.37% 0.006 300.98);
|
|
||||||
--gray-700: oklch(36.98% 0.014 302.71);
|
|
||||||
--gray-400: oklch(70.9% 0.015 304.04);
|
|
||||||
|
|
||||||
--red-to-pink-to-purple-vertical-gradient: linear-gradient(
|
|
||||||
180deg,
|
|
||||||
var(--orange-red) 0%,
|
|
||||||
var(--vivid-pink) 50%,
|
|
||||||
var(--electric-violet) 100%
|
|
||||||
);
|
|
||||||
|
|
||||||
--red-to-pink-to-purple-horizontal-gradient: linear-gradient(
|
|
||||||
90deg,
|
|
||||||
var(--orange-red) 0%,
|
|
||||||
var(--vivid-pink) 50%,
|
|
||||||
var(--electric-violet) 100%
|
|
||||||
);
|
|
||||||
|
|
||||||
--pill-accent: var(--bright-blue);
|
|
||||||
|
|
||||||
font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
|
||||||
Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
|
|
||||||
"Segoe UI Symbol";
|
|
||||||
box-sizing: border-box;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 3.125rem;
|
|
||||||
color: var(--gray-900);
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 100%;
|
|
||||||
letter-spacing: -0.125rem;
|
|
||||||
margin: 0;
|
|
||||||
font-family: "Inter Tight", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
|
||||||
Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
|
|
||||||
"Segoe UI Symbol";
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0;
|
|
||||||
color: var(--gray-700);
|
|
||||||
}
|
|
||||||
|
|
||||||
main {
|
|
||||||
width: 100%;
|
|
||||||
min-height: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
padding: 1rem;
|
|
||||||
box-sizing: inherit;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.angular-logo {
|
|
||||||
max-width: 9.2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-around;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 700px;
|
|
||||||
margin-bottom: 3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content h1 {
|
|
||||||
margin-top: 1.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content p {
|
|
||||||
margin-top: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.divider {
|
|
||||||
width: 1px;
|
|
||||||
background: var(--red-to-pink-to-purple-vertical-gradient);
|
|
||||||
margin-inline: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pill-group {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: start;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 1.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pill {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
--pill-accent: var(--bright-blue);
|
|
||||||
background: color-mix(in srgb, var(--pill-accent) 5%, transparent);
|
|
||||||
color: var(--pill-accent);
|
|
||||||
padding-inline: 0.75rem;
|
|
||||||
padding-block: 0.375rem;
|
|
||||||
border-radius: 2.75rem;
|
|
||||||
border: 0;
|
|
||||||
transition: background 0.3s ease;
|
|
||||||
font-family: var(--inter-font);
|
|
||||||
font-size: 0.875rem;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 1.4rem;
|
|
||||||
letter-spacing: -0.00875rem;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pill:hover {
|
|
||||||
background: color-mix(in srgb, var(--pill-accent) 15%, transparent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.pill-group .pill:nth-child(6n + 1) {
|
|
||||||
--pill-accent: var(--bright-blue);
|
|
||||||
}
|
|
||||||
.pill-group .pill:nth-child(6n + 2) {
|
|
||||||
--pill-accent: var(--french-violet);
|
|
||||||
}
|
|
||||||
.pill-group .pill:nth-child(6n + 3),
|
|
||||||
.pill-group .pill:nth-child(6n + 4),
|
|
||||||
.pill-group .pill:nth-child(6n + 5) {
|
|
||||||
--pill-accent: var(--hot-red);
|
|
||||||
}
|
|
||||||
|
|
||||||
.pill-group svg {
|
|
||||||
margin-inline-start: 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.social-links {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.73rem;
|
|
||||||
margin-top: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.social-links path {
|
|
||||||
transition: fill 0.3s ease;
|
|
||||||
fill: var(--gray-400);
|
|
||||||
}
|
|
||||||
|
|
||||||
.social-links a:hover svg path {
|
|
||||||
fill: var(--gray-900);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 650px) {
|
|
||||||
.content {
|
|
||||||
flex-direction: column;
|
|
||||||
width: max-content;
|
|
||||||
}
|
|
||||||
|
|
||||||
.divider {
|
|
||||||
height: 1px;
|
|
||||||
width: 100%;
|
|
||||||
background: var(--red-to-pink-to-purple-horizontal-gradient);
|
|
||||||
margin-block: 1.5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<main class="main">
|
|
||||||
<div class="content">
|
|
||||||
<div class="left-side">
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 982 239"
|
|
||||||
fill="none"
|
|
||||||
class="angular-logo"
|
|
||||||
>
|
|
||||||
<g clip-path="url(#a)">
|
|
||||||
<path
|
|
||||||
fill="url(#b)"
|
|
||||||
d="M388.676 191.625h30.849L363.31 31.828h-35.758l-56.215 159.797h30.848l13.174-39.356h60.061l13.256 39.356Zm-65.461-62.675 21.602-64.311h1.227l21.602 64.311h-44.431Zm126.831-7.527v70.202h-28.23V71.839h27.002v20.374h1.392c2.782-6.71 7.2-12.028 13.255-15.956 6.056-3.927 13.584-5.89 22.503-5.89 8.264 0 15.465 1.8 21.684 5.318 6.137 3.518 10.964 8.673 14.319 15.382 3.437 6.71 5.074 14.81 4.992 24.383v76.175h-28.23v-71.92c0-8.019-2.046-14.237-6.219-18.819-4.173-4.5-9.819-6.791-17.102-6.791-4.91 0-9.328 1.063-13.174 3.272-3.846 2.128-6.792 5.237-9.001 9.328-2.046 4.009-3.191 8.918-3.191 14.728ZM589.233 239c-10.147 0-18.82-1.391-26.103-4.091-7.282-2.7-13.092-6.382-17.511-10.964-4.418-4.582-7.528-9.655-9.164-15.219l25.448-6.136c1.145 2.372 2.782 4.663 4.991 6.954 2.209 2.291 5.155 4.255 8.837 5.81 3.683 1.554 8.428 2.291 14.074 2.291 8.019 0 14.647-1.964 19.884-5.81 5.237-3.845 7.856-10.227 7.856-19.064v-22.665h-1.391c-1.473 2.946-3.601 5.892-6.383 9.001-2.782 3.109-6.464 5.645-10.965 7.691-4.582 2.046-10.228 3.109-17.101 3.109-9.165 0-17.511-2.209-25.039-6.545-7.446-4.337-13.42-10.883-17.757-19.474-4.418-8.673-6.628-19.473-6.628-32.565 0-13.091 2.21-24.301 6.628-33.383 4.419-9.082 10.311-15.955 17.839-20.7 7.528-4.746 15.874-7.037 25.039-7.037 7.037 0 12.846 1.145 17.347 3.518 4.582 2.373 8.182 5.236 10.883 8.51 2.7 3.272 4.746 6.382 6.137 9.327h1.554v-19.8h27.821v121.749c0 10.228-2.454 18.737-7.364 25.447-4.91 6.709-11.538 11.7-20.048 15.055-8.509 3.355-18.165 4.991-28.884 4.991Zm.245-71.266c5.974 0 11.047-1.473 15.302-4.337 4.173-2.945 7.446-7.118 9.573-12.519 2.21-5.482 3.274-12.027 3.274-19.637 0-7.609-1.064-14.155-3.274-19.8-2.127-5.646-5.318-10.064-9.491-13.255-4.174-3.11-9.329-4.746-15.384-4.746s-11.537 1.636-15.792 4.91c-4.173 3.272-7.365 7.772-9.492 13.418-2.128 5.727-3.191 12.191-3.191 19.392 0 7.2 1.063 13.745 3.273 19.228 2.127 5.482 5.318 9.736 9.573 12.764 4.174 3.027 9.41 4.582 15.629 4.582Zm141.56-26.51V71.839h28.23v119.786h-27.412v-21.273h-1.227c-2.7 6.709-7.119 12.191-13.338 16.446-6.137 4.255-13.747 6.382-22.748 6.382-7.855 0-14.81-1.718-20.783-5.237-5.974-3.518-10.72-8.591-14.075-15.382-3.355-6.709-5.073-14.891-5.073-24.464V71.839h28.312v71.921c0 7.609 2.046 13.664 6.219 18.083 4.173 4.5 9.655 6.709 16.365 6.709 4.173 0 8.183-.982 12.111-3.028 3.927-2.045 7.118-5.072 9.655-9.082 2.537-4.091 3.764-9.164 3.764-15.218Zm65.707-109.395v159.796h-28.23V31.828h28.23Zm44.841 162.169c-7.61 0-14.402-1.391-20.457-4.091-6.055-2.7-10.883-6.791-14.32-12.109-3.518-5.319-5.237-11.946-5.237-19.801 0-6.791 1.228-12.355 3.765-16.773 2.536-4.419 5.891-7.937 10.228-10.637 4.337-2.618 9.164-4.664 14.647-6.055 5.4-1.391 11.046-2.373 16.856-3.027 7.037-.737 12.683-1.391 17.102-1.964 4.337-.573 7.528-1.555 9.574-2.782 1.963-1.309 3.027-3.273 3.027-5.973v-.491c0-5.891-1.718-10.391-5.237-13.664-3.518-3.191-8.51-4.828-15.056-4.828-6.955 0-12.356 1.473-16.447 4.5-4.009 3.028-6.71 6.546-8.183 10.719l-26.348-3.764c2.046-7.282 5.483-13.336 10.31-18.328 4.746-4.909 10.638-8.59 17.511-11.045 6.955-2.455 14.565-3.682 22.912-3.682 5.809 0 11.537.654 17.265 2.045s10.965 3.6 15.711 6.71c4.746 3.109 8.51 7.282 11.455 12.6 2.864 5.318 4.337 11.946 4.337 19.883v80.184h-27.166v-16.446h-.9c-1.719 3.355-4.092 6.464-7.201 9.328-3.109 2.864-6.955 5.237-11.619 6.955-4.828 1.718-10.229 2.536-16.529 2.536Zm7.364-20.701c5.646 0 10.556-1.145 14.729-3.354 4.173-2.291 7.364-5.237 9.655-9.001 2.292-3.763 3.355-7.854 3.355-12.273v-14.155c-.9.737-2.373 1.391-4.5 2.046-2.128.654-4.419 1.145-7.037 1.636-2.619.491-5.155.9-7.692 1.227-2.537.328-4.746.655-6.628.901-4.173.572-8.019 1.472-11.292 2.781-3.355 1.31-5.973 3.11-7.855 5.401-1.964 2.291-2.864 5.318-2.864 8.918 0 5.237 1.882 9.164 5.728 11.782 3.682 2.782 8.51 4.091 14.401 4.091Zm64.643 18.328V71.839h27.412v19.965h1.227c2.21-6.955 5.974-12.274 11.292-16.038 5.319-3.763 11.456-5.645 18.329-5.645 1.555 0 3.355.082 5.237.163 1.964.164 3.601.328 4.91.573v25.938c-1.227-.41-3.109-.819-5.646-1.146a58.814 58.814 0 0 0-7.446-.49c-5.155 0-9.738 1.145-13.829 3.354-4.091 2.209-7.282 5.236-9.655 9.164-2.373 3.927-3.519 8.427-3.519 13.5v70.448h-28.312ZM222.077 39.192l-8.019 125.923L137.387 0l84.69 39.192Zm-53.105 162.825-57.933 33.056-57.934-33.056 11.783-28.556h92.301l11.783 28.556ZM111.039 62.675l30.357 73.803H80.681l30.358-73.803ZM7.937 165.115 0 39.192 84.69 0 7.937 165.115Z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
fill="url(#c)"
|
|
||||||
d="M388.676 191.625h30.849L363.31 31.828h-35.758l-56.215 159.797h30.848l13.174-39.356h60.061l13.256 39.356Zm-65.461-62.675 21.602-64.311h1.227l21.602 64.311h-44.431Zm126.831-7.527v70.202h-28.23V71.839h27.002v20.374h1.392c2.782-6.71 7.2-12.028 13.255-15.956 6.056-3.927 13.584-5.89 22.503-5.89 8.264 0 15.465 1.8 21.684 5.318 6.137 3.518 10.964 8.673 14.319 15.382 3.437 6.71 5.074 14.81 4.992 24.383v76.175h-28.23v-71.92c0-8.019-2.046-14.237-6.219-18.819-4.173-4.5-9.819-6.791-17.102-6.791-4.91 0-9.328 1.063-13.174 3.272-3.846 2.128-6.792 5.237-9.001 9.328-2.046 4.009-3.191 8.918-3.191 14.728ZM589.233 239c-10.147 0-18.82-1.391-26.103-4.091-7.282-2.7-13.092-6.382-17.511-10.964-4.418-4.582-7.528-9.655-9.164-15.219l25.448-6.136c1.145 2.372 2.782 4.663 4.991 6.954 2.209 2.291 5.155 4.255 8.837 5.81 3.683 1.554 8.428 2.291 14.074 2.291 8.019 0 14.647-1.964 19.884-5.81 5.237-3.845 7.856-10.227 7.856-19.064v-22.665h-1.391c-1.473 2.946-3.601 5.892-6.383 9.001-2.782 3.109-6.464 5.645-10.965 7.691-4.582 2.046-10.228 3.109-17.101 3.109-9.165 0-17.511-2.209-25.039-6.545-7.446-4.337-13.42-10.883-17.757-19.474-4.418-8.673-6.628-19.473-6.628-32.565 0-13.091 2.21-24.301 6.628-33.383 4.419-9.082 10.311-15.955 17.839-20.7 7.528-4.746 15.874-7.037 25.039-7.037 7.037 0 12.846 1.145 17.347 3.518 4.582 2.373 8.182 5.236 10.883 8.51 2.7 3.272 4.746 6.382 6.137 9.327h1.554v-19.8h27.821v121.749c0 10.228-2.454 18.737-7.364 25.447-4.91 6.709-11.538 11.7-20.048 15.055-8.509 3.355-18.165 4.991-28.884 4.991Zm.245-71.266c5.974 0 11.047-1.473 15.302-4.337 4.173-2.945 7.446-7.118 9.573-12.519 2.21-5.482 3.274-12.027 3.274-19.637 0-7.609-1.064-14.155-3.274-19.8-2.127-5.646-5.318-10.064-9.491-13.255-4.174-3.11-9.329-4.746-15.384-4.746s-11.537 1.636-15.792 4.91c-4.173 3.272-7.365 7.772-9.492 13.418-2.128 5.727-3.191 12.191-3.191 19.392 0 7.2 1.063 13.745 3.273 19.228 2.127 5.482 5.318 9.736 9.573 12.764 4.174 3.027 9.41 4.582 15.629 4.582Zm141.56-26.51V71.839h28.23v119.786h-27.412v-21.273h-1.227c-2.7 6.709-7.119 12.191-13.338 16.446-6.137 4.255-13.747 6.382-22.748 6.382-7.855 0-14.81-1.718-20.783-5.237-5.974-3.518-10.72-8.591-14.075-15.382-3.355-6.709-5.073-14.891-5.073-24.464V71.839h28.312v71.921c0 7.609 2.046 13.664 6.219 18.083 4.173 4.5 9.655 6.709 16.365 6.709 4.173 0 8.183-.982 12.111-3.028 3.927-2.045 7.118-5.072 9.655-9.082 2.537-4.091 3.764-9.164 3.764-15.218Zm65.707-109.395v159.796h-28.23V31.828h28.23Zm44.841 162.169c-7.61 0-14.402-1.391-20.457-4.091-6.055-2.7-10.883-6.791-14.32-12.109-3.518-5.319-5.237-11.946-5.237-19.801 0-6.791 1.228-12.355 3.765-16.773 2.536-4.419 5.891-7.937 10.228-10.637 4.337-2.618 9.164-4.664 14.647-6.055 5.4-1.391 11.046-2.373 16.856-3.027 7.037-.737 12.683-1.391 17.102-1.964 4.337-.573 7.528-1.555 9.574-2.782 1.963-1.309 3.027-3.273 3.027-5.973v-.491c0-5.891-1.718-10.391-5.237-13.664-3.518-3.191-8.51-4.828-15.056-4.828-6.955 0-12.356 1.473-16.447 4.5-4.009 3.028-6.71 6.546-8.183 10.719l-26.348-3.764c2.046-7.282 5.483-13.336 10.31-18.328 4.746-4.909 10.638-8.59 17.511-11.045 6.955-2.455 14.565-3.682 22.912-3.682 5.809 0 11.537.654 17.265 2.045s10.965 3.6 15.711 6.71c4.746 3.109 8.51 7.282 11.455 12.6 2.864 5.318 4.337 11.946 4.337 19.883v80.184h-27.166v-16.446h-.9c-1.719 3.355-4.092 6.464-7.201 9.328-3.109 2.864-6.955 5.237-11.619 6.955-4.828 1.718-10.229 2.536-16.529 2.536Zm7.364-20.701c5.646 0 10.556-1.145 14.729-3.354 4.173-2.291 7.364-5.237 9.655-9.001 2.292-3.763 3.355-7.854 3.355-12.273v-14.155c-.9.737-2.373 1.391-4.5 2.046-2.128.654-4.419 1.145-7.037 1.636-2.619.491-5.155.9-7.692 1.227-2.537.328-4.746.655-6.628.901-4.173.572-8.019 1.472-11.292 2.781-3.355 1.31-5.973 3.11-7.855 5.401-1.964 2.291-2.864 5.318-2.864 8.918 0 5.237 1.882 9.164 5.728 11.782 3.682 2.782 8.51 4.091 14.401 4.091Zm64.643 18.328V71.839h27.412v19.965h1.227c2.21-6.955 5.974-12.274 11.292-16.038 5.319-3.763 11.456-5.645 18.329-5.645 1.555 0 3.355.082 5.237.163 1.964.164 3.601.328 4.91.573v25.938c-1.227-.41-3.109-.819-5.646-1.146a58.814 58.814 0 0 0-7.446-.49c-5.155 0-9.738 1.145-13.829 3.354-4.091 2.209-7.282 5.236-9.655 9.164-2.373 3.927-3.519 8.427-3.519 13.5v70.448h-28.312ZM222.077 39.192l-8.019 125.923L137.387 0l84.69 39.192Zm-53.105 162.825-57.933 33.056-57.934-33.056 11.783-28.556h92.301l11.783 28.556ZM111.039 62.675l30.357 73.803H80.681l30.358-73.803ZM7.937 165.115 0 39.192 84.69 0 7.937 165.115Z"
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
<defs>
|
|
||||||
<radialGradient
|
|
||||||
id="c"
|
|
||||||
cx="0"
|
|
||||||
cy="0"
|
|
||||||
r="1"
|
|
||||||
gradientTransform="rotate(118.122 171.182 60.81) scale(205.794)"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
>
|
|
||||||
<stop stop-color="#FF41F8" />
|
|
||||||
<stop offset=".707" stop-color="#FF41F8" stop-opacity=".5" />
|
|
||||||
<stop offset="1" stop-color="#FF41F8" stop-opacity="0" />
|
|
||||||
</radialGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="b"
|
|
||||||
x1="0"
|
|
||||||
x2="982"
|
|
||||||
y1="192"
|
|
||||||
y2="192"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
>
|
|
||||||
<stop stop-color="#F0060B" />
|
|
||||||
<stop offset="0" stop-color="#F0070C" />
|
|
||||||
<stop offset=".526" stop-color="#CC26D5" />
|
|
||||||
<stop offset="1" stop-color="#7702FF" />
|
|
||||||
</linearGradient>
|
|
||||||
<clipPath id="a"><path fill="#fff" d="M0 0h982v239H0z" /></clipPath>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
||||||
<h1>Hello, {{ title }}</h1>
|
|
||||||
<p>Congratulations! Your app is running. 🎉</p>
|
|
||||||
</div>
|
|
||||||
<div class="divider" role="separator" aria-label="Divider"></div>
|
|
||||||
<div class="right-side">
|
|
||||||
<div class="pill-group">
|
|
||||||
@for (item of [
|
|
||||||
{ title: 'Explore the Docs', link: 'https://angular.dev' },
|
|
||||||
{ title: 'Learn with Tutorials', link: 'https://angular.dev/tutorials' },
|
|
||||||
{ title: 'CLI Docs', link: 'https://angular.dev/tools/cli' },
|
|
||||||
{ title: 'Angular Language Service', link: 'https://angular.dev/tools/language-service' },
|
|
||||||
{ title: 'Angular DevTools', link: 'https://angular.dev/tools/devtools' },
|
|
||||||
]; track item.title) {
|
|
||||||
<a
|
|
||||||
class="pill"
|
|
||||||
[href]="item.link"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener"
|
|
||||||
>
|
|
||||||
<span>{{ item.title }}</span>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
height="14"
|
|
||||||
viewBox="0 -960 960 960"
|
|
||||||
width="14"
|
|
||||||
fill="currentColor"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h280v80H200v560h560v-280h80v280q0 33-23.5 56.5T760-120H200Zm188-212-56-56 372-372H560v-80h280v280h-80v-144L388-332Z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
<div class="social-links">
|
|
||||||
<a
|
|
||||||
href="https://github.com/angular/angular"
|
|
||||||
aria-label="Github"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
width="25"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 25 24"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
alt="Github"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M12.3047 0C5.50634 0 0 5.50942 0 12.3047C0 17.7423 3.52529 22.3535 8.41332 23.9787C9.02856 24.0946 9.25414 23.7142 9.25414 23.3871C9.25414 23.0949 9.24389 22.3207 9.23876 21.2953C5.81601 22.0377 5.09414 19.6444 5.09414 19.6444C4.53427 18.2243 3.72524 17.8449 3.72524 17.8449C2.61064 17.082 3.81137 17.0973 3.81137 17.0973C5.04697 17.1835 5.69604 18.3647 5.69604 18.3647C6.79321 20.2463 8.57636 19.7029 9.27978 19.3881C9.39052 18.5924 9.70736 18.0499 10.0591 17.7423C7.32641 17.4347 4.45429 16.3765 4.45429 11.6618C4.45429 10.3185 4.9311 9.22133 5.72065 8.36C5.58222 8.04931 5.16694 6.79833 5.82831 5.10337C5.82831 5.10337 6.85883 4.77319 9.2121 6.36459C10.1965 6.09082 11.2424 5.95546 12.2883 5.94931C13.3342 5.95546 14.3801 6.09082 15.3644 6.36459C17.7023 4.77319 18.7328 5.10337 18.7328 5.10337C19.3942 6.79833 18.9789 8.04931 18.8559 8.36C19.6403 9.22133 20.1171 10.3185 20.1171 11.6618C20.1171 16.3888 17.2409 17.4296 14.5031 17.7321C14.9338 18.1012 15.3337 18.8559 15.3337 20.0084C15.3337 21.6552 15.3183 22.978 15.3183 23.3779C15.3183 23.7009 15.5336 24.0854 16.1642 23.9623C21.0871 22.3484 24.6094 17.7341 24.6094 12.3047C24.6094 5.50942 19.0999 0 12.3047 0Z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
href="https://twitter.com/angular"
|
|
||||||
aria-label="Twitter"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
alt="Twitter"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
href="https://www.youtube.com/channel/UCbn1OgGei-DV7aSRo_HaAiw"
|
|
||||||
aria-label="Youtube"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
width="29"
|
|
||||||
height="20"
|
|
||||||
viewBox="0 0 29 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
alt="Youtube"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill-rule="evenodd"
|
|
||||||
clip-rule="evenodd"
|
|
||||||
d="M27.4896 1.52422C27.9301 1.96749 28.2463 2.51866 28.4068 3.12258C29.0004 5.35161 29.0004 10 29.0004 10C29.0004 10 29.0004 14.6484 28.4068 16.8774C28.2463 17.4813 27.9301 18.0325 27.4896 18.4758C27.0492 18.9191 26.5 19.2389 25.8972 19.4032C23.6778 20 14.8068 20 14.8068 20C14.8068 20 5.93586 20 3.71651 19.4032C3.11363 19.2389 2.56449 18.9191 2.12405 18.4758C1.68361 18.0325 1.36732 17.4813 1.20683 16.8774C0.613281 14.6484 0.613281 10 0.613281 10C0.613281 10 0.613281 5.35161 1.20683 3.12258C1.36732 2.51866 1.68361 1.96749 2.12405 1.52422C2.56449 1.08095 3.11363 0.76113 3.71651 0.596774C5.93586 0 14.8068 0 14.8068 0C14.8068 0 23.6778 0 25.8972 0.596774C26.5 0.76113 27.0492 1.08095 27.4896 1.52422ZM19.3229 10L11.9036 5.77905V14.221L19.3229 10Z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
|
<div class="navbar-right">
|
||||||
<!-- * * * * * * * * * * * The content above * * * * * * * * * * * * -->
|
<button mat-icon-button [matMenuTriggerFor]="notificationMenu" class="notification-btn">
|
||||||
<!-- * * * * * * * * * * is only a placeholder * * * * * * * * * * * -->
|
<i class="pi pi-bell" style="color: white;"></i>
|
||||||
<!-- * * * * * * * * * * and can be replaced. * * * * * * * * * * * -->
|
<span class="notification-badge">3</span>
|
||||||
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
|
</button>
|
||||||
<!-- * * * * * * * * * * End of Placeholder * * * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
|
|
||||||
|
|
||||||
|
<button mat-icon-button [matMenuTriggerFor]="userMenu" class="user-menu-btn">
|
||||||
|
<i class="pi pi-user" style="color: white;"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
<router-outlet />
|
<mat-menu #userMenu="matMenu" class="custom-menu">
|
||||||
|
<button mat-menu-item class="menu-item-custom">
|
||||||
|
<i class="pi pi-user menu-icon"></i>
|
||||||
|
<span>Profile</span>
|
||||||
|
</button>
|
||||||
|
<button mat-menu-item class="menu-item-custom">
|
||||||
|
<i class="pi pi-cog menu-icon"></i>
|
||||||
|
<span>Settings</span>
|
||||||
|
</button>
|
||||||
|
<mat-divider class="menu-divider"></mat-divider>
|
||||||
|
<button mat-menu-item class="menu-item-custom">
|
||||||
|
<i class="pi pi-sign-out menu-icon"></i>
|
||||||
|
<span>Logout</span>
|
||||||
|
</button>
|
||||||
|
</mat-menu>
|
||||||
|
|
||||||
|
<mat-menu #notificationMenu="matMenu" class="custom-menu">
|
||||||
|
<button mat-menu-item class="menu-item-custom">
|
||||||
|
<i class="pi pi-check-circle menu-icon notification-success"></i>
|
||||||
|
<span>New asset registered</span>
|
||||||
|
</button>
|
||||||
|
<button mat-menu-item class="menu-item-custom">
|
||||||
|
<i class="pi pi-exclamation-triangle menu-icon notification-warning"></i>
|
||||||
|
<span>Maintenance due</span>
|
||||||
|
</button>
|
||||||
|
<button mat-menu-item class="menu-item-custom">
|
||||||
|
<i class="pi pi-info-circle menu-icon notification-info"></i>
|
||||||
|
<span>System update</span>
|
||||||
|
</button>
|
||||||
|
</mat-menu>
|
||||||
|
</div>
|
||||||
|
</mat-toolbar>
|
||||||
|
|
||||||
|
<!-- Main Layout with Sidebar -->
|
||||||
|
<mat-sidenav-container class="sidenav-container">
|
||||||
|
<!-- Left Sidebar -->
|
||||||
|
<mat-sidenav #sidenav
|
||||||
|
[mode]="isMobile ? 'over' : 'side'"
|
||||||
|
[opened]="!isMobile || isSidebarOpen"
|
||||||
|
class="sidebar"
|
||||||
|
(openedChange)="onSidebarToggle($event)">
|
||||||
|
|
||||||
|
<!-- Mobile Close Button -->
|
||||||
|
<div class="mobile-close-btn" *ngIf="isMobile">
|
||||||
|
<button mat-icon-button (click)="closeSidebar()">
|
||||||
|
<i class="pi pi-times"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-nav-list class="sidebar-menu">
|
||||||
|
<div class="menu-section">
|
||||||
|
<span class="section-title">
|
||||||
|
<i class="pi pi-bars menu-icon"></i>
|
||||||
|
<span>MAIN MENU</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a mat-list-item
|
||||||
|
*ngFor="let item of filteredMenuItems"
|
||||||
|
[routerLink]="item.route"
|
||||||
|
routerLinkActive="active-link"
|
||||||
|
class="menu-item"
|
||||||
|
(click)="onMenuItemClick()">
|
||||||
|
<i class="pi {{item.icon}} menu-icon"></i>
|
||||||
|
<span matListItemTitle>{{item.label}}</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<mat-divider class="menu-divider"></mat-divider>
|
||||||
|
|
||||||
|
<div class="menu-section">
|
||||||
|
<span class="section-title">
|
||||||
|
<i class="pi pi-cog menu-icon"></i>
|
||||||
|
<span>ADMIN</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a mat-list-item routerLink="/admin" class="menu-item" (click)="onMenuItemClick()">
|
||||||
|
<i class="pi pi-shield menu-icon"></i>
|
||||||
|
<span matListItemTitle>Admin Setting</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a mat-list-item routerLink="/logout" class="menu-item" (click)="onMenuItemClick()">
|
||||||
|
<i class="pi pi-sign-out menu-icon"></i>
|
||||||
|
<span matListItemTitle>Logout</span>
|
||||||
|
</a>
|
||||||
|
</mat-nav-list>
|
||||||
|
</mat-sidenav>
|
||||||
|
|
||||||
|
<!-- Main Content Area -->
|
||||||
|
<mat-sidenav-content class="main-content">
|
||||||
|
<!-- Mobile Overlay -->
|
||||||
|
<div class="mobile-overlay"
|
||||||
|
*ngIf="isMobile && isSidebarOpen"
|
||||||
|
(click)="closeSidebar()">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Content Header -->
|
||||||
|
<!-- <div class="content-header">
|
||||||
|
<h1>Index</h1>
|
||||||
|
</div> -->
|
||||||
|
|
||||||
|
<div class="content-wrapper">
|
||||||
|
<router-outlet></router-outlet>
|
||||||
|
</div>
|
||||||
|
</mat-sidenav-content>
|
||||||
|
</mat-sidenav-container>
|
||||||
|
</div>
|
||||||
|
|||||||
@ -1,12 +1,111 @@
|
|||||||
import { Component } from '@angular/core';
|
import { ChangeDetectionStrategy,Component, HostListener, Inject, PLATFORM_ID } from '@angular/core';
|
||||||
import { RouterOutlet } from '@angular/router';
|
import { CommonModule, isPlatformBrowser } from '@angular/common';
|
||||||
|
import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router';
|
||||||
|
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||||
|
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||||
|
import { MatListModule } from '@angular/material/list';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatMenuModule } from '@angular/material/menu';
|
||||||
|
import { MatBadgeModule } from '@angular/material/badge';
|
||||||
|
import { MatDividerModule } from '@angular/material/divider';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
imports: [RouterOutlet],
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
RouterLink,
|
||||||
|
RouterLinkActive,
|
||||||
|
RouterOutlet,
|
||||||
|
MatToolbarModule,
|
||||||
|
MatSidenavModule,
|
||||||
|
MatListModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatMenuModule,
|
||||||
|
MatBadgeModule,
|
||||||
|
MatDividerModule
|
||||||
|
],
|
||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
styleUrl: './app.component.css'
|
styleUrl: './app.component.css'
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
title = 'pine-asset-webapp';
|
title = 'PINEPIM Asset Management';
|
||||||
|
|
||||||
|
// Authentication flags (for demo purposes)
|
||||||
|
isAuthenticated = true;
|
||||||
|
isAdmin = true;
|
||||||
|
|
||||||
|
// Responsive design properties
|
||||||
|
isMobile = false;
|
||||||
|
isSidebarOpen = false;
|
||||||
|
|
||||||
|
// Menu items with role-based visibility - Updated to use PrimeIcons
|
||||||
|
menuItems = [
|
||||||
|
{ label: 'Dashboard', icon: 'pi-home', route: '/dashboard', show: true },
|
||||||
|
{ label: 'Purchasing', icon: 'pi-shopping-cart', route: '/purchase-orders', show: true },
|
||||||
|
{ label: 'Asset', icon: 'pi-question-circle', route: '/asset/list', show: true },
|
||||||
|
{ label: 'Tools', icon: 'pi-wrench', route: '/asset/registration', show: true },
|
||||||
|
{ label: 'Spare', icon: 'pi-cog', route: '/asset/transfer', show: true }
|
||||||
|
];
|
||||||
|
|
||||||
|
constructor(@Inject(PLATFORM_ID) private platformId: Object) {
|
||||||
|
this.checkScreenSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get filtered menu items based on user role
|
||||||
|
get filteredMenuItems() {
|
||||||
|
return this.menuItems.filter(item => item.show);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check screen size for responsive design
|
||||||
|
@HostListener('window:resize', ['$event'])
|
||||||
|
onResize() {
|
||||||
|
this.checkScreenSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
private checkScreenSize() {
|
||||||
|
// Only check window size in browser environment
|
||||||
|
if (isPlatformBrowser(this.platformId)) {
|
||||||
|
// Adjusted breakpoint for more compact sidebar
|
||||||
|
this.isMobile = window.innerWidth < 640; // Reduced from 768px
|
||||||
|
if (!this.isMobile) {
|
||||||
|
this.isSidebarOpen = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Default to desktop for SSR
|
||||||
|
this.isMobile = false;
|
||||||
|
this.isSidebarOpen = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle sidebar for mobile
|
||||||
|
toggleSidebar() {
|
||||||
|
// console.log("toggleSidebar");
|
||||||
|
if (this.isMobile) {
|
||||||
|
this.isSidebarOpen = !this.isSidebarOpen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close sidebar
|
||||||
|
closeSidebar() {
|
||||||
|
if (this.isMobile) {
|
||||||
|
this.isSidebarOpen = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle sidebar toggle events
|
||||||
|
onSidebarToggle(opened: boolean) {
|
||||||
|
if (this.isMobile) {
|
||||||
|
this.isSidebarOpen = opened;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle menu item clicks (close sidebar on mobile)
|
||||||
|
onMenuItemClick() {
|
||||||
|
if (this.isMobile) {
|
||||||
|
this.closeSidebar();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,14 @@
|
|||||||
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
|
import { ApplicationConfig } from '@angular/core';
|
||||||
import { provideRouter } from '@angular/router';
|
import { provideRouter } from '@angular/router';
|
||||||
|
import { provideAnimations } from '@angular/platform-browser/animations';
|
||||||
|
import { provideHttpClient } from '@angular/common/http';
|
||||||
|
|
||||||
import { routes } from './app.routes';
|
import { routes } from './app.routes';
|
||||||
import { provideClientHydration, withEventReplay } from '@angular/platform-browser';
|
|
||||||
|
|
||||||
export const appConfig: ApplicationConfig = {
|
export const appConfig: ApplicationConfig = {
|
||||||
providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes), provideClientHydration(withEventReplay())]
|
providers: [
|
||||||
|
provideRouter(routes),
|
||||||
|
provideAnimations(),
|
||||||
|
provideHttpClient()
|
||||||
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,3 +1,50 @@
|
|||||||
import { Routes } from '@angular/router';
|
import { Routes } from '@angular/router';
|
||||||
|
|
||||||
export const routes: Routes = [];
|
export const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
redirectTo: '/dashboard',
|
||||||
|
pathMatch: 'full'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'login',
|
||||||
|
loadComponent: () => import('./auth/login/login.component').then(m => m.LoginComponent)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'dashboard',
|
||||||
|
loadComponent: () => import('./dashboard/dashboard.component').then(m => m.DashboardComponent)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'purchase-orders',
|
||||||
|
loadComponent: () => import('./purchase-orders/purchase-orders.component').then(m => m.PurchaseOrdersComponent)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'asset',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'registration',
|
||||||
|
loadComponent: () => import('./asset/registration/asset-registration.component').then(m => m.AssetRegistrationComponent)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'list',
|
||||||
|
loadComponent: () => import('./asset/list/asset-list.component').then(m => m.AssetListComponent)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'transfer',
|
||||||
|
loadComponent: () => import('./asset/transfer/asset-transfer.component').then(m => m.AssetTransferComponent)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'retirement',
|
||||||
|
loadComponent: () => import('./asset/retirement/asset-retirement.component').then(m => m.AssetRetirementComponent)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'admin',
|
||||||
|
loadComponent: () => import('./admin/admin.component').then(m => m.AdminComponent)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '**',
|
||||||
|
redirectTo: '/dashboard'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|||||||
257
src/app/asset/list/asset-list.component.css
Normal file
257
src/app/asset/list/asset-list.component.css
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
/* Asset List Container */
|
||||||
|
.asset-list-container {
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Page Header */
|
||||||
|
.page-header {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-content h1 {
|
||||||
|
color: var(--white);
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-content p {
|
||||||
|
color: var(--light-gray);
|
||||||
|
font-size: 16px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Filter Card */
|
||||||
|
.filter-card {
|
||||||
|
background: var(--medium-gray) !important;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 12px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-card ::ng-deep .mat-mdc-card-content {
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-form {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||||
|
gap: 16px;
|
||||||
|
align-items: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-row mat-form-field {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-row ::ng-deep .mat-mdc-form-field {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-row ::ng-deep .mat-mdc-text-field-wrapper {
|
||||||
|
background-color: rgba(255, 255, 255, 0.05);
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-row ::ng-deep .mat-mdc-form-field-outline {
|
||||||
|
color: rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-row ::ng-deep .mat-mdc-form-field-outline-thick {
|
||||||
|
color: var(--primary-red);
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-row ::ng-deep .mat-mdc-form-field-label {
|
||||||
|
color: var(--light-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-row ::ng-deep .mat-mdc-input-element {
|
||||||
|
color: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-row ::ng-deep .mat-mdc-form-field-icon-suffix {
|
||||||
|
color: var(--light-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-row button {
|
||||||
|
height: 56px;
|
||||||
|
border-color: rgba(255, 255, 255, 0.2);
|
||||||
|
color: var(--light-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Table Card */
|
||||||
|
.table-card {
|
||||||
|
background: var(--medium-gray) !important;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-card ::ng-deep .mat-mdc-card-content {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-container {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-table {
|
||||||
|
width: 100%;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-table ::ng-deep .mat-mdc-header-cell {
|
||||||
|
background-color: rgba(255, 255, 255, 0.05);
|
||||||
|
color: var(--white);
|
||||||
|
font-weight: 500;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-table ::ng-deep .mat-mdc-cell {
|
||||||
|
color: var(--light-gray);
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-table ::ng-deep .mat-mdc-row:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-table ::ng-deep .mat-mdc-sort-header {
|
||||||
|
color: var(--white) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-table ::ng-deep .mat-mdc-sort-header-arrow {
|
||||||
|
color: var(--light-gray) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Action Buttons */
|
||||||
|
.action-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons button {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons ::ng-deep .mat-mdc-icon-button {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons ::ng-deep .mat-mdc-icon-button.mat-primary {
|
||||||
|
color: var(--primary-red) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons ::ng-deep .mat-mdc-icon-button.mat-accent {
|
||||||
|
color: var(--yellow-orange) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Paginator */
|
||||||
|
::ng-deep .mat-mdc-paginator {
|
||||||
|
background-color: rgba(255, 255, 255, 0.02);
|
||||||
|
color: var(--light-gray);
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-paginator-page-size-label {
|
||||||
|
color: var(--light-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-paginator-range-label {
|
||||||
|
color: var(--light-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-paginator-navigation-previous,
|
||||||
|
::ng-deep .mat-mdc-paginator-navigation-next,
|
||||||
|
::ng-deep .mat-mdc-paginator-navigation-first,
|
||||||
|
::ng-deep .mat-mdc-paginator-navigation-last {
|
||||||
|
color: var(--light-gray) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Chip Styling */
|
||||||
|
::ng-deep .mat-mdc-chip {
|
||||||
|
background-color: rgba(255, 255, 255, 0.1) !important;
|
||||||
|
color: var(--white) !important;
|
||||||
|
font-size: 12px !important;
|
||||||
|
height: 24px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-chip.accent-green {
|
||||||
|
background-color: rgba(0, 204, 102, 0.2) !important;
|
||||||
|
color: var(--accent-green) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-chip.yellow-orange {
|
||||||
|
background-color: rgba(255, 170, 0, 0.2) !important;
|
||||||
|
color: var(--yellow-orange) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-chip.red {
|
||||||
|
background-color: rgba(255, 68, 68, 0.2) !important;
|
||||||
|
color: var(--red) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Design */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.asset-list-container {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-content h1 {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-row {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-container {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-table {
|
||||||
|
min-width: 900px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Material Design Overrides */
|
||||||
|
::ng-deep .mat-mdc-select-panel {
|
||||||
|
background-color: var(--medium-gray) !important;
|
||||||
|
color: var(--white) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-option {
|
||||||
|
color: var(--white) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-option:hover {
|
||||||
|
background-color: rgba(200, 0, 0, 0.1) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-option.mat-mdc-option-active {
|
||||||
|
background-color: rgba(200, 0, 0, 0.2) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-option.mat-mdc-selected {
|
||||||
|
background-color: rgba(200, 0, 0, 0.3) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-tooltip {
|
||||||
|
background-color: var(--medium-gray) !important;
|
||||||
|
color: var(--white) !important;
|
||||||
|
font-size: 12px !important;
|
||||||
|
}
|
||||||
137
src/app/asset/list/asset-list.component.html
Normal file
137
src/app/asset/list/asset-list.component.html
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
<div class="asset-list-container">
|
||||||
|
<div class="page-header">
|
||||||
|
<div class="header-content">
|
||||||
|
<h1>Asset List</h1>
|
||||||
|
<p>View and manage all registered assets</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Filters -->
|
||||||
|
<mat-card class="filter-card">
|
||||||
|
<mat-card-content>
|
||||||
|
<form [formGroup]="filterForm" class="filter-form">
|
||||||
|
<div class="filter-row">
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Search Assets</mat-label>
|
||||||
|
<input matInput formControlName="search" placeholder="Search by asset ID, name, department, or location">
|
||||||
|
<mat-icon matSuffix>search</mat-icon>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Category</mat-label>
|
||||||
|
<mat-select formControlName="category">
|
||||||
|
<mat-option *ngFor="let category of categories" [value]="category">
|
||||||
|
{{category}}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Department</mat-label>
|
||||||
|
<mat-select formControlName="department">
|
||||||
|
<mat-option *ngFor="let department of departments" [value]="department">
|
||||||
|
{{department}}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Status</mat-label>
|
||||||
|
<mat-select formControlName="status">
|
||||||
|
<mat-option *ngFor="let status of statuses" [value]="status">
|
||||||
|
{{status}}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<button mat-stroked-button type="button" (click)="clearFilters()">
|
||||||
|
<mat-icon>clear</mat-icon>
|
||||||
|
Clear
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
|
||||||
|
<!-- Table -->
|
||||||
|
<mat-card class="table-card">
|
||||||
|
<mat-card-content>
|
||||||
|
<div class="table-container">
|
||||||
|
<table mat-table [dataSource]="dataSource" matSort class="asset-table">
|
||||||
|
|
||||||
|
<!-- Asset ID Column -->
|
||||||
|
<ng-container matColumnDef="assetId">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Asset ID </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.assetId}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Asset Name Column -->
|
||||||
|
<ng-container matColumnDef="assetName">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Asset Name </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.assetName}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Category Column -->
|
||||||
|
<ng-container matColumnDef="category">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Category </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.category}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Department Column -->
|
||||||
|
<ng-container matColumnDef="department">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Department </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.department}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Location Column -->
|
||||||
|
<ng-container matColumnDef="location">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Location </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.location}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Purchase Date Column -->
|
||||||
|
<ng-container matColumnDef="purchaseDate">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Purchase Date </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.purchaseDate | date:'shortDate'}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Value Column -->
|
||||||
|
<ng-container matColumnDef="value">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Value </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{formatCurrency(element.value)}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Status Column -->
|
||||||
|
<ng-container matColumnDef="status">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Status </th>
|
||||||
|
<td mat-cell *matCellDef="let element">
|
||||||
|
<mat-chip [class]="getStatusColor(element.status)">
|
||||||
|
{{element.status}}
|
||||||
|
</mat-chip>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Actions Column -->
|
||||||
|
<ng-container matColumnDef="actions">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Actions </th>
|
||||||
|
<td mat-cell *matCellDef="let element">
|
||||||
|
<div class="action-buttons">
|
||||||
|
<button mat-icon-button color="primary" (click)="viewAsset(element)" matTooltip="View Details">
|
||||||
|
<mat-icon>visibility</mat-icon>
|
||||||
|
</button>
|
||||||
|
<button mat-icon-button color="accent" (click)="editAsset(element)" matTooltip="Edit Asset">
|
||||||
|
<mat-icon>edit</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<mat-paginator [pageSizeOptions]="[10, 25, 50, 100]" showFirstLastButtons></mat-paginator>
|
||||||
|
</div>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
</div>
|
||||||
228
src/app/asset/list/asset-list.component.ts
Normal file
228
src/app/asset/list/asset-list.component.ts
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { MatTableModule, MatTableDataSource } from '@angular/material/table';
|
||||||
|
import { MatPaginatorModule, MatPaginator } from '@angular/material/paginator';
|
||||||
|
import { MatSortModule, MatSort } from '@angular/material/sort';
|
||||||
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatCardModule } from '@angular/material/card';
|
||||||
|
import { MatChipsModule } from '@angular/material/chips';
|
||||||
|
import { ReactiveFormsModule, FormBuilder, FormGroup } from '@angular/forms';
|
||||||
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-asset-list',
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
MatTableModule,
|
||||||
|
MatPaginatorModule,
|
||||||
|
MatSortModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatSelectModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatCardModule,
|
||||||
|
MatChipsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
MatTooltipModule
|
||||||
|
],
|
||||||
|
templateUrl: './asset-list.component.html',
|
||||||
|
styleUrl: './asset-list.component.css'
|
||||||
|
})
|
||||||
|
export class AssetListComponent implements OnInit {
|
||||||
|
|
||||||
|
@ViewChild(MatPaginator) paginator!: MatPaginator;
|
||||||
|
@ViewChild(MatSort) sort!: MatSort;
|
||||||
|
|
||||||
|
displayedColumns: string[] = [
|
||||||
|
'assetId',
|
||||||
|
'assetName',
|
||||||
|
'category',
|
||||||
|
'department',
|
||||||
|
'location',
|
||||||
|
'purchaseDate',
|
||||||
|
'value',
|
||||||
|
'status',
|
||||||
|
'actions'
|
||||||
|
];
|
||||||
|
|
||||||
|
dataSource: MatTableDataSource<any>;
|
||||||
|
filterForm: FormGroup;
|
||||||
|
|
||||||
|
// Sample data
|
||||||
|
assets = [
|
||||||
|
{
|
||||||
|
assetId: 'AST-001',
|
||||||
|
assetName: 'Dell Latitude 5520 Laptop',
|
||||||
|
category: 'Computers & IT',
|
||||||
|
department: 'IT Department',
|
||||||
|
location: 'Office Building A, Floor 2',
|
||||||
|
purchaseDate: '2024-01-15',
|
||||||
|
value: 45000,
|
||||||
|
status: 'Active'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
assetId: 'AST-002',
|
||||||
|
assetName: 'Office Desk Set',
|
||||||
|
category: 'Office Furniture',
|
||||||
|
department: 'HR Department',
|
||||||
|
location: 'Office Building B, Floor 1',
|
||||||
|
purchaseDate: '2024-01-14',
|
||||||
|
value: 15000,
|
||||||
|
status: 'Active'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
assetId: 'AST-003',
|
||||||
|
assetName: 'Industrial Drill Machine',
|
||||||
|
category: 'Machinery',
|
||||||
|
department: 'Production',
|
||||||
|
location: 'Factory Building C',
|
||||||
|
purchaseDate: '2024-01-13',
|
||||||
|
value: 85000,
|
||||||
|
status: 'Maintenance'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
assetId: 'AST-004',
|
||||||
|
assetName: 'HP LaserJet Printer',
|
||||||
|
category: 'Office Equipment',
|
||||||
|
department: 'Administration',
|
||||||
|
location: 'Office Building A, Floor 1',
|
||||||
|
purchaseDate: '2024-01-12',
|
||||||
|
value: 25000,
|
||||||
|
status: 'Active'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
assetId: 'AST-005',
|
||||||
|
assetName: 'Company Vehicle - Toyota Camry',
|
||||||
|
category: 'Vehicles',
|
||||||
|
department: 'Sales Department',
|
||||||
|
location: 'Parking Lot A',
|
||||||
|
purchaseDate: '2024-01-11',
|
||||||
|
value: 850000,
|
||||||
|
status: 'Active'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
assetId: 'AST-006',
|
||||||
|
assetName: 'Air Conditioning Unit',
|
||||||
|
category: 'Building Equipment',
|
||||||
|
department: 'Facilities',
|
||||||
|
location: 'Office Building A, Floor 3',
|
||||||
|
purchaseDate: '2024-01-10',
|
||||||
|
value: 120000,
|
||||||
|
status: 'Retired'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
categories = ['All', 'Computers & IT', 'Office Furniture', 'Machinery', 'Office Equipment', 'Vehicles', 'Building Equipment'];
|
||||||
|
departments = ['All', 'IT Department', 'HR Department', 'Production', 'Administration', 'Sales Department', 'Facilities'];
|
||||||
|
statuses = ['All', 'Active', 'Maintenance', 'Retired'];
|
||||||
|
|
||||||
|
constructor(private fb: FormBuilder) {
|
||||||
|
this.dataSource = new MatTableDataSource(this.assets);
|
||||||
|
this.filterForm = this.fb.group({
|
||||||
|
search: [''],
|
||||||
|
category: ['All'],
|
||||||
|
department: ['All'],
|
||||||
|
status: ['All']
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.setupFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
this.dataSource.paginator = this.paginator;
|
||||||
|
this.dataSource.sort = this.sort;
|
||||||
|
}
|
||||||
|
|
||||||
|
setupFilter() {
|
||||||
|
this.filterForm.valueChanges.subscribe(values => {
|
||||||
|
this.applyFilter();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
applyFilter() {
|
||||||
|
const filterValue = this.filterForm.value;
|
||||||
|
let filteredData = [...this.assets];
|
||||||
|
|
||||||
|
// Filter by search term
|
||||||
|
if (filterValue.search) {
|
||||||
|
const searchTerm = filterValue.search.toLowerCase();
|
||||||
|
filteredData = filteredData.filter(item =>
|
||||||
|
item.assetId.toLowerCase().includes(searchTerm) ||
|
||||||
|
item.assetName.toLowerCase().includes(searchTerm) ||
|
||||||
|
item.department.toLowerCase().includes(searchTerm) ||
|
||||||
|
item.location.toLowerCase().includes(searchTerm)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter by category
|
||||||
|
if (filterValue.category && filterValue.category !== 'All') {
|
||||||
|
filteredData = filteredData.filter(item =>
|
||||||
|
item.category === filterValue.category
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter by department
|
||||||
|
if (filterValue.department && filterValue.department !== 'All') {
|
||||||
|
filteredData = filteredData.filter(item =>
|
||||||
|
item.department === filterValue.department
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter by status
|
||||||
|
if (filterValue.status && filterValue.status !== 'All') {
|
||||||
|
filteredData = filteredData.filter(item =>
|
||||||
|
item.status === filterValue.status
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dataSource.data = filteredData;
|
||||||
|
}
|
||||||
|
|
||||||
|
getStatusColor(status: string): string {
|
||||||
|
switch (status) {
|
||||||
|
case 'Active':
|
||||||
|
return 'accent-green';
|
||||||
|
case 'Maintenance':
|
||||||
|
return 'yellow-orange';
|
||||||
|
case 'Retired':
|
||||||
|
return 'red';
|
||||||
|
default:
|
||||||
|
return 'light-gray';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
formatCurrency(amount: number): string {
|
||||||
|
return new Intl.NumberFormat('en-US', {
|
||||||
|
style: 'currency',
|
||||||
|
currency: 'THB',
|
||||||
|
minimumFractionDigits: 0
|
||||||
|
}).format(amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
viewAsset(asset: any) {
|
||||||
|
// Navigate to asset details
|
||||||
|
console.log('View asset:', asset.assetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
editAsset(asset: any) {
|
||||||
|
// Navigate to edit form
|
||||||
|
console.log('Edit asset:', asset.assetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearFilters() {
|
||||||
|
this.filterForm.patchValue({
|
||||||
|
search: '',
|
||||||
|
category: 'All',
|
||||||
|
department: 'All',
|
||||||
|
status: 'All'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
251
src/app/asset/registration/asset-registration.component.css
Normal file
251
src/app/asset/registration/asset-registration.component.css
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
/* Asset Registration Container */
|
||||||
|
.asset-registration-container {
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Page Header */
|
||||||
|
.page-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-content h1 {
|
||||||
|
color: var(--white);
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-content p {
|
||||||
|
color: var(--light-gray);
|
||||||
|
font-size: 16px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-actions button {
|
||||||
|
background-color: var(--primary-red) !important;
|
||||||
|
color: var(--white) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tabs Card */
|
||||||
|
.tabs-card {
|
||||||
|
background: var(--medium-gray) !important;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 12px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs-card ::ng-deep .mat-mdc-card-content {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-content {
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-header h3 {
|
||||||
|
color: var(--white);
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-header p {
|
||||||
|
color: var(--light-gray);
|
||||||
|
font-size: 14px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tab Styling */
|
||||||
|
::ng-deep .mat-mdc-tab-group {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-tab-header {
|
||||||
|
background-color: rgba(255, 255, 255, 0.02);
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-tab-label {
|
||||||
|
color: var(--light-gray) !important;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-tab-label.mat-mdc-tab-label-active {
|
||||||
|
color: var(--primary-red) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-tab-header-pagination-chevron {
|
||||||
|
border-color: var(--light-gray) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-tab-header-pagination-disabled .mat-mdc-tab-header-pagination-chevron {
|
||||||
|
border-color: rgba(176, 176, 176, 0.3) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-ink-bar {
|
||||||
|
background-color: var(--primary-red) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Table Card */
|
||||||
|
.table-card {
|
||||||
|
background: var(--medium-gray) !important;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-card ::ng-deep .mat-mdc-card-content {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-container {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-table {
|
||||||
|
width: 100%;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-table ::ng-deep .mat-mdc-header-cell {
|
||||||
|
background-color: rgba(255, 255, 255, 0.05);
|
||||||
|
color: var(--white);
|
||||||
|
font-weight: 500;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-table ::ng-deep .mat-mdc-cell {
|
||||||
|
color: var(--light-gray);
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-table ::ng-deep .mat-mdc-row:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-table ::ng-deep .mat-mdc-sort-header {
|
||||||
|
color: var(--white) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-table ::ng-deep .mat-mdc-sort-header-arrow {
|
||||||
|
color: var(--light-gray) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Action Buttons */
|
||||||
|
.action-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons button {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons ::ng-deep .mat-mdc-icon-button {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons ::ng-deep .mat-mdc-icon-button.mat-primary {
|
||||||
|
color: var(--primary-red) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons ::ng-deep .mat-mdc-icon-button.mat-accent {
|
||||||
|
color: var(--yellow-orange) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons ::ng-deep .mat-mdc-icon-button.mat-warn {
|
||||||
|
color: var(--red) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Paginator */
|
||||||
|
::ng-deep .mat-mdc-paginator {
|
||||||
|
background-color: rgba(255, 255, 255, 0.02);
|
||||||
|
color: var(--light-gray);
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-paginator-page-size-label {
|
||||||
|
color: var(--light-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-paginator-range-label {
|
||||||
|
color: var(--light-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-paginator-navigation-previous,
|
||||||
|
::ng-deep .mat-mdc-paginator-navigation-next,
|
||||||
|
::ng-deep .mat-mdc-paginator-navigation-first,
|
||||||
|
::ng-deep .mat-mdc-paginator-navigation-last {
|
||||||
|
color: var(--light-gray) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Chip Styling */
|
||||||
|
::ng-deep .mat-mdc-chip {
|
||||||
|
background-color: rgba(255, 255, 255, 0.1) !important;
|
||||||
|
color: var(--white) !important;
|
||||||
|
font-size: 12px !important;
|
||||||
|
height: 24px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-chip.accent-green {
|
||||||
|
background-color: rgba(0, 204, 102, 0.2) !important;
|
||||||
|
color: var(--accent-green) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-chip.yellow-orange {
|
||||||
|
background-color: rgba(255, 170, 0, 0.2) !important;
|
||||||
|
color: var(--yellow-orange) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-chip.red {
|
||||||
|
background-color: rgba(255, 68, 68, 0.2) !important;
|
||||||
|
color: var(--red) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Design */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.asset-registration-container {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-content h1 {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-content {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-container {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-table {
|
||||||
|
min-width: 900px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Material Design Overrides */
|
||||||
|
::ng-deep .mat-mdc-tooltip {
|
||||||
|
background-color: var(--medium-gray) !important;
|
||||||
|
color: var(--white) !important;
|
||||||
|
font-size: 12px !important;
|
||||||
|
}
|
||||||
139
src/app/asset/registration/asset-registration.component.html
Normal file
139
src/app/asset/registration/asset-registration.component.html
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
<div class="asset-registration-container">
|
||||||
|
<div class="page-header">
|
||||||
|
<div class="header-content">
|
||||||
|
<h1>Asset Registration</h1>
|
||||||
|
<p>Manage asset registration and track registration status</p>
|
||||||
|
</div>
|
||||||
|
<div class="header-actions">
|
||||||
|
<button mat-raised-button color="primary" (click)="registerAsset()">
|
||||||
|
<mat-icon>add</mat-icon>
|
||||||
|
Register Asset
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tabs -->
|
||||||
|
<mat-card class="tabs-card">
|
||||||
|
<mat-card-content>
|
||||||
|
<mat-tab-group (selectedTabChange)="onTabChange($event)" [selectedIndex]="selectedTabIndex">
|
||||||
|
<mat-tab label="All">
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="tab-header">
|
||||||
|
<h3>All Asset Registrations</h3>
|
||||||
|
<p>Showing all asset registration items</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-tab>
|
||||||
|
<mat-tab label="Pending">
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="tab-header">
|
||||||
|
<h3>Pending Registrations</h3>
|
||||||
|
<p>Assets awaiting registration completion</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-tab>
|
||||||
|
<mat-tab label="Complete">
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="tab-header">
|
||||||
|
<h3>Completed Registrations</h3>
|
||||||
|
<p>Successfully registered assets</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-tab>
|
||||||
|
<mat-tab label="In Progress">
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="tab-header">
|
||||||
|
<h3>In Progress Registrations</h3>
|
||||||
|
<p>Assets currently being processed</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-tab>
|
||||||
|
</mat-tab-group>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
|
||||||
|
<!-- Table -->
|
||||||
|
<mat-card class="table-card">
|
||||||
|
<mat-card-content>
|
||||||
|
<div class="table-container">
|
||||||
|
<table mat-table [dataSource]="dataSource" matSort class="asset-table">
|
||||||
|
|
||||||
|
<!-- Asset ID Column -->
|
||||||
|
<ng-container matColumnDef="assetId">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Asset ID </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.assetId}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Asset Name Column -->
|
||||||
|
<ng-container matColumnDef="assetName">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Asset Name </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.assetName}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Category Column -->
|
||||||
|
<ng-container matColumnDef="category">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Category </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.category}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Department Column -->
|
||||||
|
<ng-container matColumnDef="department">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Department </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.department}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Location Column -->
|
||||||
|
<ng-container matColumnDef="location">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Location </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.location}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Purchase Date Column -->
|
||||||
|
<ng-container matColumnDef="purchaseDate">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Purchase Date </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.purchaseDate | date:'shortDate'}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Value Column -->
|
||||||
|
<ng-container matColumnDef="value">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Value </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{formatCurrency(element.value)}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Status Column -->
|
||||||
|
<ng-container matColumnDef="status">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Status </th>
|
||||||
|
<td mat-cell *matCellDef="let element">
|
||||||
|
<mat-chip [class]="getStatusColor(element.status)">
|
||||||
|
{{element.status}}
|
||||||
|
</mat-chip>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Actions Column -->
|
||||||
|
<ng-container matColumnDef="actions">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Actions </th>
|
||||||
|
<td mat-cell *matCellDef="let element">
|
||||||
|
<div class="action-buttons">
|
||||||
|
<button mat-icon-button color="primary" (click)="viewAsset(element)" matTooltip="View Details">
|
||||||
|
<mat-icon>visibility</mat-icon>
|
||||||
|
</button>
|
||||||
|
<button mat-icon-button color="accent" (click)="editAsset(element)" matTooltip="Edit Asset">
|
||||||
|
<mat-icon>edit</mat-icon>
|
||||||
|
</button>
|
||||||
|
<button mat-icon-button color="warn" (click)="deleteAsset(element)" matTooltip="Delete Asset">
|
||||||
|
<mat-icon>delete</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<mat-paginator [pageSizeOptions]="[10, 25, 50, 100]" showFirstLastButtons></mat-paginator>
|
||||||
|
</div>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
</div>
|
||||||
217
src/app/asset/registration/asset-registration.component.ts
Normal file
217
src/app/asset/registration/asset-registration.component.ts
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { MatTableModule, MatTableDataSource } from '@angular/material/table';
|
||||||
|
import { MatPaginatorModule, MatPaginator } from '@angular/material/paginator';
|
||||||
|
import { MatSortModule, MatSort } from '@angular/material/sort';
|
||||||
|
import { MatTabsModule } from '@angular/material/tabs';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatCardModule } from '@angular/material/card';
|
||||||
|
import { MatChipsModule } from '@angular/material/chips';
|
||||||
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
|
import { MatDialogModule, MatDialog } from '@angular/material/dialog';
|
||||||
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-asset-registration',
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
MatTableModule,
|
||||||
|
MatPaginatorModule,
|
||||||
|
MatSortModule,
|
||||||
|
MatTabsModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatCardModule,
|
||||||
|
MatChipsModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatSelectModule,
|
||||||
|
MatDialogModule,
|
||||||
|
MatTooltipModule
|
||||||
|
],
|
||||||
|
templateUrl: './asset-registration.component.html',
|
||||||
|
styleUrl: './asset-registration.component.css'
|
||||||
|
})
|
||||||
|
export class AssetRegistrationComponent implements OnInit {
|
||||||
|
|
||||||
|
@ViewChild(MatPaginator) paginator!: MatPaginator;
|
||||||
|
@ViewChild(MatSort) sort!: MatSort;
|
||||||
|
|
||||||
|
displayedColumns: string[] = [
|
||||||
|
'assetId',
|
||||||
|
'assetName',
|
||||||
|
'category',
|
||||||
|
'department',
|
||||||
|
'location',
|
||||||
|
'purchaseDate',
|
||||||
|
'value',
|
||||||
|
'status',
|
||||||
|
'actions'
|
||||||
|
];
|
||||||
|
|
||||||
|
dataSource: MatTableDataSource<any>;
|
||||||
|
selectedTabIndex = 0;
|
||||||
|
|
||||||
|
// Sample data
|
||||||
|
assetRegistrations = [
|
||||||
|
{
|
||||||
|
assetId: 'AST-001',
|
||||||
|
assetName: 'Dell Latitude 5520 Laptop',
|
||||||
|
category: 'Computers & IT',
|
||||||
|
department: 'IT Department',
|
||||||
|
location: 'Office Building A, Floor 2',
|
||||||
|
purchaseDate: '2024-01-15',
|
||||||
|
value: 45000,
|
||||||
|
status: 'Complete'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
assetId: 'AST-002',
|
||||||
|
assetName: 'Office Desk Set',
|
||||||
|
category: 'Office Furniture',
|
||||||
|
department: 'HR Department',
|
||||||
|
location: 'Office Building B, Floor 1',
|
||||||
|
purchaseDate: '2024-01-14',
|
||||||
|
value: 15000,
|
||||||
|
status: 'In Progress'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
assetId: 'AST-003',
|
||||||
|
assetName: 'Industrial Drill Machine',
|
||||||
|
category: 'Machinery',
|
||||||
|
department: 'Production',
|
||||||
|
location: 'Factory Building C',
|
||||||
|
purchaseDate: '2024-01-13',
|
||||||
|
value: 85000,
|
||||||
|
status: 'Pending'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
assetId: 'AST-004',
|
||||||
|
assetName: 'HP LaserJet Printer',
|
||||||
|
category: 'Office Equipment',
|
||||||
|
department: 'Administration',
|
||||||
|
location: 'Office Building A, Floor 1',
|
||||||
|
purchaseDate: '2024-01-12',
|
||||||
|
value: 25000,
|
||||||
|
status: 'Complete'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
assetId: 'AST-005',
|
||||||
|
assetName: 'Company Vehicle - Toyota Camry',
|
||||||
|
category: 'Vehicles',
|
||||||
|
department: 'Sales Department',
|
||||||
|
location: 'Parking Lot A',
|
||||||
|
purchaseDate: '2024-01-11',
|
||||||
|
value: 850000,
|
||||||
|
status: 'Critical'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
assetId: 'AST-006',
|
||||||
|
assetName: 'Air Conditioning Unit',
|
||||||
|
category: 'Building Equipment',
|
||||||
|
department: 'Facilities',
|
||||||
|
location: 'Office Building A, Floor 3',
|
||||||
|
purchaseDate: '2024-01-10',
|
||||||
|
value: 120000,
|
||||||
|
status: 'Over Due'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private router: Router,
|
||||||
|
private dialog: MatDialog
|
||||||
|
) {
|
||||||
|
this.dataSource = new MatTableDataSource(this.assetRegistrations);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.applyTabFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
this.dataSource.paginator = this.paginator;
|
||||||
|
this.dataSource.sort = this.sort;
|
||||||
|
}
|
||||||
|
|
||||||
|
onTabChange(event: any) {
|
||||||
|
this.selectedTabIndex = event.index;
|
||||||
|
this.applyTabFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
applyTabFilter() {
|
||||||
|
let filteredData = [...this.assetRegistrations];
|
||||||
|
|
||||||
|
switch (this.selectedTabIndex) {
|
||||||
|
case 0: // All
|
||||||
|
break;
|
||||||
|
case 1: // Pending
|
||||||
|
filteredData = filteredData.filter(item => item.status === 'Pending');
|
||||||
|
break;
|
||||||
|
case 2: // Complete
|
||||||
|
filteredData = filteredData.filter(item => item.status === 'Complete');
|
||||||
|
break;
|
||||||
|
case 3: // In Progress
|
||||||
|
filteredData = filteredData.filter(item => item.status === 'In Progress');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dataSource.data = filteredData;
|
||||||
|
}
|
||||||
|
|
||||||
|
getStatusColor(status: string): string {
|
||||||
|
switch (status) {
|
||||||
|
case 'Complete':
|
||||||
|
return 'accent-green';
|
||||||
|
case 'In Progress':
|
||||||
|
return 'yellow-orange';
|
||||||
|
case 'Pending':
|
||||||
|
return 'yellow-orange';
|
||||||
|
case 'Critical':
|
||||||
|
return 'red';
|
||||||
|
case 'Over Due':
|
||||||
|
return 'yellow-orange';
|
||||||
|
default:
|
||||||
|
return 'light-gray';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
formatCurrency(amount: number): string {
|
||||||
|
return new Intl.NumberFormat('en-US', {
|
||||||
|
style: 'currency',
|
||||||
|
currency: 'THB',
|
||||||
|
minimumFractionDigits: 0
|
||||||
|
}).format(amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
registerAsset() {
|
||||||
|
// Navigate to registration form
|
||||||
|
this.router.navigate(['/asset/registration/form']);
|
||||||
|
}
|
||||||
|
|
||||||
|
editAsset(asset: any) {
|
||||||
|
// Navigate to edit form with asset data
|
||||||
|
this.router.navigate(['/asset/registration/edit', asset.assetId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
viewAsset(asset: any) {
|
||||||
|
// Navigate to asset details
|
||||||
|
this.router.navigate(['/asset/details', asset.assetId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteAsset(asset: any) {
|
||||||
|
// Show confirmation dialog
|
||||||
|
if (confirm(`Are you sure you want to delete asset ${asset.assetId}?`)) {
|
||||||
|
// Remove from data source
|
||||||
|
const index = this.dataSource.data.findIndex(item => item.assetId === asset.assetId);
|
||||||
|
if (index > -1) {
|
||||||
|
this.dataSource.data.splice(index, 1);
|
||||||
|
this.dataSource._updateChangeSubscription();
|
||||||
|
this.applyTabFilter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
166
src/app/asset/retirement/asset-retirement.component.css
Normal file
166
src/app/asset/retirement/asset-retirement.component.css
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
/* Asset Retirement Container */
|
||||||
|
.asset-retirement-container {
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Page Header */
|
||||||
|
.page-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-content h1 {
|
||||||
|
color: var(--white);
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-content p {
|
||||||
|
color: var(--light-gray);
|
||||||
|
font-size: 16px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-actions button {
|
||||||
|
background-color: var(--primary-red) !important;
|
||||||
|
color: var(--white) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Table Card */
|
||||||
|
.table-card {
|
||||||
|
background: var(--medium-gray) !important;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-card ::ng-deep .mat-mdc-card-content {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-container {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.retirement-table {
|
||||||
|
width: 100%;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.retirement-table ::ng-deep .mat-mdc-header-cell {
|
||||||
|
background-color: rgba(255, 255, 255, 0.05);
|
||||||
|
color: var(--white);
|
||||||
|
font-weight: 500;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.retirement-table ::ng-deep .mat-mdc-cell {
|
||||||
|
color: var(--light-gray);
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.retirement-table ::ng-deep .mat-mdc-row:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
.retirement-table ::ng-deep .mat-mdc-sort-header {
|
||||||
|
color: var(--white) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.retirement-table ::ng-deep .mat-mdc-sort-header-arrow {
|
||||||
|
color: var(--light-gray) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Action Buttons */
|
||||||
|
.action-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons button {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons ::ng-deep .mat-mdc-icon-button {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons ::ng-deep .mat-mdc-icon-button.mat-primary {
|
||||||
|
color: var(--primary-red) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons ::ng-deep .mat-mdc-icon-button.mat-accent {
|
||||||
|
color: var(--yellow-orange) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Disposal Chip */
|
||||||
|
::ng-deep .mat-mdc-chip.disposal-chip {
|
||||||
|
background-color: rgba(255, 68, 68, 0.2) !important;
|
||||||
|
color: var(--red) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Paginator */
|
||||||
|
::ng-deep .mat-mdc-paginator {
|
||||||
|
background-color: rgba(255, 255, 255, 0.02);
|
||||||
|
color: var(--light-gray);
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-paginator-page-size-label {
|
||||||
|
color: var(--light-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-paginator-range-label {
|
||||||
|
color: var(--light-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-paginator-navigation-previous,
|
||||||
|
::ng-deep .mat-mdc-paginator-navigation-next,
|
||||||
|
::ng-deep .mat-mdc-paginator-navigation-first,
|
||||||
|
::ng-deep .mat-mdc-paginator-navigation-last {
|
||||||
|
color: var(--light-gray) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Design */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.asset-retirement-container {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-content h1 {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-container {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.retirement-table {
|
||||||
|
min-width: 1000px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Material Design Overrides */
|
||||||
|
::ng-deep .mat-mdc-tooltip {
|
||||||
|
background-color: var(--medium-gray) !important;
|
||||||
|
color: var(--white) !important;
|
||||||
|
font-size: 12px !important;
|
||||||
|
}
|
||||||
96
src/app/asset/retirement/asset-retirement.component.html
Normal file
96
src/app/asset/retirement/asset-retirement.component.html
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
<div class="asset-retirement-container">
|
||||||
|
<div class="page-header">
|
||||||
|
<div class="header-content">
|
||||||
|
<h1>Asset Retirement</h1>
|
||||||
|
<p>Track retired and disposed assets</p>
|
||||||
|
</div>
|
||||||
|
<div class="header-actions">
|
||||||
|
<button mat-raised-button color="primary" (click)="retireAsset()">
|
||||||
|
<mat-icon>delete_forever</mat-icon>
|
||||||
|
Retire Asset
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Table -->
|
||||||
|
<mat-card class="table-card">
|
||||||
|
<mat-card-content>
|
||||||
|
<div class="table-container">
|
||||||
|
<table mat-table [dataSource]="dataSource" matSort class="retirement-table">
|
||||||
|
|
||||||
|
<!-- Asset ID Column -->
|
||||||
|
<ng-container matColumnDef="assetId">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Asset ID </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.assetId}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Asset Name Column -->
|
||||||
|
<ng-container matColumnDef="assetName">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Asset Name </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.assetName}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Category Column -->
|
||||||
|
<ng-container matColumnDef="category">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Category </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.category}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Department Column -->
|
||||||
|
<ng-container matColumnDef="department">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Department </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.department}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Retirement Date Column -->
|
||||||
|
<ng-container matColumnDef="retirementDate">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Retirement Date </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.retirementDate | date:'shortDate'}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Retirement Reason Column -->
|
||||||
|
<ng-container matColumnDef="retirementReason">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Retirement Reason </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.retirementReason}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Retired By Column -->
|
||||||
|
<ng-container matColumnDef="retiredBy">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Retired By </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.retiredBy}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Disposal Method Column -->
|
||||||
|
<ng-container matColumnDef="disposalMethod">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Disposal Method </th>
|
||||||
|
<td mat-cell *matCellDef="let element">
|
||||||
|
<mat-chip class="disposal-chip">
|
||||||
|
{{element.disposalMethod}}
|
||||||
|
</mat-chip>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Actions Column -->
|
||||||
|
<ng-container matColumnDef="actions">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Actions </th>
|
||||||
|
<td mat-cell *matCellDef="let element">
|
||||||
|
<div class="action-buttons">
|
||||||
|
<button mat-icon-button color="primary" (click)="viewRetirementDetails(element)" matTooltip="View Details">
|
||||||
|
<mat-icon>visibility</mat-icon>
|
||||||
|
</button>
|
||||||
|
<button mat-icon-button color="accent" (click)="editRetirement(element)" matTooltip="Edit Retirement">
|
||||||
|
<mat-icon>edit</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<mat-paginator [pageSizeOptions]="[10, 25, 50, 100]" showFirstLastButtons></mat-paginator>
|
||||||
|
</div>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
</div>
|
||||||
110
src/app/asset/retirement/asset-retirement.component.ts
Normal file
110
src/app/asset/retirement/asset-retirement.component.ts
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { MatTableModule, MatTableDataSource } from '@angular/material/table';
|
||||||
|
import { MatPaginatorModule, MatPaginator } from '@angular/material/paginator';
|
||||||
|
import { MatSortModule, MatSort } from '@angular/material/sort';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatCardModule } from '@angular/material/card';
|
||||||
|
import { MatChipsModule } from '@angular/material/chips';
|
||||||
|
import { MatDialogModule } from '@angular/material/dialog';
|
||||||
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-asset-retirement',
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
MatTableModule,
|
||||||
|
MatPaginatorModule,
|
||||||
|
MatSortModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatCardModule,
|
||||||
|
MatChipsModule,
|
||||||
|
MatDialogModule,
|
||||||
|
MatTooltipModule
|
||||||
|
],
|
||||||
|
templateUrl: './asset-retirement.component.html',
|
||||||
|
styleUrl: './asset-retirement.component.css'
|
||||||
|
})
|
||||||
|
export class AssetRetirementComponent implements OnInit {
|
||||||
|
|
||||||
|
@ViewChild(MatPaginator) paginator!: MatPaginator;
|
||||||
|
@ViewChild(MatSort) sort!: MatSort;
|
||||||
|
|
||||||
|
displayedColumns: string[] = [
|
||||||
|
'assetId',
|
||||||
|
'assetName',
|
||||||
|
'category',
|
||||||
|
'department',
|
||||||
|
'retirementDate',
|
||||||
|
'retirementReason',
|
||||||
|
'retiredBy',
|
||||||
|
'disposalMethod',
|
||||||
|
'actions'
|
||||||
|
];
|
||||||
|
|
||||||
|
dataSource: MatTableDataSource<any>;
|
||||||
|
|
||||||
|
// Sample data
|
||||||
|
retiredAssets = [
|
||||||
|
{
|
||||||
|
assetId: 'AST-006',
|
||||||
|
assetName: 'Air Conditioning Unit',
|
||||||
|
category: 'Building Equipment',
|
||||||
|
department: 'Facilities',
|
||||||
|
retirementDate: '2024-01-10',
|
||||||
|
retirementReason: 'End of useful life',
|
||||||
|
retiredBy: 'John Smith',
|
||||||
|
disposalMethod: 'Recycled'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
assetId: 'AST-007',
|
||||||
|
assetName: 'Old Desktop Computer',
|
||||||
|
category: 'Computers & IT',
|
||||||
|
department: 'IT Department',
|
||||||
|
retirementDate: '2024-01-08',
|
||||||
|
retirementReason: 'Obsolete technology',
|
||||||
|
retiredBy: 'Sarah Johnson',
|
||||||
|
disposalMethod: 'Donated'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
assetId: 'AST-008',
|
||||||
|
assetName: 'Broken Office Chair',
|
||||||
|
category: 'Office Furniture',
|
||||||
|
department: 'HR Department',
|
||||||
|
retirementDate: '2024-01-05',
|
||||||
|
retirementReason: 'Damaged beyond repair',
|
||||||
|
retiredBy: 'Mike Wilson',
|
||||||
|
disposalMethod: 'Disposed'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.dataSource = new MatTableDataSource(this.retiredAssets);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
this.dataSource.paginator = this.paginator;
|
||||||
|
this.dataSource.sort = this.sort;
|
||||||
|
}
|
||||||
|
|
||||||
|
retireAsset() {
|
||||||
|
// Navigate to retirement form
|
||||||
|
console.log('Retire asset');
|
||||||
|
}
|
||||||
|
|
||||||
|
viewRetirementDetails(asset: any) {
|
||||||
|
// Navigate to retirement details
|
||||||
|
console.log('View retirement details:', asset.assetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
editRetirement(asset: any) {
|
||||||
|
// Navigate to edit retirement form
|
||||||
|
console.log('Edit retirement:', asset.assetId);
|
||||||
|
}
|
||||||
|
}
|
||||||
187
src/app/asset/transfer/asset-transfer.component.css
Normal file
187
src/app/asset/transfer/asset-transfer.component.css
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
/* Asset Transfer Container */
|
||||||
|
.asset-transfer-container {
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Page Header */
|
||||||
|
.page-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-content h1 {
|
||||||
|
color: var(--white);
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-content p {
|
||||||
|
color: var(--light-gray);
|
||||||
|
font-size: 16px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-actions button {
|
||||||
|
background-color: var(--primary-red) !important;
|
||||||
|
color: var(--white) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Table Card */
|
||||||
|
.table-card {
|
||||||
|
background: var(--medium-gray) !important;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-card ::ng-deep .mat-mdc-card-content {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-container {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.transfer-table {
|
||||||
|
width: 100%;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.transfer-table ::ng-deep .mat-mdc-header-cell {
|
||||||
|
background-color: rgba(255, 255, 255, 0.05);
|
||||||
|
color: var(--white);
|
||||||
|
font-weight: 500;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.transfer-table ::ng-deep .mat-mdc-cell {
|
||||||
|
color: var(--light-gray);
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.transfer-table ::ng-deep .mat-mdc-row:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
.transfer-table ::ng-deep .mat-mdc-sort-header {
|
||||||
|
color: var(--white) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.transfer-table ::ng-deep .mat-mdc-sort-header-arrow {
|
||||||
|
color: var(--light-gray) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Action Buttons */
|
||||||
|
.action-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons button {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons ::ng-deep .mat-mdc-icon-button {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons ::ng-deep .mat-mdc-icon-button.mat-primary {
|
||||||
|
color: var(--primary-red) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons ::ng-deep .mat-mdc-icon-button.mat-accent {
|
||||||
|
color: var(--accent-green) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons ::ng-deep .mat-mdc-icon-button.mat-warn {
|
||||||
|
color: var(--red) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Paginator */
|
||||||
|
::ng-deep .mat-mdc-paginator {
|
||||||
|
background-color: rgba(255, 255, 255, 0.02);
|
||||||
|
color: var(--light-gray);
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-paginator-page-size-label {
|
||||||
|
color: var(--light-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-paginator-range-label {
|
||||||
|
color: var(--light-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-paginator-navigation-previous,
|
||||||
|
::ng-deep .mat-mdc-paginator-navigation-next,
|
||||||
|
::ng-deep .mat-mdc-paginator-navigation-first,
|
||||||
|
::ng-deep .mat-mdc-paginator-navigation-last {
|
||||||
|
color: var(--light-gray) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Chip Styling */
|
||||||
|
::ng-deep .mat-mdc-chip {
|
||||||
|
background-color: rgba(255, 255, 255, 0.1) !important;
|
||||||
|
color: var(--white) !important;
|
||||||
|
font-size: 12px !important;
|
||||||
|
height: 24px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-chip.accent-green {
|
||||||
|
background-color: rgba(0, 204, 102, 0.2) !important;
|
||||||
|
color: var(--accent-green) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-chip.yellow-orange {
|
||||||
|
background-color: rgba(255, 170, 0, 0.2) !important;
|
||||||
|
color: var(--yellow-orange) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-chip.red {
|
||||||
|
background-color: rgba(255, 68, 68, 0.2) !important;
|
||||||
|
color: var(--red) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Design */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.asset-transfer-container {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-content h1 {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-container {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.transfer-table {
|
||||||
|
min-width: 1000px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Material Design Overrides */
|
||||||
|
::ng-deep .mat-mdc-tooltip {
|
||||||
|
background-color: var(--medium-gray) !important;
|
||||||
|
color: var(--white) !important;
|
||||||
|
font-size: 12px !important;
|
||||||
|
}
|
||||||
99
src/app/asset/transfer/asset-transfer.component.html
Normal file
99
src/app/asset/transfer/asset-transfer.component.html
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
<div class="asset-transfer-container">
|
||||||
|
<div class="page-header">
|
||||||
|
<div class="header-content">
|
||||||
|
<h1>Asset Transfer</h1>
|
||||||
|
<p>Manage asset transfers between departments</p>
|
||||||
|
</div>
|
||||||
|
<div class="header-actions">
|
||||||
|
<button mat-raised-button color="primary" (click)="createTransferRequest()">
|
||||||
|
<mat-icon>add</mat-icon>
|
||||||
|
New Transfer Request
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Table -->
|
||||||
|
<mat-card class="table-card">
|
||||||
|
<mat-card-content>
|
||||||
|
<div class="table-container">
|
||||||
|
<table mat-table [dataSource]="dataSource" matSort class="transfer-table">
|
||||||
|
|
||||||
|
<!-- Transfer ID Column -->
|
||||||
|
<ng-container matColumnDef="transferId">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Transfer ID </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.transferId}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Asset ID Column -->
|
||||||
|
<ng-container matColumnDef="assetId">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Asset ID </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.assetId}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Asset Name Column -->
|
||||||
|
<ng-container matColumnDef="assetName">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Asset Name </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.assetName}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- From Department Column -->
|
||||||
|
<ng-container matColumnDef="fromDepartment">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> From Department </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.fromDepartment}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- To Department Column -->
|
||||||
|
<ng-container matColumnDef="toDepartment">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> To Department </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.toDepartment}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Request Date Column -->
|
||||||
|
<ng-container matColumnDef="requestDate">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Request Date </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.requestDate | date:'shortDate'}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Requested By Column -->
|
||||||
|
<ng-container matColumnDef="requestedBy">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Requested By </th>
|
||||||
|
<td mat-cell *matCellDef="let element"> {{element.requestedBy}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Status Column -->
|
||||||
|
<ng-container matColumnDef="status">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Status </th>
|
||||||
|
<td mat-cell *matCellDef="let element">
|
||||||
|
<mat-chip [class]="getStatusColor(element.status)">
|
||||||
|
{{element.status}}
|
||||||
|
</mat-chip>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Actions Column -->
|
||||||
|
<ng-container matColumnDef="actions">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Actions </th>
|
||||||
|
<td mat-cell *matCellDef="let element">
|
||||||
|
<div class="action-buttons">
|
||||||
|
<button mat-icon-button color="primary" (click)="viewTransferDetails(element)" matTooltip="View Details">
|
||||||
|
<mat-icon>visibility</mat-icon>
|
||||||
|
</button>
|
||||||
|
<button mat-icon-button color="accent" (click)="approveTransfer(element)" matTooltip="Approve Transfer" *ngIf="element.status === 'Pending'">
|
||||||
|
<mat-icon>check</mat-icon>
|
||||||
|
</button>
|
||||||
|
<button mat-icon-button color="warn" (click)="rejectTransfer(element)" matTooltip="Reject Transfer" *ngIf="element.status === 'Pending'">
|
||||||
|
<mat-icon>close</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<mat-paginator [pageSizeOptions]="[10, 25, 50, 100]" showFirstLastButtons></mat-paginator>
|
||||||
|
</div>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
</div>
|
||||||
144
src/app/asset/transfer/asset-transfer.component.ts
Normal file
144
src/app/asset/transfer/asset-transfer.component.ts
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { MatTableModule, MatTableDataSource } from '@angular/material/table';
|
||||||
|
import { MatPaginatorModule, MatPaginator } from '@angular/material/paginator';
|
||||||
|
import { MatSortModule, MatSort } from '@angular/material/sort';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatCardModule } from '@angular/material/card';
|
||||||
|
import { MatChipsModule } from '@angular/material/chips';
|
||||||
|
import { MatDialogModule, MatDialog } from '@angular/material/dialog';
|
||||||
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-asset-transfer',
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
MatTableModule,
|
||||||
|
MatPaginatorModule,
|
||||||
|
MatSortModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatCardModule,
|
||||||
|
MatChipsModule,
|
||||||
|
MatDialogModule,
|
||||||
|
MatTooltipModule
|
||||||
|
],
|
||||||
|
templateUrl: './asset-transfer.component.html',
|
||||||
|
styleUrl: './asset-transfer.component.css'
|
||||||
|
})
|
||||||
|
export class AssetTransferComponent implements OnInit {
|
||||||
|
|
||||||
|
@ViewChild(MatPaginator) paginator!: MatPaginator;
|
||||||
|
@ViewChild(MatSort) sort!: MatSort;
|
||||||
|
|
||||||
|
displayedColumns: string[] = [
|
||||||
|
'transferId',
|
||||||
|
'assetId',
|
||||||
|
'assetName',
|
||||||
|
'fromDepartment',
|
||||||
|
'toDepartment',
|
||||||
|
'requestDate',
|
||||||
|
'requestedBy',
|
||||||
|
'status',
|
||||||
|
'actions'
|
||||||
|
];
|
||||||
|
|
||||||
|
dataSource: MatTableDataSource<any>;
|
||||||
|
|
||||||
|
// Sample data
|
||||||
|
transferRequests = [
|
||||||
|
{
|
||||||
|
transferId: 'TR-2024-001',
|
||||||
|
assetId: 'AST-001',
|
||||||
|
assetName: 'Dell Latitude 5520 Laptop',
|
||||||
|
fromDepartment: 'IT Department',
|
||||||
|
toDepartment: 'Sales Department',
|
||||||
|
requestDate: '2024-01-15',
|
||||||
|
requestedBy: 'John Smith',
|
||||||
|
status: 'Pending'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
transferId: 'TR-2024-002',
|
||||||
|
assetId: 'AST-002',
|
||||||
|
assetName: 'Office Desk Set',
|
||||||
|
fromDepartment: 'HR Department',
|
||||||
|
toDepartment: 'Marketing Department',
|
||||||
|
requestDate: '2024-01-14',
|
||||||
|
requestedBy: 'Sarah Johnson',
|
||||||
|
status: 'Approved'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
transferId: 'TR-2024-003',
|
||||||
|
assetId: 'AST-003',
|
||||||
|
assetName: 'Industrial Drill Machine',
|
||||||
|
fromDepartment: 'Production',
|
||||||
|
toDepartment: 'Maintenance',
|
||||||
|
requestDate: '2024-01-13',
|
||||||
|
requestedBy: 'Mike Wilson',
|
||||||
|
status: 'Rejected'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
transferId: 'TR-2024-004',
|
||||||
|
assetId: 'AST-004',
|
||||||
|
assetName: 'HP LaserJet Printer',
|
||||||
|
fromDepartment: 'Administration',
|
||||||
|
toDepartment: 'IT Department',
|
||||||
|
requestDate: '2024-01-12',
|
||||||
|
requestedBy: 'David Brown',
|
||||||
|
status: 'In Progress'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.dataSource = new MatTableDataSource(this.transferRequests);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
this.dataSource.paginator = this.paginator;
|
||||||
|
this.dataSource.sort = this.sort;
|
||||||
|
}
|
||||||
|
|
||||||
|
getStatusColor(status: string): string {
|
||||||
|
switch (status) {
|
||||||
|
case 'Approved':
|
||||||
|
return 'accent-green';
|
||||||
|
case 'Pending':
|
||||||
|
return 'yellow-orange';
|
||||||
|
case 'In Progress':
|
||||||
|
return 'yellow-orange';
|
||||||
|
case 'Rejected':
|
||||||
|
return 'red';
|
||||||
|
default:
|
||||||
|
return 'light-gray';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
approveTransfer(transfer: any) {
|
||||||
|
if (confirm(`Approve transfer ${transfer.transferId}?`)) {
|
||||||
|
transfer.status = 'Approved';
|
||||||
|
this.dataSource._updateChangeSubscription();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rejectTransfer(transfer: any) {
|
||||||
|
if (confirm(`Reject transfer ${transfer.transferId}?`)) {
|
||||||
|
transfer.status = 'Rejected';
|
||||||
|
this.dataSource._updateChangeSubscription();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createTransferRequest() {
|
||||||
|
// Navigate to create transfer form
|
||||||
|
console.log('Create new transfer request');
|
||||||
|
}
|
||||||
|
|
||||||
|
viewTransferDetails(transfer: any) {
|
||||||
|
// Navigate to transfer details
|
||||||
|
console.log('View transfer details:', transfer.transferId);
|
||||||
|
}
|
||||||
|
}
|
||||||
425
src/app/auth/login/login.component.css
Normal file
425
src/app/auth/login/login.component.css
Normal file
@ -0,0 +1,425 @@
|
|||||||
|
/* Login Container */
|
||||||
|
.login-container {
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: linear-gradient(135deg, var(--dark-gray) 0%, var(--medium-gray) 100%);
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Login Card */
|
||||||
|
.login-card {
|
||||||
|
background-color: var(--medium-gray);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 2rem;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 400px;
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
animation: slideInUp 0.6s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Login Header */
|
||||||
|
.login-header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-subtitle {
|
||||||
|
color: var(--light-gray);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Login Form */
|
||||||
|
.login-form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-field {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-field label {
|
||||||
|
color: var(--white);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-field input {
|
||||||
|
padding: 0.75rem;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 6px;
|
||||||
|
background-color: rgba(255, 255, 255, 0.05);
|
||||||
|
color: var(--white);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-field input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--primary-red);
|
||||||
|
background-color: rgba(255, 255, 255, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-field input::placeholder {
|
||||||
|
color: var(--light-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-field .error {
|
||||||
|
color: var(--red);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Form Options */
|
||||||
|
.form-options {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remember-checkbox {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-label {
|
||||||
|
color: var(--light-gray);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.forgot-password {
|
||||||
|
color: var(--primary-red);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.forgot-password:hover {
|
||||||
|
color: #b00000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Login Button */
|
||||||
|
.login-button {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.875rem;
|
||||||
|
background-color: var(--primary-red);
|
||||||
|
color: var(--white);
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-button:hover:not(:disabled) {
|
||||||
|
background-color: #b00000;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-button:disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-spinner {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
border-top: 2px solid var(--white);
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-icon {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Login Footer */
|
||||||
|
.login-footer {
|
||||||
|
margin-top: 2rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-section {
|
||||||
|
background-color: rgba(255, 255, 255, 0.05);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 1rem;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-icon {
|
||||||
|
color: var(--yellow-orange);
|
||||||
|
font-size: 1rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-content {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-title {
|
||||||
|
color: var(--white);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-credentials {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.credential-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.credential-icon {
|
||||||
|
color: var(--light-gray);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.credential-label {
|
||||||
|
color: var(--light-gray);
|
||||||
|
min-width: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.credential-value {
|
||||||
|
color: var(--white);
|
||||||
|
font-family: monospace;
|
||||||
|
background-color: rgba(0, 0, 0, 0.3);
|
||||||
|
padding: 0.125rem 0.375rem;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animations */
|
||||||
|
@keyframes slideInUp {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(30px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Material Design Overrides */
|
||||||
|
::ng-deep .mat-mdc-form-field {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-text-field-wrapper {
|
||||||
|
background-color: rgba(255, 255, 255, 0.05) !important;
|
||||||
|
border-radius: 6px !important;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-text-field-wrapper:hover {
|
||||||
|
border-color: rgba(200, 0, 0, 0.3) !important;
|
||||||
|
background-color: rgba(255, 255, 255, 0.08) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-form-field-focus-overlay {
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-form-field-outline {
|
||||||
|
color: rgba(255, 255, 255, 0.2) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-form-field-outline-thick {
|
||||||
|
color: var(--primary-red) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-form-field-label {
|
||||||
|
color: var(--light-gray) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-input-element {
|
||||||
|
color: var(--white) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-checkbox {
|
||||||
|
color: var(--light-gray) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-checkbox .mdc-checkbox {
|
||||||
|
color: var(--primary-red) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-mdc-checkbox .mdc-checkbox__ripple {
|
||||||
|
color: var(--primary-red) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Design */
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.login-container {
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-card {
|
||||||
|
padding: 1.5rem;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
font-size: 1.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-options {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-button {
|
||||||
|
padding: 0.75rem;
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-section {
|
||||||
|
padding: 0.75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 360px) {
|
||||||
|
.login-card {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form {
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-field input {
|
||||||
|
padding: 0.625rem;
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-button {
|
||||||
|
padding: 0.625rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compact spacing optimizations */
|
||||||
|
.login-container {
|
||||||
|
padding: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-card {
|
||||||
|
padding: 1.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-header {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-container {
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form {
|
||||||
|
gap: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-options {
|
||||||
|
margin: 0.75rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-button {
|
||||||
|
margin-top: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-footer {
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-section {
|
||||||
|
margin-top: 0.75rem;
|
||||||
|
padding: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reduce font sizes for better density */
|
||||||
|
.logo {
|
||||||
|
font-size: 1.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-subtitle {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-field label {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-field input {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-label,
|
||||||
|
.forgot-password {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-button {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-title {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.credential-item {
|
||||||
|
font-size: 0.6875rem;
|
||||||
|
}
|
||||||
97
src/app/auth/login/login.component.html
Normal file
97
src/app/auth/login/login.component.html
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
<div class="login-container">
|
||||||
|
<div class="login-card">
|
||||||
|
<div class="login-header">
|
||||||
|
<div class="logo-container">
|
||||||
|
<div class="logo">
|
||||||
|
<span class="logo-text-white">PINE</span>
|
||||||
|
<span class="logo-text-red">PIM</span>
|
||||||
|
</div>
|
||||||
|
<div class="logo-subtitle">Asset Management System</div>
|
||||||
|
</div>
|
||||||
|
<h2>Welcome Back</h2>
|
||||||
|
<p>Sign in to your account</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form [formGroup]="loginForm" (ngSubmit)="onSubmit()" class="login-form">
|
||||||
|
<mat-form-field appearance="outline" class="form-field">
|
||||||
|
<mat-label>
|
||||||
|
<mat-icon class="field-icon" aria-hidden="false">person</mat-icon>
|
||||||
|
Employee ID
|
||||||
|
</mat-label>
|
||||||
|
<input matInput formControlName="employeeId" placeholder="Enter your employee ID">
|
||||||
|
<mat-icon matSuffix class="field-suffix-icon" aria-hidden="false">badge</mat-icon>
|
||||||
|
<mat-error *ngIf="loginForm.get('employeeId')?.hasError('required')">
|
||||||
|
<mat-icon class="error-icon" aria-hidden="false">error</mat-icon>
|
||||||
|
Employee ID is required
|
||||||
|
</mat-error>
|
||||||
|
<mat-error *ngIf="loginForm.get('employeeId')?.hasError('minlength')">
|
||||||
|
<mat-icon class="error-icon" aria-hidden="false">error</mat-icon>
|
||||||
|
Employee ID must be at least 3 characters
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="form-field">
|
||||||
|
<mat-label>
|
||||||
|
<mat-icon class="field-icon" aria-hidden="false">lock</mat-icon>
|
||||||
|
Password
|
||||||
|
</mat-label>
|
||||||
|
<input matInput [type]="hidePassword ? 'password' : 'text'" formControlName="password" placeholder="Enter your password">
|
||||||
|
<button mat-icon-button matSuffix (click)="togglePasswordVisibility()" type="button" class="visibility-toggle">
|
||||||
|
<mat-icon aria-hidden="false">{{hidePassword ? 'visibility_off' : 'visibility'}}</mat-icon>
|
||||||
|
</button>
|
||||||
|
<mat-error *ngIf="loginForm.get('password')?.hasError('required')">
|
||||||
|
<mat-icon class="error-icon" aria-hidden="false">error</mat-icon>
|
||||||
|
Password is required
|
||||||
|
</mat-error>
|
||||||
|
<mat-error *ngIf="loginForm.get('password')?.hasError('minlength')">
|
||||||
|
<mat-icon class="error-icon" aria-hidden="false">error</mat-icon>
|
||||||
|
Password must be at least 6 characters
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<div class="form-options">
|
||||||
|
<mat-checkbox formControlName="rememberMe" color="primary" class="remember-checkbox">
|
||||||
|
<span class="checkbox-label">Remember me</span>
|
||||||
|
</mat-checkbox>
|
||||||
|
<a href="#" class="forgot-password">
|
||||||
|
<mat-icon class="forgot-icon" aria-hidden="false">help</mat-icon>
|
||||||
|
Forgot password?
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button mat-raised-button
|
||||||
|
type="submit"
|
||||||
|
class="login-button"
|
||||||
|
[disabled]="loginForm.invalid || isLoading"
|
||||||
|
[class.loading]="isLoading">
|
||||||
|
<div class="button-content">
|
||||||
|
<mat-spinner *ngIf="isLoading" diameter="20" class="button-spinner"></mat-spinner>
|
||||||
|
<mat-icon *ngIf="!isLoading" class="button-icon" aria-hidden="false">login</mat-icon>
|
||||||
|
<span *ngIf="!isLoading">Sign In</span>
|
||||||
|
<span *ngIf="isLoading">Signing In...</span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="login-footer">
|
||||||
|
<div class="demo-section">
|
||||||
|
<mat-icon class="demo-icon" aria-hidden="false">info</mat-icon>
|
||||||
|
<div class="demo-content">
|
||||||
|
<p class="demo-title">Demo Credentials:</p>
|
||||||
|
<div class="demo-credentials">
|
||||||
|
<div class="credential-item">
|
||||||
|
<mat-icon class="credential-icon" aria-hidden="false">person</mat-icon>
|
||||||
|
<span class="credential-label">Employee ID:</span>
|
||||||
|
<span class="credential-value">admin</span>
|
||||||
|
</div>
|
||||||
|
<div class="credential-item">
|
||||||
|
<mat-icon class="credential-icon" aria-hidden="false">lock</mat-icon>
|
||||||
|
<span class="credential-label">Password:</span>
|
||||||
|
<span class="credential-value">password</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
84
src/app/auth/login/login.component.ts
Normal file
84
src/app/auth/login/login.component.ts
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { FormBuilder, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||||
|
import { MatCardModule } from '@angular/material/card';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
|
||||||
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-login',
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatCheckboxModule,
|
||||||
|
MatCardModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatProgressSpinnerModule,
|
||||||
|
MatSnackBarModule
|
||||||
|
],
|
||||||
|
templateUrl: './login.component.html',
|
||||||
|
styleUrl: './login.component.css'
|
||||||
|
})
|
||||||
|
export class LoginComponent {
|
||||||
|
loginForm: FormGroup;
|
||||||
|
hidePassword = true;
|
||||||
|
isLoading = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private fb: FormBuilder,
|
||||||
|
private router: Router,
|
||||||
|
private snackBar: MatSnackBar
|
||||||
|
) {
|
||||||
|
this.loginForm = this.fb.group({
|
||||||
|
employeeId: ['', [Validators.required, Validators.minLength(3)]],
|
||||||
|
password: ['', [Validators.required, Validators.minLength(6)]],
|
||||||
|
rememberMe: [false]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onSubmit() {
|
||||||
|
if (this.loginForm.valid) {
|
||||||
|
this.isLoading = true;
|
||||||
|
|
||||||
|
// Simulate API call
|
||||||
|
setTimeout(() => {
|
||||||
|
this.isLoading = false;
|
||||||
|
|
||||||
|
// Mock authentication - in real app, this would be an API call
|
||||||
|
if (this.loginForm.value.employeeId === 'admin' && this.loginForm.value.password === 'password') {
|
||||||
|
// Store auth token/user info
|
||||||
|
localStorage.setItem('isAuthenticated', 'true');
|
||||||
|
localStorage.setItem('userRole', 'admin');
|
||||||
|
|
||||||
|
this.snackBar.open('Login successful!', 'Close', {
|
||||||
|
duration: 3000,
|
||||||
|
horizontalPosition: 'center',
|
||||||
|
verticalPosition: 'top'
|
||||||
|
});
|
||||||
|
|
||||||
|
this.router.navigate(['/dashboard']);
|
||||||
|
} else {
|
||||||
|
this.snackBar.open('Invalid credentials. Please try again.', 'Close', {
|
||||||
|
duration: 3000,
|
||||||
|
horizontalPosition: 'center',
|
||||||
|
verticalPosition: 'top'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 1500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
togglePasswordVisibility() {
|
||||||
|
this.hidePassword = !this.hidePassword;
|
||||||
|
}
|
||||||
|
}
|
||||||
529
src/app/dashboard/dashboard.component.css
Normal file
529
src/app/dashboard/dashboard.component.css
Normal file
@ -0,0 +1,529 @@
|
|||||||
|
/* Dashboard Container */
|
||||||
|
.dashboard-container {
|
||||||
|
padding: 0.5rem;
|
||||||
|
/* background-color: var(--dark-gray); */
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Page Header */
|
||||||
|
.page-header {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
padding: 0.75rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-icon {
|
||||||
|
color: var(--primary-red);
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-title h1 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 600;
|
||||||
|
/* color: var(--white); */
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-subtitle {
|
||||||
|
color: var(--light-gray);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stats Grid */
|
||||||
|
.stats-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||||
|
gap: 0.75rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
/* background-color: var(--medium-gray); */
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 1rem;
|
||||||
|
border-left: 4px solid var(--primary-red);
|
||||||
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||||
|
/* color: black; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-icon {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
color: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-info {
|
||||||
|
flex: 1;
|
||||||
|
margin-left: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-info h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.125rem;
|
||||||
|
font-weight: 600;
|
||||||
|
/* color: var(--white); */
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-info p {
|
||||||
|
margin: 0.25rem 0 0;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: var(--light-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-change {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-change.positive {
|
||||||
|
color: var(--accent-green);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-change.negative {
|
||||||
|
color: var(--red);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Specific stat card styles */
|
||||||
|
.total-assets .stat-icon {
|
||||||
|
background-color: var(--primary-red);
|
||||||
|
}
|
||||||
|
|
||||||
|
.total-value .stat-icon {
|
||||||
|
background-color: var(--accent-green);
|
||||||
|
}
|
||||||
|
|
||||||
|
.active-assets .stat-icon {
|
||||||
|
background-color: var(--yellow-orange);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pending-assets .stat-icon {
|
||||||
|
background-color: var(--red);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Content Grid */
|
||||||
|
.content-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 2fr 1fr;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-card {
|
||||||
|
/* background-color: var(--medium-gray); */
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-card h3 {
|
||||||
|
margin: 0 0 0.75rem 0;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
/* color: var(--white); */
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-icon {
|
||||||
|
color: var(--primary-red);
|
||||||
|
font-size: 1.125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Activity List */
|
||||||
|
.activity-list {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 0.75rem;
|
||||||
|
padding: 0.75rem 0;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-item:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-icon {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: var(--white);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-asset {
|
||||||
|
background-color: var(--accent-green);
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-maintenance {
|
||||||
|
background-color: var(--yellow-orange);
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-alert {
|
||||||
|
background-color: var(--red);
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-content {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-time {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--light-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-description {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: var(--white);
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Category List */
|
||||||
|
.category-list {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0.75rem 0;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-item:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-color {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-blue-500 {
|
||||||
|
background-color: #3b82f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-green-500 {
|
||||||
|
background-color: var(--accent-green);
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-yellow-500 {
|
||||||
|
background-color: var(--yellow-orange);
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-red-500 {
|
||||||
|
background-color: var(--red);
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-purple-500 {
|
||||||
|
background-color: #8b5cf6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-progress {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-high {
|
||||||
|
color: var(--accent-green);
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-medium {
|
||||||
|
color: var(--yellow-orange);
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-low {
|
||||||
|
color: var(--red);
|
||||||
|
}
|
||||||
|
|
||||||
|
.percentage {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Alert List */
|
||||||
|
.alert-list {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 0.75rem;
|
||||||
|
padding: 0.75rem 0;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-item:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-priority {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-top: 0.375rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.priority-critical {
|
||||||
|
background-color: var(--red);
|
||||||
|
}
|
||||||
|
|
||||||
|
.priority-high {
|
||||||
|
background-color: var(--yellow-orange);
|
||||||
|
}
|
||||||
|
|
||||||
|
.priority-low {
|
||||||
|
background-color: var(--accent-green);
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-content {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-description {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: var(--white);
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-date {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--light-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Material Chip Customization */
|
||||||
|
.mat-mdc-chip {
|
||||||
|
font-size: 0.75rem !important;
|
||||||
|
height: 24px !important;
|
||||||
|
padding: 0 8px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Design */
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.content-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-grid {
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
padding: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-card {
|
||||||
|
padding: 0.75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.dashboard-container {
|
||||||
|
padding: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-title h1 {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-grid {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-icon {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-info h3 {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-card {
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-item,
|
||||||
|
.category-item,
|
||||||
|
.alert-item {
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.stats-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-content {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-info {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-icon,
|
||||||
|
.alert-priority {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animations */
|
||||||
|
.stat-card,
|
||||||
|
.activity-item,
|
||||||
|
.category-item,
|
||||||
|
.alert-item {
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-item:hover,
|
||||||
|
.category-item:hover,
|
||||||
|
.alert-item:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.05);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding-left: 0.5rem;
|
||||||
|
padding-right: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compact spacing optimizations */
|
||||||
|
.dashboard-container {
|
||||||
|
padding: 0.375rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-grid {
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-grid {
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-card {
|
||||||
|
padding: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-item,
|
||||||
|
.category-item,
|
||||||
|
.alert-item {
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reduce font sizes for better density */
|
||||||
|
.header-title h1 {
|
||||||
|
font-size: 1.375rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-info h3 {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-card h3 {
|
||||||
|
font-size: 0.9375rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-description,
|
||||||
|
.alert-description {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.percentage {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
163
src/app/dashboard/dashboard.component.html
Normal file
163
src/app/dashboard/dashboard.component.html
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
<div class="dashboard-container">
|
||||||
|
<!-- Page Header -->
|
||||||
|
<div class="page-header">
|
||||||
|
<div class="header-content">
|
||||||
|
<div class="header-title">
|
||||||
|
<!-- <mat-icon class="header-icon" aria-hidden="false">dashboard</mat-icon> -->
|
||||||
|
<h1>Dashboard</h1>
|
||||||
|
</div>
|
||||||
|
<p class="header-subtitle">Welcome back! Here's an overview of your asset management system.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Statistics Cards -->
|
||||||
|
<div class="stats-grid">
|
||||||
|
<mat-card class="stat-card total-assets">
|
||||||
|
<mat-card-content>
|
||||||
|
<div class="stat-content">
|
||||||
|
<div class="stat-icon">
|
||||||
|
<mat-icon aria-hidden="false">inventory_2</mat-icon>
|
||||||
|
</div>
|
||||||
|
<div class="stat-info">
|
||||||
|
<h3>{{ stats.totalAssets }}</h3>
|
||||||
|
<p>Total Assets</p>
|
||||||
|
<span class="stat-change positive">+12% from last month</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
|
||||||
|
<mat-card class="stat-card total-value">
|
||||||
|
<mat-card-content>
|
||||||
|
<div class="stat-content">
|
||||||
|
<div class="stat-icon">
|
||||||
|
<mat-icon aria-hidden="false">attach_money</mat-icon>
|
||||||
|
</div>
|
||||||
|
<div class="stat-info">
|
||||||
|
<h3>{{ formatCurrency(stats.totalValue) }}</h3>
|
||||||
|
<p>Total Value</p>
|
||||||
|
<span class="stat-change positive">+8% from last month</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
|
||||||
|
<mat-card class="stat-card active-users">
|
||||||
|
<mat-card-content>
|
||||||
|
<div class="stat-content">
|
||||||
|
<div class="stat-icon">
|
||||||
|
<mat-icon aria-hidden="false">people</mat-icon>
|
||||||
|
</div>
|
||||||
|
<div class="stat-info">
|
||||||
|
<h3>{{ stats.activeUsers }}</h3>
|
||||||
|
<p>Active Users</p>
|
||||||
|
<span class="stat-change positive">+5% from last month</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
|
||||||
|
<mat-card class="stat-card pending-tasks">
|
||||||
|
<mat-card-content>
|
||||||
|
<div class="stat-content">
|
||||||
|
<div class="stat-icon">
|
||||||
|
<mat-icon aria-hidden="false">pending_actions</mat-icon>
|
||||||
|
</div>
|
||||||
|
<div class="stat-info">
|
||||||
|
<h3>{{ stats.pendingTasks }}</h3>
|
||||||
|
<p>Pending Tasks</p>
|
||||||
|
<span class="stat-change negative">-3% from last month</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Content Grid -->
|
||||||
|
<div class="content-grid">
|
||||||
|
<!-- Recent Activities -->
|
||||||
|
<mat-card class="content-card activities-card">
|
||||||
|
<mat-card-header>
|
||||||
|
<mat-card-title>
|
||||||
|
<mat-icon class="card-icon" aria-hidden="false">timeline</mat-icon>
|
||||||
|
Recent Activities
|
||||||
|
</mat-card-title>
|
||||||
|
<mat-card-subtitle>Latest asset management activities</mat-card-subtitle>
|
||||||
|
</mat-card-header>
|
||||||
|
<mat-card-content>
|
||||||
|
<div class="activity-list">
|
||||||
|
<div class="activity-item" *ngFor="let activity of recentActivities">
|
||||||
|
<div class="activity-icon" [ngClass]="'activity-' + activity.type">
|
||||||
|
<mat-icon aria-hidden="false">{{ activity.icon }}</mat-icon>
|
||||||
|
</div>
|
||||||
|
<div class="activity-content">
|
||||||
|
<div class="activity-header">
|
||||||
|
<h4>{{ activity.title }}</h4>
|
||||||
|
<span class="activity-time">{{ activity.time }}</span>
|
||||||
|
</div>
|
||||||
|
<p class="activity-description">{{ activity.description }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
|
||||||
|
<!-- Asset Categories -->
|
||||||
|
<mat-card class="content-card categories-card">
|
||||||
|
<mat-card-header>
|
||||||
|
<mat-card-title>
|
||||||
|
<mat-icon class="card-icon" aria-hidden="false">pie_chart</mat-icon>
|
||||||
|
Asset Categories
|
||||||
|
</mat-card-title>
|
||||||
|
<mat-card-subtitle>Distribution by category</mat-card-subtitle>
|
||||||
|
</mat-card-header>
|
||||||
|
<mat-card-content>
|
||||||
|
<div class="category-list">
|
||||||
|
<div class="category-item" *ngFor="let category of assetCategories">
|
||||||
|
<div class="category-info">
|
||||||
|
<div class="category-header">
|
||||||
|
<div class="category-color" [ngClass]="'color-' + category.color"></div>
|
||||||
|
<h4>{{ category.name }}</h4>
|
||||||
|
</div>
|
||||||
|
<p>{{ category.count }} assets</p>
|
||||||
|
</div>
|
||||||
|
<div class="category-progress">
|
||||||
|
<mat-progress-bar
|
||||||
|
mode="determinate"
|
||||||
|
[value]="category.percentage"
|
||||||
|
[ngClass]="'progress-' + (category.percentage > 30 ? 'high' : category.percentage > 15 ? 'medium' : 'low')">
|
||||||
|
</mat-progress-bar>
|
||||||
|
<span class="percentage">{{ category.percentage }}%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
|
||||||
|
<!-- Maintenance Alerts -->
|
||||||
|
<mat-card class="content-card alerts-card">
|
||||||
|
<mat-card-header>
|
||||||
|
<mat-card-title>
|
||||||
|
<mat-icon class="card-icon" aria-hidden="false">warning</mat-icon>
|
||||||
|
Maintenance Alerts
|
||||||
|
</mat-card-title>
|
||||||
|
<mat-card-subtitle>Upcoming maintenance schedules</mat-card-subtitle>
|
||||||
|
</mat-card-header>
|
||||||
|
<mat-card-content>
|
||||||
|
<div class="alert-list">
|
||||||
|
<div class="alert-item" *ngFor="let alert of maintenanceAlerts">
|
||||||
|
<div class="alert-priority" [ngClass]="'priority-' + alert.priority.toLowerCase()"></div>
|
||||||
|
<div class="alert-content">
|
||||||
|
<div class="alert-header">
|
||||||
|
<h4>{{ alert.assetName }}</h4>
|
||||||
|
<mat-chip [ngClass]="'priority-' + alert.priority.toLowerCase()">{{ alert.priority }}</mat-chip>
|
||||||
|
</div>
|
||||||
|
<p class="alert-description">{{ alert.description }}</p>
|
||||||
|
<p class="alert-date">Due: {{ alert.dueDate }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
172
src/app/dashboard/dashboard.component.ts
Normal file
172
src/app/dashboard/dashboard.component.ts
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { MatCardModule } from '@angular/material/card';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatChipsModule } from '@angular/material/chips';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-dashboard',
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
MatCardModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatChipsModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatProgressBarModule
|
||||||
|
],
|
||||||
|
templateUrl: './dashboard.component.html',
|
||||||
|
styleUrl: './dashboard.component.css'
|
||||||
|
})
|
||||||
|
export class DashboardComponent {
|
||||||
|
|
||||||
|
// Updated stats structure
|
||||||
|
stats = {
|
||||||
|
totalAssets: 1247,
|
||||||
|
totalValue: 28475000,
|
||||||
|
activeUsers: 42,
|
||||||
|
pendingTasks: 8
|
||||||
|
};
|
||||||
|
|
||||||
|
// Updated recent activities structure
|
||||||
|
recentActivities = [
|
||||||
|
{
|
||||||
|
icon: 'add_circle',
|
||||||
|
title: 'New Asset Registered',
|
||||||
|
description: 'Dell Latitude 5520 Laptop added to IT Department',
|
||||||
|
time: '2 hours ago',
|
||||||
|
type: 'asset'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'build',
|
||||||
|
title: 'Maintenance Scheduled',
|
||||||
|
description: 'Industrial Drill Machine maintenance due next week',
|
||||||
|
time: '4 hours ago',
|
||||||
|
type: 'maintenance'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'warning',
|
||||||
|
title: 'Critical Alert',
|
||||||
|
description: 'Air Conditioning Unit requires immediate attention',
|
||||||
|
time: '6 hours ago',
|
||||||
|
type: 'alert'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'swap_horiz',
|
||||||
|
title: 'Asset Transfer',
|
||||||
|
description: 'HP Printer transferred from Admin to IT',
|
||||||
|
time: '1 day ago',
|
||||||
|
type: 'asset'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'delete_forever',
|
||||||
|
title: 'Asset Retired',
|
||||||
|
description: 'Old Desktop Computer marked as retired',
|
||||||
|
time: '2 days ago',
|
||||||
|
type: 'asset'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// Updated asset categories structure
|
||||||
|
assetCategories = [
|
||||||
|
{
|
||||||
|
name: 'Computers & IT',
|
||||||
|
count: 456,
|
||||||
|
percentage: 37,
|
||||||
|
color: 'blue-500'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Office Furniture',
|
||||||
|
count: 234,
|
||||||
|
percentage: 19,
|
||||||
|
color: 'green-500'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Machinery',
|
||||||
|
count: 189,
|
||||||
|
percentage: 15,
|
||||||
|
color: 'yellow-500'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Office Equipment',
|
||||||
|
count: 156,
|
||||||
|
percentage: 13,
|
||||||
|
color: 'purple-500'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Vehicles',
|
||||||
|
count: 89,
|
||||||
|
percentage: 7,
|
||||||
|
color: 'red-500'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Building Equipment',
|
||||||
|
count: 123,
|
||||||
|
percentage: 10,
|
||||||
|
color: 'indigo-500'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// Updated maintenance alerts structure
|
||||||
|
maintenanceAlerts = [
|
||||||
|
{
|
||||||
|
assetName: 'Industrial Drill Machine',
|
||||||
|
description: 'Routine maintenance and calibration required',
|
||||||
|
dueDate: '2024-01-20',
|
||||||
|
priority: 'Critical'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
assetName: 'Air Conditioning Unit',
|
||||||
|
description: 'Filter replacement and system check needed',
|
||||||
|
dueDate: '2024-01-22',
|
||||||
|
priority: 'High'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
assetName: 'HP LaserJet Printer',
|
||||||
|
description: 'Toner replacement and cleaning scheduled',
|
||||||
|
dueDate: '2024-01-25',
|
||||||
|
priority: 'Low'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
assetName: 'Company Vehicle - Toyota Camry',
|
||||||
|
description: 'Oil change and tire rotation due',
|
||||||
|
dueDate: '2024-01-28',
|
||||||
|
priority: 'High'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
assetName: 'Dell Latitude 5520 Laptop',
|
||||||
|
description: 'Software updates and security patches',
|
||||||
|
dueDate: '2024-01-30',
|
||||||
|
priority: 'Low'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
assetName: 'Office Desk Set',
|
||||||
|
description: 'General inspection and cleaning',
|
||||||
|
dueDate: '2024-02-01',
|
||||||
|
priority: 'Low'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
getStatusColor(status: string): string {
|
||||||
|
switch (status) {
|
||||||
|
case 'Critical':
|
||||||
|
return 'red';
|
||||||
|
case 'High':
|
||||||
|
return 'yellow-orange';
|
||||||
|
case 'Low':
|
||||||
|
return 'accent-green';
|
||||||
|
default:
|
||||||
|
return 'light-gray';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
formatCurrency(amount: number): string {
|
||||||
|
return new Intl.NumberFormat('en-US', {
|
||||||
|
style: 'currency',
|
||||||
|
currency: 'THB',
|
||||||
|
minimumFractionDigits: 0
|
||||||
|
}).format(amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
402
src/app/purchase-orders/purchase-orders.component.css
Normal file
402
src/app/purchase-orders/purchase-orders.component.css
Normal file
@ -0,0 +1,402 @@
|
|||||||
|
/* Purchase Orders Container */
|
||||||
|
.purchase-orders-container {
|
||||||
|
padding: 0.5rem;
|
||||||
|
background-color: var(--dark-gray);
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Page Header */
|
||||||
|
.page-header {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
padding: 0.75rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-icon {
|
||||||
|
color: var(--primary-red);
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-subtitle {
|
||||||
|
color: var(--light-gray);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Filter Card */
|
||||||
|
.filter-card {
|
||||||
|
background-color: var(--medium-gray);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 1rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||||
|
gap: 0.75rem;
|
||||||
|
align-items: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-field {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-icon {
|
||||||
|
color: var(--light-gray);
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-suffix-icon {
|
||||||
|
color: var(--light-gray);
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button {
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: none;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button.primary {
|
||||||
|
background-color: var(--primary-red);
|
||||||
|
color: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button.primary:hover {
|
||||||
|
background-color: #b00000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button.secondary {
|
||||||
|
background-color: transparent;
|
||||||
|
color: var(--white);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button.secondary:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button.accent {
|
||||||
|
background-color: var(--accent-green);
|
||||||
|
color: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button.accent:hover {
|
||||||
|
background-color: #00b359;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-icon {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Table Card */
|
||||||
|
.table-card {
|
||||||
|
background-color: var(--medium-gray);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-container {
|
||||||
|
overflow-x: auto;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.purchase-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-header {
|
||||||
|
background-color: rgba(0, 0, 0, 0.2);
|
||||||
|
color: var(--white);
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-header th {
|
||||||
|
padding: 0.75rem 0.5rem;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-cell {
|
||||||
|
padding: 0.75rem 0.5rem;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
|
.amount-cell {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--accent-green);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-header-row {
|
||||||
|
background-color: rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-row {
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-row:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Category and Status Chips */
|
||||||
|
.category-chip,
|
||||||
|
.status-chip {
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 500;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-chip.asset {
|
||||||
|
background-color: rgba(59, 130, 246, 0.2);
|
||||||
|
color: #3b82f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-chip.tools {
|
||||||
|
background-color: rgba(16, 185, 129, 0.2);
|
||||||
|
color: #10b981;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-chip.spare {
|
||||||
|
background-color: rgba(245, 158, 11, 0.2);
|
||||||
|
color: #f59e0b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-chip.expense {
|
||||||
|
background-color: rgba(239, 68, 68, 0.2);
|
||||||
|
color: #ef4444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-chip.consumable {
|
||||||
|
background-color: rgba(139, 92, 246, 0.2);
|
||||||
|
color: #8b5cf6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-chip.completed {
|
||||||
|
background-color: rgba(0, 204, 102, 0.2);
|
||||||
|
color: var(--accent-green);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-chip.pending {
|
||||||
|
background-color: rgba(255, 170, 0, 0.2);
|
||||||
|
color: var(--yellow-orange);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-chip.cancelled {
|
||||||
|
background-color: rgba(255, 68, 68, 0.2);
|
||||||
|
color: var(--red);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom Paginator */
|
||||||
|
.custom-paginator {
|
||||||
|
margin-top: 1rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.75rem 0;
|
||||||
|
color: var(--white);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Design */
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.filter-grid {
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-card {
|
||||||
|
padding: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-card {
|
||||||
|
padding: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-header th,
|
||||||
|
.table-cell {
|
||||||
|
padding: 0.5rem 0.375rem;
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.purchase-orders-container {
|
||||||
|
padding: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-title {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-actions {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.375rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button {
|
||||||
|
padding: 0.375rem 0.75rem;
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-container {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-header th,
|
||||||
|
.table-cell {
|
||||||
|
padding: 0.375rem 0.25rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-chip,
|
||||||
|
.status-chip {
|
||||||
|
font-size: 0.6875rem;
|
||||||
|
padding: 0.1875rem 0.375rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.filter-card {
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-card {
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button {
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-icon {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-header th,
|
||||||
|
.table-cell {
|
||||||
|
padding: 0.25rem 0.125rem;
|
||||||
|
font-size: 0.6875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-paginator {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animations */
|
||||||
|
.filter-card,
|
||||||
|
.table-card {
|
||||||
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-card:hover,
|
||||||
|
.table-card:hover {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-row {
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button {
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button:hover {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compact spacing optimizations */
|
||||||
|
.purchase-orders-container {
|
||||||
|
padding: 0.375rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-card {
|
||||||
|
padding: 0.75rem;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-card {
|
||||||
|
padding: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-header th,
|
||||||
|
.table-cell {
|
||||||
|
padding: 0.5rem 0.375rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reduce font sizes for better density */
|
||||||
|
.header-title {
|
||||||
|
font-size: 1.375rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-header th {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-cell {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-chip,
|
||||||
|
.status-chip {
|
||||||
|
font-size: 0.6875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
257
src/app/purchase-orders/purchase-orders.component.html
Normal file
257
src/app/purchase-orders/purchase-orders.component.html
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
<div class="purchase-orders-container">
|
||||||
|
<!-- Page Header -->
|
||||||
|
<div class="page-header">
|
||||||
|
<div class="header-content">
|
||||||
|
<div class="header-title">
|
||||||
|
<mat-icon class="header-icon" aria-hidden="false">receipt_long</mat-icon>
|
||||||
|
<h1>Purchase Orders</h1>
|
||||||
|
</div>
|
||||||
|
<p class="header-subtitle">Manage and track all purchase orders in the system</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Filter Card -->
|
||||||
|
<mat-card class="filter-card">
|
||||||
|
<mat-card-header>
|
||||||
|
<mat-card-title>
|
||||||
|
<mat-icon class="card-icon" aria-hidden="false">filter_list</mat-icon>
|
||||||
|
Filter Options
|
||||||
|
</mat-card-title>
|
||||||
|
<mat-card-subtitle>Refine your search criteria</mat-card-subtitle>
|
||||||
|
</mat-card-header>
|
||||||
|
<mat-card-content>
|
||||||
|
<div class="filter-grid">
|
||||||
|
<!-- Year Filter -->
|
||||||
|
<mat-form-field appearance="outline" class="filter-field">
|
||||||
|
<mat-label>
|
||||||
|
<mat-icon class="field-icon" aria-hidden="false">calendar_today</mat-icon>
|
||||||
|
Year
|
||||||
|
</mat-label>
|
||||||
|
<mat-select formControlName="year">
|
||||||
|
<mat-option value="">All Years</mat-option>
|
||||||
|
<mat-option value="2024">2024</mat-option>
|
||||||
|
<mat-option value="2023">2023</mat-option>
|
||||||
|
<mat-option value="2022">2022</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-icon matSuffix class="field-suffix-icon" aria-hidden="false">expand_more</mat-icon>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<!-- Month Filter -->
|
||||||
|
<mat-form-field appearance="outline" class="filter-field">
|
||||||
|
<mat-label>
|
||||||
|
<mat-icon class="field-icon" aria-hidden="false">date_range</mat-icon>
|
||||||
|
Month
|
||||||
|
</mat-label>
|
||||||
|
<mat-select formControlName="month">
|
||||||
|
<mat-option value="">All Months</mat-option>
|
||||||
|
<mat-option value="1">January</mat-option>
|
||||||
|
<mat-option value="2">February</mat-option>
|
||||||
|
<mat-option value="3">March</mat-option>
|
||||||
|
<mat-option value="4">April</mat-option>
|
||||||
|
<mat-option value="5">May</mat-option>
|
||||||
|
<mat-option value="6">June</mat-option>
|
||||||
|
<mat-option value="7">July</mat-option>
|
||||||
|
<mat-option value="8">August</mat-option>
|
||||||
|
<mat-option value="9">September</mat-option>
|
||||||
|
<mat-option value="10">October</mat-option>
|
||||||
|
<mat-option value="11">November</mat-option>
|
||||||
|
<mat-option value="12">December</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-icon matSuffix class="field-suffix-icon" aria-hidden="false">expand_more</mat-icon>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<!-- Category Filter -->
|
||||||
|
<mat-form-field appearance="outline" class="filter-field">
|
||||||
|
<mat-label>
|
||||||
|
<mat-icon class="field-icon" aria-hidden="false">category</mat-icon>
|
||||||
|
Category
|
||||||
|
</mat-label>
|
||||||
|
<mat-select formControlName="category">
|
||||||
|
<mat-option value="">All Categories</mat-option>
|
||||||
|
<mat-option value="Asset">Asset</mat-option>
|
||||||
|
<mat-option value="Tools">Tools</mat-option>
|
||||||
|
<mat-option value="Spare">Spare</mat-option>
|
||||||
|
<mat-option value="Expense">Expense</mat-option>
|
||||||
|
<mat-option value="Consumable">Consumable</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-icon matSuffix class="field-suffix-icon" aria-hidden="false">expand_more</mat-icon>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<!-- Search Field -->
|
||||||
|
<mat-form-field appearance="outline" class="filter-field">
|
||||||
|
<mat-label>
|
||||||
|
<mat-icon class="field-icon" aria-hidden="false">search</mat-icon>
|
||||||
|
Search
|
||||||
|
</mat-label>
|
||||||
|
<input matInput formControlName="search" placeholder="Search PO ID, Product...">
|
||||||
|
<mat-icon matSuffix class="field-suffix-icon" aria-hidden="false">search</mat-icon>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Action Buttons -->
|
||||||
|
<div class="filter-actions">
|
||||||
|
<button mat-raised-button
|
||||||
|
color="primary"
|
||||||
|
(click)="applyFilter()"
|
||||||
|
class="action-button primary">
|
||||||
|
<mat-icon class="button-icon" aria-hidden="false">filter_list</mat-icon>
|
||||||
|
Apply Filters
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button mat-stroked-button
|
||||||
|
(click)="clearFilters()"
|
||||||
|
class="action-button secondary">
|
||||||
|
<mat-icon class="button-icon" aria-hidden="false">clear</mat-icon>
|
||||||
|
Clear Filters
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button mat-raised-button
|
||||||
|
color="accent"
|
||||||
|
(click)="exportToExcel()"
|
||||||
|
class="action-button accent">
|
||||||
|
<mat-icon class="button-icon" aria-hidden="false">download</mat-icon>
|
||||||
|
Export Excel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
|
||||||
|
<!-- Table Card -->
|
||||||
|
<mat-card class="table-card">
|
||||||
|
<mat-card-header>
|
||||||
|
<mat-card-title>
|
||||||
|
<mat-icon class="card-icon" aria-hidden="false">table_chart</mat-icon>
|
||||||
|
Purchase Orders
|
||||||
|
</mat-card-title>
|
||||||
|
<mat-card-subtitle>Total: {{ dataSource.data.length }} records</mat-card-subtitle>
|
||||||
|
</mat-card-header>
|
||||||
|
<mat-card-content>
|
||||||
|
<div class="table-container">
|
||||||
|
<table mat-table [dataSource]="dataSource" matSort class="purchase-table">
|
||||||
|
<!-- Company Column -->
|
||||||
|
<ng-container matColumnDef="company">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header class="table-header">
|
||||||
|
<mat-icon class="header-icon" aria-hidden="false">business</mat-icon>
|
||||||
|
Company
|
||||||
|
</th>
|
||||||
|
<td mat-cell *matCellDef="let element" class="table-cell">
|
||||||
|
{{element.company}}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Receive Date Column -->
|
||||||
|
<ng-container matColumnDef="receiveDate">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header class="table-header">
|
||||||
|
<mat-icon class="header-icon" aria-hidden="false">event</mat-icon>
|
||||||
|
Receive Date
|
||||||
|
</th>
|
||||||
|
<td mat-cell *matCellDef="let element" class="table-cell">
|
||||||
|
{{element.receiveDate}}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- PR ID Column -->
|
||||||
|
<ng-container matColumnDef="prId">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header class="table-header">
|
||||||
|
<mat-icon class="header-icon" aria-hidden="false">receipt</mat-icon>
|
||||||
|
PR ID
|
||||||
|
</th>
|
||||||
|
<td mat-cell *matCellDef="let element" class="table-cell">
|
||||||
|
{{element.prId}}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- PO ID Column -->
|
||||||
|
<ng-container matColumnDef="poId">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header class="table-header">
|
||||||
|
<mat-icon class="header-icon" aria-hidden="false">shopping_cart</mat-icon>
|
||||||
|
PO ID
|
||||||
|
</th>
|
||||||
|
<td mat-cell *matCellDef="let element" class="table-cell">
|
||||||
|
{{element.poId}}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Product ID Column -->
|
||||||
|
<ng-container matColumnDef="productId">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header class="table-header">
|
||||||
|
<mat-icon class="header-icon" aria-hidden="false">inventory</mat-icon>
|
||||||
|
Product ID
|
||||||
|
</th>
|
||||||
|
<td mat-cell *matCellDef="let element" class="table-cell">
|
||||||
|
{{element.productId}}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Product Name Column -->
|
||||||
|
<ng-container matColumnDef="productName">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header class="table-header">
|
||||||
|
<mat-icon class="header-icon" aria-hidden="false">inventory_2</mat-icon>
|
||||||
|
Product Name
|
||||||
|
</th>
|
||||||
|
<td mat-cell *matCellDef="let element" class="table-cell">
|
||||||
|
{{element.productName}}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Amount Column -->
|
||||||
|
<ng-container matColumnDef="amount">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header class="table-header">
|
||||||
|
<mat-icon class="header-icon" aria-hidden="false">attach_money</mat-icon>
|
||||||
|
Amount
|
||||||
|
</th>
|
||||||
|
<td mat-cell *matCellDef="let element" class="table-cell amount-cell">
|
||||||
|
{{formatCurrency(element.amount)}}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Purchase By Column -->
|
||||||
|
<ng-container matColumnDef="purchaseBy">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header class="table-header">
|
||||||
|
<mat-icon class="header-icon" aria-hidden="false">person</mat-icon>
|
||||||
|
Purchase By
|
||||||
|
</th>
|
||||||
|
<td mat-cell *matCellDef="let element" class="table-cell">
|
||||||
|
{{element.purchaseBy}}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Category Column -->
|
||||||
|
<ng-container matColumnDef="category">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header class="table-header">
|
||||||
|
<mat-icon class="header-icon" aria-hidden="false">category</mat-icon>
|
||||||
|
Category
|
||||||
|
</th>
|
||||||
|
<td mat-cell *matCellDef="let element" class="table-cell">
|
||||||
|
<mat-chip [ngClass]="'category-' + getCategoryColor(element.category)" class="category-chip">
|
||||||
|
{{element.category}}
|
||||||
|
</mat-chip>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Status Column -->
|
||||||
|
<ng-container matColumnDef="status">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header class="table-header">
|
||||||
|
<mat-icon class="header-icon" aria-hidden="false">flag</mat-icon>
|
||||||
|
Status
|
||||||
|
</th>
|
||||||
|
<td mat-cell *matCellDef="let element" class="table-cell">
|
||||||
|
<mat-chip [ngClass]="'status-' + getStatusColor(element.status)" class="status-chip">
|
||||||
|
{{element.status}}
|
||||||
|
</mat-chip>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="displayedColumns" class="table-header-row"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: displayedColumns;" class="table-row"></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Paginator -->
|
||||||
|
<mat-paginator [pageSizeOptions]="[10, 25, 50, 100]"
|
||||||
|
showFirstLastButtons
|
||||||
|
class="custom-paginator">
|
||||||
|
</mat-paginator>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
</div>
|
||||||
247
src/app/purchase-orders/purchase-orders.component.ts
Normal file
247
src/app/purchase-orders/purchase-orders.component.ts
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { MatTableModule, MatTableDataSource } from '@angular/material/table';
|
||||||
|
import { MatPaginatorModule, MatPaginator } from '@angular/material/paginator';
|
||||||
|
import { MatSortModule, MatSort } from '@angular/material/sort';
|
||||||
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatCardModule } from '@angular/material/card';
|
||||||
|
import { MatChipsModule } from '@angular/material/chips';
|
||||||
|
import { ReactiveFormsModule, FormBuilder, FormGroup } from '@angular/forms';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-purchase-orders',
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
MatTableModule,
|
||||||
|
MatPaginatorModule,
|
||||||
|
MatSortModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatSelectModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatCardModule,
|
||||||
|
MatChipsModule,
|
||||||
|
ReactiveFormsModule
|
||||||
|
],
|
||||||
|
templateUrl: './purchase-orders.component.html',
|
||||||
|
styleUrl: './purchase-orders.component.css'
|
||||||
|
})
|
||||||
|
export class PurchaseOrdersComponent implements OnInit {
|
||||||
|
|
||||||
|
@ViewChild(MatPaginator) paginator!: MatPaginator;
|
||||||
|
@ViewChild(MatSort) sort!: MatSort;
|
||||||
|
|
||||||
|
displayedColumns: string[] = [
|
||||||
|
'company',
|
||||||
|
'receiveDate',
|
||||||
|
'prId',
|
||||||
|
'poId',
|
||||||
|
'productId',
|
||||||
|
'productName',
|
||||||
|
'amount',
|
||||||
|
'purchaseBy',
|
||||||
|
'category',
|
||||||
|
'status'
|
||||||
|
];
|
||||||
|
|
||||||
|
dataSource: MatTableDataSource<any>;
|
||||||
|
filterForm: FormGroup;
|
||||||
|
|
||||||
|
// Sample data
|
||||||
|
purchaseOrders = [
|
||||||
|
{
|
||||||
|
company: 'ABC Corporation',
|
||||||
|
receiveDate: '2024-01-15',
|
||||||
|
prId: 'PR-2024-001',
|
||||||
|
poId: 'PO-2024-001',
|
||||||
|
productId: 'PROD-001',
|
||||||
|
productName: 'Dell Latitude 5520 Laptop',
|
||||||
|
amount: 45000,
|
||||||
|
purchaseBy: 'John Smith',
|
||||||
|
category: 'Asset',
|
||||||
|
status: 'Completed'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
company: 'XYZ Industries',
|
||||||
|
receiveDate: '2024-01-14',
|
||||||
|
prId: 'PR-2024-002',
|
||||||
|
poId: 'PO-2024-002',
|
||||||
|
productId: 'PROD-002',
|
||||||
|
productName: 'Office Desk Set',
|
||||||
|
amount: 15000,
|
||||||
|
purchaseBy: 'Sarah Johnson',
|
||||||
|
category: 'Asset',
|
||||||
|
status: 'In Progress'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
company: 'Tech Solutions Ltd',
|
||||||
|
receiveDate: '2024-01-13',
|
||||||
|
prId: 'PR-2024-003',
|
||||||
|
poId: 'PO-2024-003',
|
||||||
|
productId: 'PROD-003',
|
||||||
|
productName: 'Industrial Drill Machine',
|
||||||
|
amount: 850000,
|
||||||
|
purchaseBy: 'Mike Wilson',
|
||||||
|
category: 'Tools',
|
||||||
|
status: 'Pending'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
company: 'Office Supplies Co',
|
||||||
|
receiveDate: '2024-01-12',
|
||||||
|
prId: 'PR-2024-004',
|
||||||
|
poId: 'PO-2024-004',
|
||||||
|
productId: 'PROD-004',
|
||||||
|
productName: 'HP LaserJet Printer',
|
||||||
|
amount: 25000,
|
||||||
|
purchaseBy: 'David Brown',
|
||||||
|
category: 'Asset',
|
||||||
|
status: 'Completed'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
company: 'Auto Parts Inc',
|
||||||
|
receiveDate: '2024-01-11',
|
||||||
|
prId: 'PR-2024-005',
|
||||||
|
poId: 'PO-2024-005',
|
||||||
|
productId: 'PROD-005',
|
||||||
|
productName: 'Company Vehicle - Toyota Camry',
|
||||||
|
amount: 850000,
|
||||||
|
purchaseBy: 'Lisa Chen',
|
||||||
|
category: 'Asset',
|
||||||
|
status: 'Completed'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
company: 'Building Systems',
|
||||||
|
receiveDate: '2024-01-10',
|
||||||
|
prId: 'PR-2024-006',
|
||||||
|
poId: 'PO-2024-006',
|
||||||
|
productId: 'PROD-006',
|
||||||
|
productName: 'Air Conditioning Unit',
|
||||||
|
amount: 120000,
|
||||||
|
purchaseBy: 'Robert Kim',
|
||||||
|
category: 'Asset',
|
||||||
|
status: 'In Progress'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
constructor(private fb: FormBuilder) {
|
||||||
|
this.dataSource = new MatTableDataSource(this.purchaseOrders);
|
||||||
|
this.filterForm = this.fb.group({
|
||||||
|
year: [''],
|
||||||
|
month: [''],
|
||||||
|
category: [''],
|
||||||
|
search: ['']
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.setupFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
this.dataSource.paginator = this.paginator;
|
||||||
|
this.dataSource.sort = this.sort;
|
||||||
|
}
|
||||||
|
|
||||||
|
setupFilter() {
|
||||||
|
this.filterForm.valueChanges.subscribe(values => {
|
||||||
|
this.applyFilter();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
applyFilter() {
|
||||||
|
const filterValue = this.filterForm.value;
|
||||||
|
let filteredData = [...this.purchaseOrders];
|
||||||
|
|
||||||
|
// Filter by search term
|
||||||
|
if (filterValue.search) {
|
||||||
|
const searchTerm = filterValue.search.toLowerCase();
|
||||||
|
filteredData = filteredData.filter(item =>
|
||||||
|
item.poId.toLowerCase().includes(searchTerm) ||
|
||||||
|
item.productName.toLowerCase().includes(searchTerm) ||
|
||||||
|
item.purchaseBy.toLowerCase().includes(searchTerm) ||
|
||||||
|
item.company.toLowerCase().includes(searchTerm)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter by year
|
||||||
|
if (filterValue.year) {
|
||||||
|
filteredData = filteredData.filter(item =>
|
||||||
|
new Date(item.receiveDate).getFullYear().toString() === filterValue.year
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter by month
|
||||||
|
if (filterValue.month) {
|
||||||
|
filteredData = filteredData.filter(item =>
|
||||||
|
(new Date(item.receiveDate).getMonth() + 1).toString() === filterValue.month
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter by category
|
||||||
|
if (filterValue.category) {
|
||||||
|
filteredData = filteredData.filter(item =>
|
||||||
|
item.category === filterValue.category
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dataSource.data = filteredData;
|
||||||
|
}
|
||||||
|
|
||||||
|
getStatusColor(status: string): string {
|
||||||
|
switch (status) {
|
||||||
|
case 'Completed':
|
||||||
|
return 'accent-green';
|
||||||
|
case 'In Progress':
|
||||||
|
return 'yellow-orange';
|
||||||
|
case 'Pending':
|
||||||
|
return 'yellow-orange';
|
||||||
|
default:
|
||||||
|
return 'light-gray';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getCategoryColor(category: string): string {
|
||||||
|
switch (category) {
|
||||||
|
case 'Asset':
|
||||||
|
return 'blue-500';
|
||||||
|
case 'Tools':
|
||||||
|
return 'green-500';
|
||||||
|
case 'Spare':
|
||||||
|
return 'yellow-500';
|
||||||
|
case 'Expense':
|
||||||
|
return 'red-500';
|
||||||
|
case 'Consumable':
|
||||||
|
return 'purple-500';
|
||||||
|
default:
|
||||||
|
return 'gray-500';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
formatCurrency(amount: number): string {
|
||||||
|
return new Intl.NumberFormat('en-US', {
|
||||||
|
style: 'currency',
|
||||||
|
currency: 'THB',
|
||||||
|
minimumFractionDigits: 0
|
||||||
|
}).format(amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
exportToExcel() {
|
||||||
|
// Implement Excel export functionality
|
||||||
|
console.log('Exporting to Excel...');
|
||||||
|
}
|
||||||
|
|
||||||
|
clearFilters() {
|
||||||
|
this.filterForm.patchValue({
|
||||||
|
year: '',
|
||||||
|
month: '',
|
||||||
|
category: '',
|
||||||
|
search: ''
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,7 +2,7 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>PineAssetWebapp</title>
|
<title>PINEPIM Asset Management</title>
|
||||||
<base href="/">
|
<base href="/">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||||
|
|||||||
310
src/styles.css
310
src/styles.css
@ -1 +1,309 @@
|
|||||||
/* You can add global styles to this file, and also import other style files */
|
/* Import Angular Material theme */
|
||||||
|
@import '@angular/material/prebuilt-themes/indigo-pink.css';
|
||||||
|
|
||||||
|
/* CSS Variables for Color Palette */
|
||||||
|
:root {
|
||||||
|
--primary-red: #c80000;
|
||||||
|
--dark-gray: #1a1a1a;
|
||||||
|
--medium-gray: #2e2e2e;
|
||||||
|
--white: #ffffff;
|
||||||
|
--light-gray: #b0b0b0;
|
||||||
|
--accent-green: #00cc66;
|
||||||
|
--yellow-orange: #ffaa00;
|
||||||
|
--red: #ff4444;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Global Styles */
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Roboto', 'Helvetica Neue', sans-serif;
|
||||||
|
background-color: var(--dark-gray);
|
||||||
|
color: var(--white);
|
||||||
|
line-height: 1.5;
|
||||||
|
font-size: 14px;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Material Design Overrides */
|
||||||
|
.mat-mdc-snack-bar-container {
|
||||||
|
background-color: var(--medium-gray) !important;
|
||||||
|
color: var(--white) !important;
|
||||||
|
border-radius: 8px !important;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-mdc-snack-bar-container .mdc-snackbar__surface {
|
||||||
|
background-color: var(--medium-gray) !important;
|
||||||
|
color: var(--white) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom Scrollbar */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: var(--dark-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--medium-gray);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: var(--light-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure mat-icon elements are visible */
|
||||||
|
mat-icon {
|
||||||
|
display: inline-block !important;
|
||||||
|
visibility: visible !important;
|
||||||
|
opacity: 1 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compact spacing for better UX */
|
||||||
|
html {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reduce default margins and paddings */
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 { font-size: 1.5rem; }
|
||||||
|
h2 { font-size: 1.375rem; }
|
||||||
|
h3 { font-size: 1.25rem; }
|
||||||
|
h4 { font-size: 1.125rem; }
|
||||||
|
h5 { font-size: 1rem; }
|
||||||
|
h6 { font-size: 0.875rem; }
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compact button styles */
|
||||||
|
button {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compact form styles */
|
||||||
|
input, select, textarea {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
background-color: rgba(255, 255, 255, 0.05);
|
||||||
|
color: var(--white);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus, select:focus, textarea:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--primary-red);
|
||||||
|
background-color: rgba(255, 255, 255, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compact card styles */
|
||||||
|
.card {
|
||||||
|
background-color: var(--medium-gray);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 1rem;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compact table styles */
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background-color: rgba(0, 0, 0, 0.2);
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compact list styles */
|
||||||
|
ul, ol {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
padding: 0.25rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compact spacing utilities */
|
||||||
|
.m-0 { margin: 0 !important; }
|
||||||
|
.m-1 { margin: 0.25rem !important; }
|
||||||
|
.m-2 { margin: 0.5rem !important; }
|
||||||
|
.m-3 { margin: 0.75rem !important; }
|
||||||
|
.m-4 { margin: 1rem !important; }
|
||||||
|
|
||||||
|
.p-0 { padding: 0 !important; }
|
||||||
|
.p-1 { padding: 0.25rem !important; }
|
||||||
|
.p-2 { padding: 0.5rem !important; }
|
||||||
|
.p-3 { padding: 0.75rem !important; }
|
||||||
|
.p-4 { padding: 1rem !important; }
|
||||||
|
|
||||||
|
/* Compact grid system */
|
||||||
|
.grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-2 { grid-template-columns: repeat(2, 1fr); }
|
||||||
|
.grid-3 { grid-template-columns: repeat(3, 1fr); }
|
||||||
|
.grid-4 { grid-template-columns: repeat(4, 1fr); }
|
||||||
|
|
||||||
|
/* Compact flex utilities */
|
||||||
|
.flex {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-col {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.items-center {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.justify-between {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gap-1 { gap: 0.25rem; }
|
||||||
|
.gap-2 { gap: 0.5rem; }
|
||||||
|
.gap-3 { gap: 0.75rem; }
|
||||||
|
.gap-4 { gap: 1rem; }
|
||||||
|
|
||||||
|
/* Responsive utilities */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
html {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-2,
|
||||||
|
.grid-3,
|
||||||
|
.grid-4 {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
padding: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
padding: 0.375rem 0.5rem;
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
html {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
padding: 0.25rem 0.375rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 0.375rem 0.75rem;
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
input, select, textarea {
|
||||||
|
padding: 0.375rem 0.5rem;
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compact animations */
|
||||||
|
.fade-in {
|
||||||
|
animation: fadeIn 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-up {
|
||||||
|
animation: slideUp 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from { opacity: 0; }
|
||||||
|
to { opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideUp {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(10px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compact focus styles */
|
||||||
|
*:focus {
|
||||||
|
outline: 2px solid var(--primary-red);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compact selection styles */
|
||||||
|
::selection {
|
||||||
|
background-color: var(--primary-red);
|
||||||
|
color: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compact print styles */
|
||||||
|
@media print {
|
||||||
|
* {
|
||||||
|
background: white !important;
|
||||||
|
color: black !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-print {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user