From acecaef024cad7eb6a3cd17aa2cfbc2cb2a05bf1 Mon Sep 17 00:00:00 2001 From: mastah Date: Tue, 27 Jan 2026 23:22:50 +0100 Subject: [PATCH] Added ability to generate and copy identity card number. --- src/app/app.module.ts | 4 +- src/app/components/app/app.component.html | 3 +- .../identitycard/identitycard.component.html | 17 ++++ .../identitycard/identitycard.component.scss | 0 .../identitycard/identitycard.component.ts | 35 ++++++++ src/app/service/identity-card.service.ts | 85 +++++++++++++++++++ 6 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 src/app/components/identitycard/identitycard.component.html create mode 100644 src/app/components/identitycard/identitycard.component.scss create mode 100644 src/app/components/identitycard/identitycard.component.ts create mode 100644 src/app/service/identity-card.service.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 86d90dc..99cf931 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -11,6 +11,7 @@ import { PeselComponent } from './components/pesel/pesel.component'; import { NipComponent } from './components/nip/nip.component'; import { RegonComponent } from './components/regon/regon.component'; import { IbanComponent } from './components/iban/iban.component'; +import { IdentityCardComponent } from './components/identitycard/identitycard.component'; import {NgOptimizedImage} from "@angular/common"; @NgModule({ @@ -20,7 +21,8 @@ import {NgOptimizedImage} from "@angular/common"; PeselComponent, NipComponent, RegonComponent, - IbanComponent + IbanComponent, + IdentityCardComponent ], imports: [ BrowserModule, diff --git a/src/app/components/app/app.component.html b/src/app/components/app/app.component.html index 0e42051..78e1cab 100644 --- a/src/app/components/app/app.component.html +++ b/src/app/components/app/app.component.html @@ -3,5 +3,6 @@ + - \ No newline at end of file + diff --git a/src/app/components/identitycard/identitycard.component.html b/src/app/components/identitycard/identitycard.component.html new file mode 100644 index 0000000..3356630 --- /dev/null +++ b/src/app/components/identitycard/identitycard.component.html @@ -0,0 +1,17 @@ +
+
+ DOWÓD OSOBISTY +
+
+ + +
+ Generuj numer dowodu + Skopiuj numer dowodu do schowka + Definicja numeru dowodu na Wikipedii +
+
+
diff --git a/src/app/components/identitycard/identitycard.component.scss b/src/app/components/identitycard/identitycard.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/components/identitycard/identitycard.component.ts b/src/app/components/identitycard/identitycard.component.ts new file mode 100644 index 0000000..bf06aac --- /dev/null +++ b/src/app/components/identitycard/identitycard.component.ts @@ -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"); + } +} diff --git a/src/app/service/identity-card.service.ts b/src/app/service/identity-card.service.ts new file mode 100644 index 0000000..99a3492 --- /dev/null +++ b/src/app/service/identity-card.service.ts @@ -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; + } +}