Commit 26c17e1d authored by haque's avatar haque

Initial commit

parents
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.ts]
quote_type = single
[*.md]
max_line_length = off
trim_trailing_whitespace = false
# See http://help.github.com/ignore-files/ for more about ignoring files.
# Compiled output
/dist
/tmp
/out-tsc
/bazel-out
# Node
/node_modules
npm-debug.log
yarn-error.log
# IDEs and editors
.idea/
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# Visual Studio Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# Miscellaneous
/.angular/cache
.sass-cache/
/connect.lock
/coverage
/libpeerconnection.log
testem.log
/typings
# System files
.DS_Store
Thumbs.db
# DataHandler
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17.0.3.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"dataHandler": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"standalone": false
},
"@schematics/angular:directive": {
"standalone": false
},
"@schematics/angular:pipe": {
"standalone": false
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"outputPath": "dist/data-handler",
"index": "src/index.html",
"browser": "src/main.ts",
"polyfills": [
"zone.js"
],
"tsConfig": "tsconfig.app.json",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"node_modules/primeng/resources/themes/lara-light-blue/theme.css",
"node_modules/primeng/resources/primeng.min.css",
"src/styles.css"
],
"scripts": []
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "dataHandler:build:production"
},
"development": {
"buildTarget": "dataHandler:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"buildTarget": "dataHandler:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"polyfills": [
"zone.js",
"zone.js/testing"
],
"tsConfig": "tsconfig.spec.json",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
}
}
}
}
},
"cli": {
"analytics": "e6092410-b030-4e98-ae0c-80695f75e73e"
}
}
This diff is collapsed.
{
"name": "data-handler",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test"
},
"private": true,
"dependencies": {
"@angular/animations": "^17.0.0",
"@angular/common": "^17.0.0",
"@angular/compiler": "^17.0.0",
"@angular/core": "^17.0.0",
"@angular/forms": "^17.0.0",
"@angular/platform-browser": "^17.0.0",
"@angular/platform-browser-dynamic": "^17.0.0",
"@angular/router": "^17.0.0",
"@rxweb/reactive-form-validators": "^13.0.1",
"chart.js": "^4.4.0",
"html2canvas": "^1.4.1",
"html2pdf.js": "^0.10.1",
"jspdf": "^2.5.1",
"ng2-charts": "^5.0.3",
"primeng": "^17.0.0-beta.1",
"rxjs": "~7.8.0",
"tinycolor2": "^1.6.0",
"tslib": "^2.3.0",
"zone.js": "~0.14.2"
},
"devDependencies": {
"@angular-devkit/build-angular": "^17.0.3",
"@angular/cli": "^17.0.3",
"@angular/compiler-cli": "^17.0.0",
"@types/jasmine": "~5.1.0",
"@types/tinycolor2": "^1.4.6",
"jasmine-core": "~5.1.0",
"karma": "~6.4.0",
"karma-chrome-launcher": "~3.2.0",
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.1.0",
"typescript": "~5.2.2"
}
}
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import {OccupationDataComponent} from "./components/occupation-data/occupation-data.component";
import {DobDataComponent} from "./components/dob-data/dob-data.component";
const routes: Routes = [
{ path: 'occupational-data', component: OccupationDataComponent },
{ path: 'dob-data', component: DobDataComponent },
{ path: '', redirectTo: '/occupational-data', pathMatch: 'full' }, // Default route
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
<div class="card">
<p-tabMenu [model]="items" [activeItem]="activeItem"></p-tabMenu>
</div>
<br/>
<router-outlet></router-outlet>
import { TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
RouterTestingModule
],
declarations: [
AppComponent
],
}).compileComponents();
});
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
it(`should have as title 'dataHandler'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('dataHandler');
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('h1')?.textContent).toContain('Hello, dataHandler');
});
});
import { Component, OnInit } from '@angular/core';
import {MenuItem} from "primeng/api";
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit{
title:string = 'DataPresentor';
items: MenuItem[] | undefined;
activeItem: MenuItem | undefined;
ngOnInit() {
this.items = [
{ label: 'Occupation', routerLink: ['occupational-data'] },
{ label: 'Age', routerLink: ['dob-data'] }
];
this.activeItem = this.items[0];
}
}
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule} from '@angular/platform-browser/animations'
import { RouterModule } from '@angular/router';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { RxReactiveFormsModule} from '@rxweb/reactive-form-validators';
import { AppComponent } from './app.component';
import { OccupationDataComponent } from './components/occupation-data/occupation-data.component';
import { DobDataComponent } from './components/dob-data/dob-data.component';
import {AppRoutingModule} from "./app-routing.module";
import { HttpClientModule } from '@angular/common/http';
import {TabMenuModule} from "primeng/tabmenu";
import { ChartModule } from 'primeng/chart';
import {MenuModule} from "primeng/menu";
import { PanelModule } from 'primeng/panel';
import { ButtonModule } from 'primeng/button';
import { InputTextModule } from 'primeng/inputtext';
@NgModule({
declarations: [
AppComponent,
OccupationDataComponent,
DobDataComponent
],
imports: [
BrowserAnimationsModule,
BrowserModule,
RouterModule,
FormsModule,
ReactiveFormsModule,
RxReactiveFormsModule,
AppRoutingModule,
ChartModule,
TabMenuModule,
MenuModule,
PanelModule,
ButtonModule,
InputTextModule,
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
/* search-form.component.css */
form {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.search-field {
display: flex;
align-items: center;
margin-bottom: 10px;
}
.search-field label {
margin-right: 10px;
}
<div class="card">
<div class="grid m-0">
<div class="col-12">
<p-panel header="Search" [toggleable]="true" toggler="header" [transitionOptions]="'500ms ease'" [collapsed]="false">
<form [formGroup]="searchForm" (ngSubmit)="onSearch()">
<div class="search-container">
<div class="search-field">
<label for="upazilla">Upazilla:</label>
<input id="upazilla" formControlName="upazilla" type="text" pInputText />
</div>
<div class="search-field">
<label for="union">Union:</label>
<input id="union" formControlName="union" type="text" pInputText />
</div>
<div class="search-field">
<label for="area">Area:</label>
<input id="area" formControlName="area" type="text" pInputText />
</div>
<div class="search-field">
<p-button label="Search" [disabled]="!hasAnyValue" icon="pi pi-check-circle" styleClass="site-button p-button-sm" type="submit">
</p-button>&nbsp;&nbsp;&nbsp;
<p-button label="Reset" [disabled]="!hasAnyValue" icon="pi pi-times" styleClass="site-button p-button-sm" type="reset" (onClick)="resetSearchForm()">
</p-button>
</div>
</div>
</form>
</p-panel>
</div>
</div>
<br/>
<div class="grid m-0">
<div class="col-12">
<p-panel header="Age wise data" [toggleable]="false" toggler="header" [transitionOptions]="'500ms ease'" [collapsed]="false">
<button (click)="downloadAsPDF()" pButton pRipple label="Download as PDF"></button>
<div #pdfContent>
<p-chart type="bar" [data]="data" [options]="options"></p-chart>
</div>
</p-panel>
</div>
</div>
</div>
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DobDataComponent } from './dob-data.component';
describe('DobDataComponent', () => {
let component: DobDataComponent;
let fixture: ComponentFixture<DobDataComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ DobDataComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(DobDataComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import {Component, ElementRef, OnInit, ViewChild} from '@angular/core';
import {Person} from "../../model/person";
import {DataLoaderService} from "../../services/data-loader.service";
import tinycolor from 'tinycolor2';
import {Utilities} from "../../utilities/utilities";
import {FormGroup} from "@angular/forms";
import {SearchModel} from "../../model/search-model";
import {RxFormBuilder} from "@rxweb/reactive-form-validators";
@Component({
selector: 'app-dob-data',
templateUrl: './dob-data.component.html',
styleUrls: ['./dob-data.component.css']
})
export class DobDataComponent implements OnInit {
data: any;
options: any;
people: Person[] = [];
ageGroupCountMap: Map<string, number> = new Map();
ageGroups: string[] = ['Below 18', '18-20', '21-25', '26-30', '31-40', '41-50', '51-60', '61-80', 'Above 80'];
searchForm!: FormGroup;
searchModel: SearchModel = new SearchModel();
@ViewChild('pdfContent') pdfContent!: ElementRef;
constructor(private dataLoader: DataLoaderService, private formBuilder:RxFormBuilder) {}
ngOnInit() {
this.ageGroups.forEach(ageLabel => {
this.ageGroupCountMap.set(ageLabel, 0);
});
this.searchForm = this.formBuilder.formGroup(this.searchModel);
this.loadData(null);
}
downloadAsPDF(){
Utilities.downloadAsPDF(this.pdfContent, 'AgeWise');
}
onSearch(){
if(this.searchForm.valid){
let searchData = <SearchModel> this.searchForm.value;
console.log(searchData);
this.loadData(searchData);
}
}
get hasAnyValue(){
return Object.keys(this.searchForm.value).some(k => !!this.searchForm.value[k]);
}
resetSearchForm(){
this.loadData(null);
}
loadData(searchObject: SearchModel | null): void {
this.dataLoader.getData().subscribe((data)=>{
this.people = data;
this.countPersonsByAgeGroup(searchObject);
});
}
countPersonsByAgeGroup(searchObject: SearchModel | null): void {
this.people.forEach(person => {
const age = this.calculateAge(person.date_of_birth);
if (age !== null) {
const ageGroup = this.assignToAgeGroup(age);
let currentCount = this.ageGroupCountMap.get(ageGroup) ?? 0;
this.ageGroupCountMap.set(ageGroup, currentCount + 1);
}
});
// console.log('Occupation Count Map:', this.ageGroupCountMap);
this.generateGraph(searchObject);
}
assignToAgeGroup(age: number): string {
if (age < 18) {
return this.ageGroups[0];
} else if (age <= 20) {
return this.ageGroups[1];
} else if (age <= 25) {
return this.ageGroups[2];
} else if (age <= 30) {
return this.ageGroups[3];
} else if (age <= 40) {
return this.ageGroups[4];
} else if (age <= 50) {
return this.ageGroups[5];
} else if (age <= 60) {
return this.ageGroups[6];
} else if (age <= 80) {
return this.ageGroups[7];
} else {
return this.ageGroups[8];
}
}
calculateAge(dateOfBirth: string): number | null {
const birthDate = new Date(dateOfBirth);
const currentDate = new Date();
if (isNaN(birthDate.getTime())) {
// Invalid date
return null;
}
const age = currentDate.getFullYear() - birthDate.getFullYear();
// Adjust age if birthday hasn't occurred yet this year
if (
currentDate.getMonth() < birthDate.getMonth() ||
(currentDate.getMonth() === birthDate.getMonth() && currentDate.getDate() < birthDate.getDate())
) {
return age - 1;
}
return age;
}
generateGraph(searchObject: SearchModel | null): void {
const keysArray: string[] = Array.from(this.ageGroupCountMap.keys()).reverse();
const valuesArray: number[] = Array.from(this.ageGroupCountMap.values()).reverse();
var label: string = '';
if(searchObject!=null){
if(searchObject.upazilla!=null && searchObject.upazilla!=''){
label = 'Upazilla:' + searchObject.upazilla;
}
if(searchObject.union!=null && searchObject.union!=''){
if(label!=''){
label = label + ', Union:' + searchObject.union;
}
else{
label = 'Union:' + searchObject.union;
}
}
if(searchObject.area!=null && searchObject.area!=''){
if(label!=''){
label = label + ', Area:' + searchObject.area;
}
else{
label = 'Area:' + searchObject.area;
}
}
}
if(label==''){
label = 'Complete data';
}
// Generate shades of blue based on the values in dynamicData
const backgroundColors = Utilities.generateShades(valuesArray, '#007BFF');
const documentStyle = getComputedStyle(document.documentElement);
const textColor = documentStyle.getPropertyValue('--text-color');
const textColorSecondary = documentStyle.getPropertyValue('--text-color-secondary');
const surfaceBorder = documentStyle.getPropertyValue('--surface-border');
this.data = {
labels: keysArray,
datasets: [
{
label: label,
backgroundColor: backgroundColors,
data: valuesArray
}
]
};
this.options = {
indexAxis: 'y',
maintainAspectRatio: false,
aspectRatio: 0.8,
plugins: {
legend: {
labels: {
color: textColor
}
}
},
scales: {
x: {
ticks: {
color: textColorSecondary,
precision: 0,
font: {
weight: 500
}
},
grid: {
color: surfaceBorder,
drawBorder: false
}
},
y: {
ticks: {
color: textColorSecondary
},
grid: {
color: surfaceBorder,
drawBorder: false
}
}
}
};
}
}
/* search-form.component.css */
form {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.search-field {
display: flex;
align-items: center;
margin-bottom: 10px;
}
.search-field label {
margin-right: 10px;
}
<div class="card">
<div class="grid m-0">
<div class="col-12">
<p-panel header="Search" [toggleable]="true" toggler="header" [transitionOptions]="'500ms ease'" [collapsed]="false">
<form [formGroup]="searchForm" (ngSubmit)="onSearch()">
<div class="search-container">
<div class="search-field">
<label for="upazilla">Upazilla:</label>
<input id="upazilla" formControlName="upazilla" type="text" pInputText />
</div>
<div class="search-field">
<label for="union">Union:</label>
<input id="union" formControlName="union" type="text" pInputText />
</div>
<div class="search-field">
<label for="area">Area:</label>
<input id="area" formControlName="area" type="text" pInputText />
</div>
<div class="search-field">
<p-button label="Search" [disabled]="!hasAnyValue" icon="pi pi-check-circle" styleClass="site-button p-button-sm" type="submit">
</p-button>&nbsp;&nbsp;&nbsp;
<p-button label="Reset" [disabled]="!hasAnyValue" icon="pi pi-times" styleClass="site-button p-button-sm" type="reset" (onClick)="resetSearchForm()">
</p-button>
</div>
</div>
</form>
</p-panel>
</div>
</div>
<br/>
<div class="grid m-0">
<div class="col-12">
<p-panel header="Occupation wise data" [toggleable]="false" toggler="header" [transitionOptions]="'500ms ease'" [collapsed]="false">
<button (click)="downloadAsPDF()" pButton pRipple label="Download as PDF"></button>
<div #pdfContent>
<p-chart type="bar" [data]="data" [options]="options"></p-chart>
</div>
</p-panel>
</div>
</div>
</div>
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { OccupationDataComponent } from './occupation-data.component';
describe('OccupationDataComponent', () => {
let component: OccupationDataComponent;
let fixture: ComponentFixture<OccupationDataComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ OccupationDataComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(OccupationDataComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, ElementRef, ViewChild, OnInit } from '@angular/core';
import {DataLoaderService} from "../../services/data-loader.service";
import {Person} from "../../model/person";
import {SearchModel} from "../../model/search-model";
import { FormGroup } from '@angular/forms';
import { RxFormBuilder } from '@rxweb/reactive-form-validators';
import {Utilities} from "../../utilities/utilities";
@Component({
selector: 'app-occupation-data',
templateUrl: './occupation-data.component.html',
styleUrls: ['./occupation-data.component.css']
})
export class OccupationDataComponent implements OnInit {
data: any;
options: any;
people: Person[] = [];
occupationCountMap: Map<string, number> = new Map();
searchForm!: FormGroup;
searchModel: SearchModel = new SearchModel();
@ViewChild('pdfContent') pdfContent!: ElementRef;
constructor(private dataLoader: DataLoaderService, private formBuilder:RxFormBuilder) {}
ngOnInit() {
this.searchForm = this.formBuilder.formGroup(this.searchModel);
this.loadData(null);
}
downloadAsPDF(){
Utilities.downloadAsPDF(this.pdfContent, 'OccupationWise');
}
onSearch(){
if(this.searchForm.valid){
let searchData = <SearchModel> this.searchForm.value;
console.log(searchData);
this.loadData(searchData);
}
}
get hasAnyValue(){
return Object.keys(this.searchForm.value).some(k => !!this.searchForm.value[k]);
}
resetSearchForm(){
this.loadData(null);
}
loadData(searchObject: SearchModel | null): void {
this.dataLoader.getData().subscribe((data)=>{
this.people = data;
this.calculateOccupationCount(searchObject);
});
}
calculateOccupationCount(searchObject: SearchModel | null): void {
this.occupationCountMap.clear();
if (this.people) {
this.people.forEach(person => {
const occupation = person.occupation;
let currentCount = this.occupationCountMap.get(occupation) ?? 0;
this.occupationCountMap.set(occupation, currentCount + 1);
});
}
// console.log('Occupation Count Map:', this.occupationCountMap);
this.generateGraph(searchObject);
}
generateGraph(searchObject: SearchModel | null): void{
const keysArray: string[] = Array.from(this.occupationCountMap.keys());
const valuesArray: number[] = Array.from(this.occupationCountMap.values());
var label: string = '';
if(searchObject!=null){
if(searchObject.upazilla!=null && searchObject.upazilla!=''){
label = 'Upazilla:' + searchObject.upazilla;
}
if(searchObject.union!=null && searchObject.union!=''){
if(label!=''){
label = label + ', Union:' + searchObject.union;
}
else{
label = 'Union:' + searchObject.union;
}
}
if(searchObject.area!=null && searchObject.area!=''){
if(label!=''){
label = label + ', Area:' + searchObject.area;
}
else{
label = 'Area:' + searchObject.area;
}
}
}
if(label==''){
label = 'Complete data';
}
const documentStyle = getComputedStyle(document.documentElement);
const textColor = documentStyle.getPropertyValue('--text-color');
const textColorSecondary = documentStyle.getPropertyValue('--text-color-secondary');
const surfaceBorder = documentStyle.getPropertyValue('--surface-border');
this.data = {
labels: keysArray,
datasets: [
{
label: label,
backgroundColor: documentStyle.getPropertyValue('--blue-500'),
borderColor: documentStyle.getPropertyValue('--blue-500'),
data: valuesArray
}
]
};
this.options = {
indexAxis: 'y',
maintainAspectRatio: false,
aspectRatio: 0.8,
plugins: {
legend: {
labels: {
color: textColor
}
}
},
scales: {
x: {
ticks: {
color: textColorSecondary,
precision: 0,
font: {
weight: 500
}
},
grid: {
color: surfaceBorder,
drawBorder: false
}
},
y: {
ticks: {
color: textColorSecondary
},
grid: {
color: surfaceBorder,
drawBorder: false
}
}
}
};
}
}
export interface Person {
name: string;
voter_no: number;
fathers_name: string;
mothers_name: string;
occupation: string;
date_of_birth: string;
address: string;
}
import {prop} from "@rxweb/reactive-form-validators";
export class SearchModel {
@prop()
upazilla!: string;
@prop()
union!: string;
@prop()
area!: string;
}
import { TestBed } from '@angular/core/testing';
import { DataLoaderService } from './data-loader.service';
describe('DataLoaderService', () => {
let service: DataLoaderService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(DataLoaderService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
import { Injectable } from '@angular/core';
import {HttpClient} from "@angular/common/http";
import {Person} from "../model/person";
import { Observable } from "rxjs";
@Injectable({
providedIn: 'root'
})
export class DataLoaderService {
constructor(private httpClient: HttpClient){ }
getData(): Observable<Person[]>{
let url = `assets/data.json`;
return this.httpClient.get<Person[]>(url);
}
}
import { Utilities } from './utilities';
describe('Utilities', () => {
it('should create an instance', () => {
expect(new Utilities()).toBeTruthy();
});
});
import tinycolor from "tinycolor2";
import html2canvas from "html2canvas";
import jsPDF from "jspdf";
import {ElementRef} from "@angular/core";
export abstract class Utilities {
// Function to generate shades of a color based on values
public static generateShades(data: number[], colorCode: string): string[] {
const maxValue = Math.max(...data);
const minValue = Math.min(...data);
return data.map(value => {
// Calculate the percentage between the min and max values
const percentage = (value - minValue) / (maxValue - minValue);
// Generate a shade based on the percentage
const shade = tinycolor(colorCode).darken(percentage * 20).toHexString();
return shade;
});
}
public static downloadAsPDF(pdfContent: ElementRef, pdfName: string) {
const content = pdfContent.nativeElement;
// Use html2canvas to capture the content as an image with higher DPI
html2canvas(content, { scale: 2 }).then(canvas => {
const pdf = new jsPDF({
orientation: 'landscape', // Set PDF to landscape
unit: 'mm',
format: 'a4',
});
const imgData = canvas.toDataURL('image/png', 1.0); // Use 1.0 quality
// Calculate aspect ratio
const aspectRatio = canvas.width / canvas.height;
// Calculate new dimensions to fit within PDF
const pdfWidth = pdf.internal.pageSize.width;
const pdfHeight = pdfWidth / aspectRatio;
// Set padding
const paddingX = 10;
const paddingY = 10;
// Center the image on the PDF with padding
const xOffset = paddingX;
const yOffset = (pdf.internal.pageSize.height - pdfHeight) / 2 + paddingY;
// Add the image to the PDF
pdf.addImage(imgData, 'PNG', xOffset, yOffset, pdfWidth - 2 * paddingX, pdfHeight - 2 * paddingY);
pdf.save(pdfName + '.pdf');
});
}
}
This diff is collapsed.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>DataHandler</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": []
},
"files": [
"src/main.ts"
],
"include": [
"src/**/*.d.ts"
]
}
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"compileOnSave": false,
"compilerOptions": {
"outDir": "./dist/out-tsc",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"esModuleInterop": true,
"sourceMap": true,
"declaration": false,
"experimentalDecorators": true,
"moduleResolution": "node",
"importHelpers": true,
"target": "ES2022",
"module": "ES2022",
"useDefineForClassFields": false,
"lib": [
"ES2022",
"dom"
]
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true
}
}
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": [
"jasmine"
]
},
"include": [
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment