Added ability to generate and copy identity card number.
This commit was merged in pull request #3.
This commit is contained in:
@@ -11,6 +11,7 @@ import { PeselComponent } from './components/pesel/pesel.component';
|
|||||||
import { NipComponent } from './components/nip/nip.component';
|
import { NipComponent } from './components/nip/nip.component';
|
||||||
import { RegonComponent } from './components/regon/regon.component';
|
import { RegonComponent } from './components/regon/regon.component';
|
||||||
import { IbanComponent } from './components/iban/iban.component';
|
import { IbanComponent } from './components/iban/iban.component';
|
||||||
|
import { IdentityCardComponent } from './components/identitycard/identitycard.component';
|
||||||
import {NgOptimizedImage} from "@angular/common";
|
import {NgOptimizedImage} from "@angular/common";
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@@ -20,7 +21,8 @@ import {NgOptimizedImage} from "@angular/common";
|
|||||||
PeselComponent,
|
PeselComponent,
|
||||||
NipComponent,
|
NipComponent,
|
||||||
RegonComponent,
|
RegonComponent,
|
||||||
IbanComponent
|
IbanComponent,
|
||||||
|
IdentityCardComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
|
|||||||
@@ -3,5 +3,6 @@
|
|||||||
<app-regon></app-regon>
|
<app-regon></app-regon>
|
||||||
<app-nip></app-nip>
|
<app-nip></app-nip>
|
||||||
<app-pesel></app-pesel>
|
<app-pesel></app-pesel>
|
||||||
|
<app-identity-card></app-identity-card>
|
||||||
<app-iban></app-iban>
|
<app-iban></app-iban>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
17
src/app/components/identitycard/identitycard.component.html
Normal file
17
src/app/components/identitycard/identitycard.component.html
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<div class="generator">
|
||||||
|
<div class="title">
|
||||||
|
<span>DOWÓD OSOBISTY</span>
|
||||||
|
</div>
|
||||||
|
<div class="container">
|
||||||
|
<input type="text" [formControl]="valueField" />
|
||||||
|
|
||||||
|
<div class="buttons">
|
||||||
|
<img id="identityCardGenerateButton" ngSrc="assets/autorenew_black_24dp.svg" title="Generuj numer dowodu" alt="Generuj numer dowodu" style="margin-left: 1em;" (click)="generate()"
|
||||||
|
height="24" width="24"/>
|
||||||
|
<img id="identityCardCopyButton" ngSrc="assets/content_copy_black_24dp.svg" title="Skopiuj numer dowodu do schowka" alt="Skopiuj numer dowodu do schowka" style="margin: auto 1em;" (click)="copyToClipboard()"
|
||||||
|
height="24" width="24"/>
|
||||||
|
<img id="identityCardHelpButton" ngSrc="assets/info_black_24dp.svg" title="Szczegóły dotyczące numeru dowodu" alt="Definicja numeru dowodu na Wikipedii" (click)="navigateToDocs()"
|
||||||
|
height="24" width="24"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
35
src/app/components/identitycard/identitycard.component.ts
Normal file
35
src/app/components/identitycard/identitycard.component.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { AbstractControl, FormControl } from '@angular/forms';
|
||||||
|
import { IdentityCardService } from 'src/app/service/identity-card.service';
|
||||||
|
import { ClipboardService } from 'src/app/service/gui/clipboard.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-identity-card',
|
||||||
|
templateUrl: './identitycard.component.html',
|
||||||
|
styleUrls: ['./identitycard.component.scss'],
|
||||||
|
standalone: false
|
||||||
|
})
|
||||||
|
export class IdentityCardComponent implements OnInit {
|
||||||
|
public valueField: FormControl;
|
||||||
|
|
||||||
|
constructor(private identityCardService: IdentityCardService, private clipboardService: ClipboardService) {
|
||||||
|
this.valueField = new FormControl('');
|
||||||
|
this.valueField.setValidators([(control: AbstractControl) => this.identityCardService.validateIdentityCard(control.value)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
generate(): void {
|
||||||
|
this.valueField.setValue(this.identityCardService.generateIdentityCard());
|
||||||
|
}
|
||||||
|
|
||||||
|
copyToClipboard(): void {
|
||||||
|
this.clipboardService.copyToClipboard(this.valueField.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
navigateToDocs(): void {
|
||||||
|
window.open('https://pl.wikipedia.org/wiki/Dow%C3%B3d_osobisty_w_Polsce#Numer_dowodu_osobistego', "_blank");
|
||||||
|
}
|
||||||
|
}
|
||||||
85
src/app/service/identity-card.service.ts
Normal file
85
src/app/service/identity-card.service.ts
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { ValidationErrors } from '@angular/forms';
|
||||||
|
import { CommonService } from './common.service';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class IdentityCardService {
|
||||||
|
private static readonly weights: number[] = [7, 3, 1, 0, 7, 3, 1, 7, 3];
|
||||||
|
private static readonly letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
|
|
||||||
|
constructor(private common: CommonService) { }
|
||||||
|
|
||||||
|
public generateIdentityCard(): string {
|
||||||
|
let result = "";
|
||||||
|
// Generate 3 letters
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
result += IdentityCardService.letters.charAt(this.common.getRandomInt(0, 25));
|
||||||
|
}
|
||||||
|
// Generate 5 digits (for positions 4-8)
|
||||||
|
let digits = "";
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
digits += this.common.getRandomInt(0, 9).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate sum
|
||||||
|
let sum = 0;
|
||||||
|
// Letters
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
sum += this.letterToValue(result.charAt(i)) * IdentityCardService.weights[i];
|
||||||
|
}
|
||||||
|
// Digits
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
sum += parseInt(digits.charAt(i), 10) * IdentityCardService.weights[i + 4];
|
||||||
|
}
|
||||||
|
|
||||||
|
const controlDigit = sum % 10;
|
||||||
|
|
||||||
|
return result + controlDigit + digits;
|
||||||
|
}
|
||||||
|
|
||||||
|
public validateIdentityCard(id: string): ValidationErrors | null {
|
||||||
|
const errors: ValidationErrors = {};
|
||||||
|
if (!id) {
|
||||||
|
errors.required = true;
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
const upperId = id.toUpperCase();
|
||||||
|
|
||||||
|
if (upperId.length !== 9) {
|
||||||
|
if (upperId.length < 9) errors.tooShort = true;
|
||||||
|
else errors.tooLong = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pattern = /^[A-Z]{3}[0-9]{6}$/;
|
||||||
|
if (!pattern.test(upperId)) {
|
||||||
|
errors.invalidPattern = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(errors).length > 0) return errors;
|
||||||
|
|
||||||
|
// Control digit validation
|
||||||
|
let sum = 0;
|
||||||
|
for (let i = 0; i < 9; i++) {
|
||||||
|
if (i === 3) continue; // skip control digit
|
||||||
|
const char = upperId.charAt(i);
|
||||||
|
const val = i < 3 ? this.letterToValue(char) : parseInt(char, 10);
|
||||||
|
sum += val * IdentityCardService.weights[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
const calculatedControl = sum % 10;
|
||||||
|
const actualControl = parseInt(upperId.charAt(3), 10);
|
||||||
|
|
||||||
|
if (calculatedControl !== actualControl) {
|
||||||
|
errors.invalidControlDigit = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.keys(errors).length > 0 ? errors : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private letterToValue(letter: string): number {
|
||||||
|
return IdentityCardService.letters.indexOf(letter.toUpperCase()) + 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user