removing friendly pages

This commit is contained in:
Artur 2025-01-18 12:10:10 +01:00
parent 797863008d
commit 444ee0481d
13 changed files with 195 additions and 24 deletions

View File

@ -0,0 +1 @@
python3 compress-with-structure.py

View File

@ -0,0 +1,51 @@
import os
from PIL import Image, UnidentifiedImageError
from shutil import copy2
# Ścieżki katalogów
input_folder = "src/main/resources/static/zaklik-public-images" # Folder źródłowy
output_folder = "src/main/resources/static/zaklik-public-images" # Folder docelowy
# Funkcja kompresująca obrazy
def compress_image(input_path, output_path):
try:
with Image.open(input_path) as img:
img_format = img.format # Zachowaj format pliku
if img_format in ["JPEG", "PNG"]:
img.save(output_path, optimize=True, quality=15)
else:
copy2(input_path, output_path) # Kopiuj bez zmian, jeśli format nie jest obsługiwany
except UnidentifiedImageError:
print(f"Nie rozpoznano formatu obrazu: {input_path}")
# Funkcja do przetwarzania plików w folderze
def process_folder(input_folder, output_folder):
for root, _, files in os.walk(input_folder):
for file in files:
# Ścieżka do pliku wejściowego
input_path = os.path.join(root, file)
# Tworzenie ścieżki docelowej z odpowiednią strukturą katalogów
relative_path = os.path.relpath(root, input_folder)
target_dir = os.path.join(output_folder, relative_path)
os.makedirs(target_dir, exist_ok=True)
# Obsługa plików już skompresowanych
if "(compressed)" in file:
continue
if "properties" in file:
continue
# Generowanie nazwy pliku wyjściowego
name, ext = os.path.splitext(file)
output_file = f"{name}(compressed){ext}"
output_path = os.path.join(target_dir, output_file)
# Sprawdzenie, czy skompresowany plik już istnieje
if os.path.exists(output_path):
print(f"Pominięto, plik już istnieje: {output_path}")
continue
# Kompresja pliku
print(f"Kompresowanie: {input_path} -> {output_path}")
compress_image(input_path, output_path)
# Wykonanie skryptu
process_folder(input_folder, output_folder)

View File

@ -0,0 +1,42 @@
INPUT_DIR="./src/main/resources/static/zaklik-public-images" # Folder z oryginalnymi obrazami
OUTPUT_DIR="./src/main/resources/static/zaklik-public-images-generated" # Główny folder do zapisu obrazów w nowych formatach
# Iteracja po wszystkich plikach w katalogu wejściowym i jego podkatalogach
find "$INPUT_DIR" -type f | while read -r file; do
# Relatywna ścieżka pliku w katalogu wejściowym
relative_path="${file#$INPUT_DIR/}"
# Ścieżka do katalogu docelowego
target_dir="$OUTPUT_DIR/$(dirname "$relative_path")"
# Nazwa pliku bez rozszerzenia
filename=$(basename "$file" | cut -f 1 -d '.')
# Ścieżki docelowych plików w formatach WebP i AVIF
webp_file="$target_dir/$filename.webp"
avif_file="$target_dir/$filename.avif"
# Sprawdzenie, czy oba pliki już istnieją
if [ -f "$webp_file" ] && [ -f "$avif_file" ]; then
echo "Pomijam plik: $file (już skompresowany)"
continue
fi
# Tworzenie podkatalogu w katalogu docelowym
mkdir -p "$target_dir"
# Konwersja do WebP (jeśli plik nie istnieje)
if [ ! -f "$webp_file" ]; then
convert "$file" -quality 80 "$webp_file"
echo "Skompresowano do WebP: $webp_file"
fi
# Konwersja do AVIF (jeśli plik nie istnieje)
if [ ! -f "$avif_file" ]; then
convert "$file" -quality 80 "$avif_file"
echo "Skompresowano do AVIF: $avif_file"
fi
done
echo "Konwersja zakończona."

View File

@ -0,0 +1,33 @@
const sharp = require('sharp');
const fs = require('fs');
const path = require('path');
const inputDir = './src/main/resources/static/zaklik-public-images'; // Folder z oryginalnymi obrazami
const outputDir = './src/main/resources/static/main-images/optimized-by-sharp'; // Folder do zapisu obrazów w nowych formatach
const formats = ['webp', 'avif'];
fs.readdir(inputDir, (err, files) => {
if (err) {
console.error('Error reading input directory:', err);
return;
}
files.forEach(file => {
const inputFile = path.join(inputDir, file);
const fileName = path.parse(file).name;
formats.forEach(format => {
const outputFile = path.join(outputDir, `${fileName}.${format}`);
sharp(inputFile)
.toFormat(format, { quality: 80 })
.toFile(outputFile, (err, info) => {
if (err) {
console.error(`Error converting ${file} to ${format}:`, err);
} else {
console.log(`Converted ${file} to ${format} format.`);
}
});
});
});
});

10
package-lock.json generated
View File

@ -21,6 +21,7 @@
"@ngx-translate/core": "^16.0.3", "@ngx-translate/core": "^16.0.3",
"@ngx-translate/http-loader": "^16.0.0", "@ngx-translate/http-loader": "^16.0.0",
"bootstrap": "^5.3.3", "bootstrap": "^5.3.3",
"date-fns": "^4.1.0",
"rxjs": "~7.8.0", "rxjs": "~7.8.0",
"tslib": "^2.3.0", "tslib": "^2.3.0",
"zone.js": "~0.14.10" "zone.js": "~0.14.10"
@ -9423,6 +9424,15 @@
"integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==",
"dev": true "dev": true
}, },
"node_modules/date-fns": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/kossnocorp"
}
},
"node_modules/date-format": { "node_modules/date-format": {
"version": "4.0.14", "version": "4.0.14",
"resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz",

View File

@ -24,6 +24,7 @@
"@ngx-translate/core": "^16.0.3", "@ngx-translate/core": "^16.0.3",
"@ngx-translate/http-loader": "^16.0.0", "@ngx-translate/http-loader": "^16.0.0",
"bootstrap": "^5.3.3", "bootstrap": "^5.3.3",
"date-fns": "^4.1.0",
"rxjs": "~7.8.0", "rxjs": "~7.8.0",
"tslib": "^2.3.0", "tslib": "^2.3.0",
"zone.js": "~0.14.10" "zone.js": "~0.14.10"
@ -47,4 +48,4 @@
"typescript-eslint": "8.10.0", "typescript-eslint": "8.10.0",
"vite": "^5.4.10" "vite": "^5.4.10"
} }
} }

View File

@ -8,19 +8,19 @@
<h5 class="card-title">Wrocław</h5> <h5 class="card-title">Wrocław</h5>
<div class="col-auto"> <div class="col-auto">
<a href="mailto:kusartur@gmail.com"> <a href="mailto:kusartur@gmail.com">
<img width="30" height="20" src="icons/envelope.svg" /> <img width="30" height="20" src="icons/envelope.svg" alt="envelope" >
kusartur&#64;gmail.com kusartur&#64;gmail.com
</a> </a>
</div> </div>
<div class="col-auto"> <div class="col-auto">
<a href="tel:+48512558222"> <img width="30" height="20" <a href="tel:+48511116016"> <img width="30" height="20"
src="icons/telephone.svg" /> +48 511 116 016</a> src="icons/telephone.svg" alt="telephone" > +48 511 116 016</a>
</div> </div>
<div class="col-auto"> <div class="col-auto">
<a href="https://www.linkedin.com/in/artur-ku%C5%9B-99a7b02/"> <img <a href="https://www.linkedin.com/in/artur-ku%C5%9B-99a7b02/"> <img
width="30" height="20" src="icons/linkedin.svg" />Linkedin width="30" height="20" src="icons/linkedin.svg" alt="linkedin" >Linkedin
</a> </a>
</div> </div>
</div> </div>

View File

@ -47,7 +47,6 @@
<a class="navbar-brand" [routerLink]="'/about-me'">{{ 'topBar.aboutMe' | translate }}</a> <a class="navbar-brand" [routerLink]="'/about-me'">{{ 'topBar.aboutMe' | translate }}</a>
<a class="navbar-brand" [routerLink]="'/asset-calculator'">{{ 'topBar.depreciationCalculator' | translate }}</a> <a class="navbar-brand" [routerLink]="'/asset-calculator'">{{ 'topBar.depreciationCalculator' | translate }}</a>
<a class="navbar-brand" [routerLink]="'/quotes'">{{ 'topBar.courses' | translate }}</a> <a class="navbar-brand" [routerLink]="'/quotes'">{{ 'topBar.courses' | translate }}</a>
<a class="navbar-brand" [routerLink]="'/friendly-pages'">{{ 'topBar.friendlyPages' | translate }}</a>
</div> </div>
</nav> </nav>

View File

@ -36,11 +36,6 @@ export const routes: Routes = [
path: "fixed-asset", path: "fixed-asset",
title:"Środki trwałe", title:"Środki trwałe",
component: FixedAssetComponent component: FixedAssetComponent
},
{
path: "friendly-pages",
title:"Zaprzyjaznione strony",
component: FriendlyPagesComponent
} }

View File

@ -0,0 +1,6 @@
.month-picker .mat-calendar-body-cell-content {
display: none; /* Ukrywa dni */
}
.month-picker .mat-calendar-body-cell-content:not(.mat-calendar-body-disabled) {
display: flex;
}

View File

@ -44,12 +44,18 @@
</div> </div>
<div class="form-group row align-items-center "> <div class="form-group row align-items-center ">
<div class="col-5 col-lg-6 text-start "> <div class="col-5 col-lg-6 text-start ">
<label class="form-label" for="startDepreciation" >{{ 'asset-calculator.startOfDepreciation' | translate }} </label> <label class="form-label" for="startDepreciation" >{{ 'asset-calculator.startOfDepreciation' | translate }} </label>
</div> </div>
<div class="col-7 col-lg-6"> <div class="col-7 col-lg-6">
<input type="month" lang="pl" class="form-control" [(ngModel)]="startDepreciation" id="startDepreciation" placeholder="2024" /> <mat-form-field appearance="outline" class="w-100">
<mat-label>{{ 'asset-calculator.startOfDepreciation' | translate }}</mat-label>
<input matInput [(ngModel)]="startDepreciation" [matDatepicker]="picker" readonly>
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker startView="multi-year" (monthSelected)="onMonthSelected($event, picker)" panelClass="month-picker"></mat-datepicker>
</mat-form-field>
</div> </div>
</div> </div>

View File

@ -4,11 +4,16 @@ import { Asset, AssetPlanPosition, TypeDepreciation, YearMonth, AssetLifeChange,
import { AssetService } from '../assets/service/asset.service'; import { AssetService } from '../assets/service/asset.service';
import { TranslateModule, TranslateService } from "@ngx-translate/core"; import { TranslateModule, TranslateService } from "@ngx-translate/core";
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { MatDatepicker, MatDatepickerModule } from '@angular/material/datepicker';
import { MatNativeDateModule } from '@angular/material/core';
import { MatInputModule } from '@angular/material/input';
import { format, parse, addMonths, isAfter } from 'date-fns';
interface FormValues { interface FormValues {
initialValueAsset:number; initialValueAsset:number;
rate: number; rate: number;
startDepreciation: string; startDepreciation: Date;
typeDepreciation: TypeDepreciation; typeDepreciation: TypeDepreciation;
factor: number; factor: number;
} }
@ -17,7 +22,6 @@ class AssetLifeChangeWrapper{
when = signal<string> ( YearMonth.todayTxt()); when = signal<string> ( YearMonth.todayTxt());
initial = signal<number>( 0 ); initial = signal<number>( 0 );
constructor( assetLifeChange : AssetLifeChange ){ constructor( assetLifeChange : AssetLifeChange ){
this.set(assetLifeChange); this.set(assetLifeChange);
} }
@ -38,7 +42,11 @@ const NAME_IN_STORAGE = 'assetForCalculator';
@Component({ @Component({
selector: 'app-asset-calculator', selector: 'app-asset-calculator',
standalone: true, standalone: true,
imports: [DecimalPipe, TranslateModule, FormsModule], imports: [DecimalPipe, TranslateModule, FormsModule, MatDatepickerModule, MatNativeDateModule, MatInputModule],
providers: [
MatDatepickerModule,
MatNativeDateModule
],
templateUrl: "asset-calculator.component.html", templateUrl: "asset-calculator.component.html",
styleUrls: ['asset-calculator.component.css'] styleUrls: ['asset-calculator.component.css']
}) })
@ -50,7 +58,7 @@ export class AssetCalculatorComponent {
initialValueAsset = signal <number> ( 6000 ); initialValueAsset = signal <number> ( 6000 );
rate = signal <number> ( 20 ); rate = signal <number> ( 20 );
startDepreciation = signal <string> ( YearMonth.today().toString() ); startDepreciation = signal <Date> ( new Date() );
typeDepreciation = signal <TypeDepreciation>( TypeDepreciation.linear ); typeDepreciation = signal <TypeDepreciation>( TypeDepreciation.linear );
factor = signal <number>( 2 ); factor = signal <number>( 2 );
@ -74,6 +82,8 @@ export class AssetCalculatorComponent {
effect( () => this.reCalculate( ) ) effect( () => this.reCalculate( ) )
} }
private storage = { private storage = {
load: (key: string) => { load: (key: string) => {
try { try {
@ -97,7 +107,6 @@ export class AssetCalculatorComponent {
this.restoreState(); this.restoreState();
} }
ngOnDestroy(): void { ngOnDestroy(): void {
const asset = this.controlsToAsset( ); const asset = this.controlsToAsset( );
if( null != asset){ if( null != asset){
@ -114,7 +123,6 @@ export class AssetCalculatorComponent {
this.storage.save(NAME_IN_STORAGE, asset); this.storage.save(NAME_IN_STORAGE, asset);
} }
private assetToControls( asset : Asset ) { private assetToControls( asset : Asset ) {
if( asset.life.length > 0 ){ if( asset.life.length > 0 ){
this.updateBatch(asset); this.updateBatch(asset);
@ -126,7 +134,7 @@ private updateBatch(asset: Asset) {
const method0 = asset.depreciationMethods[0]; const method0 = asset.depreciationMethods[0];
this.initialValueAsset.set(firstLife.initial); this.initialValueAsset.set(firstLife.initial);
this.startDepreciation.set(YearMonth.toTxt(firstLife.when)); this.startDepreciation.set(YearMonth.toDate(firstLife.when));
this.rate.set(method0.rate); this.rate.set(method0.rate);
this.typeDepreciation.set(method0.type); this.typeDepreciation.set(method0.type);
this.factor.set(method0.factor); this.factor.set(method0.factor);
@ -137,13 +145,12 @@ private updateBatch(asset: Asset) {
this.lifeChangesSignal.set(lifeChanges); this.lifeChangesSignal.set(lifeChanges);
} }
private controlsToAsset(): Asset { private controlsToAsset(): Asset {
const formValues = this.formValues(); const formValues = this.formValues();
const lifeChanges = this.lifeChangesSignal(); const lifeChanges = this.lifeChangesSignal();
const when = YearMonth.from( formValues.startDepreciation ); const when = YearMonth.fromDate( formValues.startDepreciation );
const asset = new Asset( when ); const asset = new Asset( when );
const method = new AssetDepreciationMethod( when.year, formValues.rate, formValues.typeDepreciation, formValues.factor ); const method = new AssetDepreciationMethod( when.year, formValues.rate, formValues.typeDepreciation, formValues.factor );
@ -200,7 +207,7 @@ private controlsToAsset(): Asset {
// Method to add a life change // Method to add a life change
addLifeChange( ) { addLifeChange( ) {
const assetLifeChange2 : AssetLifeChange const assetLifeChange2 : AssetLifeChange
= new AssetLifeChange( YearMonth.from( this.formValues().startDepreciation ), 1000, 0, 0 ); = new AssetLifeChange( YearMonth.fromDate( this.formValues().startDepreciation ), 1000, 0, 0 );
return this.addLifeChange2( assetLifeChange2 ); return this.addLifeChange2( assetLifeChange2 );
} }
@ -218,6 +225,15 @@ private controlsToAsset(): Asset {
); );
} }
updateDateFromInput(inputValue: string) {
// Konwersja stringa 'YYYY-MM' na Date
const parsedDate = parse(inputValue, 'yyyy-MM', new Date());
this.startDepreciation.set(parsedDate);
}
onMonthSelected(event: Date, datepicker: MatDatepicker<Date>) {
this.startDepreciation.set(event); // Aktualizacja pola w formularzu
datepicker.close();
}
} }

View File

@ -10,14 +10,22 @@ export class YearMonth{
return new YearMonth( year, month ); return new YearMonth( year, month );
} }
static fromDate( date: Date) {
return new YearMonth( date.getFullYear(), date.getMonth()+1 );
}
readonly year : number ; readonly year : number ;
readonly month : number ; readonly month : number ;
constructor( year:number, month : number ){ constructor( year:number, month : number ){
this.year = year; this.year = year;
this.month = month; this.month = month;
} }
toDate(){
return new Date(this.year, this.month-1, 1);
}
static today( ):YearMonth{ static today( ):YearMonth{
const today = new Date(); const today = new Date();
@ -31,6 +39,9 @@ export class YearMonth{
static toTxt( ym : YearMonth ):string{ static toTxt( ym : YearMonth ):string{
return ym.year + '-' + String(ym.month).padStart(2, '0');; return ym.year + '-' + String(ym.month).padStart(2, '0');;
} }
static toDate( ym : YearMonth) : Date{
return new Date(ym.year, ym.month-1, 1);
}
toJSON() { toJSON() {
return { year: this.year, month: this.month }; return { year: this.year, month: this.month };