@jjlmoya/utils-finance 1.28.0 → 1.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/package.json +1 -1
  2. package/src/category/index.ts +3 -1
  3. package/src/entries.ts +7 -1
  4. package/src/index.ts +2 -1
  5. package/src/tests/locale_completeness.test.ts +2 -3
  6. package/src/tests/tool_validation.test.ts +2 -2
  7. package/src/tool/pesetaValue/bibliography.astro +6 -0
  8. package/src/tool/pesetaValue/bibliography.ts +16 -0
  9. package/src/tool/pesetaValue/component.astro +86 -0
  10. package/src/tool/pesetaValue/controller.ts +126 -0
  11. package/src/tool/pesetaValue/dom-views.ts +83 -0
  12. package/src/tool/pesetaValue/entry.ts +31 -0
  13. package/src/tool/pesetaValue/evaluator.ts +18 -0
  14. package/src/tool/pesetaValue/i18n/de.ts +54 -0
  15. package/src/tool/pesetaValue/i18n/en.ts +138 -0
  16. package/src/tool/pesetaValue/i18n/es.ts +54 -0
  17. package/src/tool/pesetaValue/i18n/fr.ts +54 -0
  18. package/src/tool/pesetaValue/i18n/id.ts +48 -0
  19. package/src/tool/pesetaValue/i18n/it.ts +54 -0
  20. package/src/tool/pesetaValue/i18n/ja.ts +48 -0
  21. package/src/tool/pesetaValue/i18n/ko.ts +48 -0
  22. package/src/tool/pesetaValue/i18n/nl.ts +48 -0
  23. package/src/tool/pesetaValue/i18n/pl.ts +48 -0
  24. package/src/tool/pesetaValue/i18n/pt.ts +48 -0
  25. package/src/tool/pesetaValue/i18n/ru.ts +48 -0
  26. package/src/tool/pesetaValue/i18n/sv.ts +48 -0
  27. package/src/tool/pesetaValue/i18n/tr.ts +48 -0
  28. package/src/tool/pesetaValue/i18n/zh.ts +48 -0
  29. package/src/tool/pesetaValue/index.ts +11 -0
  30. package/src/tool/pesetaValue/logic.test.ts +63 -0
  31. package/src/tool/pesetaValue/logic.ts +162 -0
  32. package/src/tool/pesetaValue/pesetas-to-euros-with-inflation-calculator.css +564 -0
  33. package/src/tool/pesetaValue/seo.astro +15 -0
  34. package/src/tool/pesetaValue/storage.ts +25 -0
  35. package/src/tool/pesetaValue/ui.ts +33 -0
  36. package/src/tool/salaryEmployerCost/bibliography.astro +6 -0
  37. package/src/tool/salaryEmployerCost/bibliography.ts +16 -0
  38. package/src/tool/salaryEmployerCost/component.astro +130 -0
  39. package/src/tool/salaryEmployerCost/controller.ts +129 -0
  40. package/src/tool/salaryEmployerCost/dom-views.ts +87 -0
  41. package/src/tool/salaryEmployerCost/entry.ts +31 -0
  42. package/src/tool/salaryEmployerCost/evaluator.ts +9 -0
  43. package/src/tool/salaryEmployerCost/i18n/de.ts +159 -0
  44. package/src/tool/salaryEmployerCost/i18n/en.ts +148 -0
  45. package/src/tool/salaryEmployerCost/i18n/es.ts +148 -0
  46. package/src/tool/salaryEmployerCost/i18n/fr.ts +159 -0
  47. package/src/tool/salaryEmployerCost/i18n/id.ts +159 -0
  48. package/src/tool/salaryEmployerCost/i18n/it.ts +159 -0
  49. package/src/tool/salaryEmployerCost/i18n/ja.ts +159 -0
  50. package/src/tool/salaryEmployerCost/i18n/ko.ts +159 -0
  51. package/src/tool/salaryEmployerCost/i18n/nl.ts +159 -0
  52. package/src/tool/salaryEmployerCost/i18n/pl.ts +159 -0
  53. package/src/tool/salaryEmployerCost/i18n/pt.ts +159 -0
  54. package/src/tool/salaryEmployerCost/i18n/ru.ts +159 -0
  55. package/src/tool/salaryEmployerCost/i18n/sv.ts +159 -0
  56. package/src/tool/salaryEmployerCost/i18n/tr.ts +159 -0
  57. package/src/tool/salaryEmployerCost/i18n/zh.ts +159 -0
  58. package/src/tool/salaryEmployerCost/index.ts +11 -0
  59. package/src/tool/salaryEmployerCost/logic.test.ts +88 -0
  60. package/src/tool/salaryEmployerCost/logic.ts +133 -0
  61. package/src/tool/salaryEmployerCost/seo.astro +15 -0
  62. package/src/tool/salaryEmployerCost/spain-gross-net-salary-employer-cost-calculator.css +522 -0
  63. package/src/tool/salaryEmployerCost/storage.ts +35 -0
  64. package/src/tool/salaryEmployerCost/ui.ts +41 -0
  65. package/src/tools.ts +3 -3
@@ -0,0 +1,564 @@
1
+ :root {
2
+ --n-pv-ink: #17212b;
3
+ --n-pv-muted: #6d7a80;
4
+ --n-pv-paper: #fffaf0;
5
+ --n-pv-card: #fff;
6
+ --n-pv-line: #e7dcc8;
7
+ --n-pv-copper: #b96f42;
8
+ --n-pv-copper-deep: #81462f;
9
+ --n-pv-gold: #d49b41;
10
+ --n-pv-mint: #86a99a;
11
+ --n-pv-shadow: rgba(56, 39, 21, .12);
12
+ --n-pv-stage: #f2e6d2;
13
+ }
14
+
15
+ .theme-dark,
16
+ [data-theme='dark'] {
17
+ --n-pv-ink: #f5ead8;
18
+ --n-pv-muted: #c9bca8;
19
+ --n-pv-paper: #1d2222;
20
+ --n-pv-card: #252b2a;
21
+ --n-pv-line: #4b4840;
22
+ --n-pv-copper: #e29a69;
23
+ --n-pv-copper-deep: #f0b68f;
24
+ --n-pv-gold: #e3ba65;
25
+ --n-pv-mint: #9bc7b6;
26
+ --n-pv-shadow: rgba(0, 0, 0, .3);
27
+ --n-pv-stage: #303535;
28
+ }
29
+
30
+ .pv-tool {
31
+ color: var(--n-pv-ink);
32
+ background: var(--n-pv-paper);
33
+ border: 1px solid var(--n-pv-line);
34
+ border-radius: 2rem;
35
+ box-shadow: 0 1.5rem 3rem var(--n-pv-shadow);
36
+ overflow: hidden;
37
+ }
38
+
39
+ .pv-stage {
40
+ display: grid;
41
+ grid-template-columns: minmax(15rem, .8fr) minmax(18rem, 1.2fr);
42
+ min-height: 27rem;
43
+ }
44
+
45
+ .pv-controls {
46
+ padding: 2.5rem;
47
+ background: var(--n-pv-card);
48
+ }
49
+
50
+ .pv-control-heading {
51
+ display: flex;
52
+ align-items: center;
53
+ justify-content: space-between;
54
+ gap: 1rem;
55
+ margin-bottom: 2rem;
56
+ }
57
+
58
+ .pv-eyebrow,
59
+ .pv-label,
60
+ .pv-result-kicker,
61
+ .pv-result-grid span {
62
+ color: var(--n-pv-muted);
63
+ font-size: .72rem;
64
+ font-weight: 700;
65
+ letter-spacing: .08em;
66
+ text-transform: uppercase;
67
+ }
68
+
69
+ .pv-amount-label {
70
+ color: var(--n-pv-copper);
71
+ font-size: .8rem;
72
+ font-weight: 700;
73
+ }
74
+
75
+ .pv-label {
76
+ display: block;
77
+ margin-bottom: .6rem;
78
+ }
79
+
80
+ .pv-amount-field {
81
+ display: flex;
82
+ align-items: center;
83
+ gap: .75rem;
84
+ border-bottom: 2px solid var(--n-pv-copper);
85
+ }
86
+
87
+ .pv-amount-field input {
88
+ width: 100%;
89
+ padding: .2rem 0 .65rem;
90
+ color: var(--n-pv-ink);
91
+ background: transparent;
92
+ border: 0;
93
+ font-size: 2.6rem;
94
+ font-weight: 700;
95
+ outline: 0;
96
+ }
97
+
98
+ .pv-amount-field span {
99
+ color: var(--n-pv-copper);
100
+ font-weight: 700;
101
+ }
102
+
103
+ .pv-presets {
104
+ display: flex;
105
+ flex-wrap: wrap;
106
+ gap: .5rem;
107
+ margin: 1.25rem 0 2.25rem;
108
+ }
109
+
110
+ .pv-presets button,
111
+ .pv-reset {
112
+ padding: .5rem .75rem;
113
+ color: var(--n-pv-muted);
114
+ background: transparent;
115
+ border: 1px solid var(--n-pv-line);
116
+ border-radius: 99rem;
117
+ cursor: pointer;
118
+ font: inherit;
119
+ font-size: .78rem;
120
+ }
121
+
122
+ .pv-presets button:hover,
123
+ .pv-presets button.is-active {
124
+ color: var(--n-pv-card);
125
+ background: var(--n-pv-copper);
126
+ border-color: var(--n-pv-copper);
127
+ }
128
+
129
+ .pv-year-control,
130
+ .pv-select {
131
+ position: relative;
132
+ }
133
+
134
+ .pv-select-trigger {
135
+ width: 100%;
136
+ padding: .9rem 1rem;
137
+ color: var(--n-pv-ink);
138
+ background: var(--n-pv-paper);
139
+ border: 1px solid var(--n-pv-line);
140
+ border-radius: .8rem;
141
+ cursor: pointer;
142
+ font: inherit;
143
+ font-size: 1.2rem;
144
+ font-weight: 700;
145
+ text-align: left;
146
+ }
147
+
148
+ .pv-select-trigger::after {
149
+ float: right;
150
+ color: var(--n-pv-copper);
151
+ content: '⌄';
152
+ }
153
+
154
+ .pv-year-menu {
155
+ position: absolute;
156
+ z-index: 2;
157
+ top: calc(100% + .4rem);
158
+ display: none;
159
+ width: 100%;
160
+ max-height: 16rem;
161
+ padding: .75rem;
162
+ background: var(--n-pv-card);
163
+ border: 1px solid var(--n-pv-line);
164
+ border-radius: .8rem;
165
+ box-shadow: 0 1rem 2rem var(--n-pv-shadow);
166
+ overflow-x: hidden;
167
+ overflow-y: auto;
168
+ }
169
+
170
+ .pv-year-menu.is-open {
171
+ display: block;
172
+ }
173
+
174
+ .pv-decade-nav {
175
+ display: grid;
176
+ grid-template-columns: repeat(5, minmax(0, 1fr));
177
+ gap: .35rem;
178
+ padding-bottom: .65rem;
179
+ border-bottom: 1px solid var(--n-pv-line);
180
+ overflow: hidden;
181
+ }
182
+
183
+ .pv-decade-nav button {
184
+ min-width: 0;
185
+ padding: .45rem .15rem;
186
+ color: var(--n-pv-muted);
187
+ background: var(--n-pv-paper);
188
+ border: 1px solid var(--n-pv-line);
189
+ border-radius: 99rem;
190
+ cursor: pointer;
191
+ font: inherit;
192
+ font-size: .68rem;
193
+ font-weight: 700;
194
+ }
195
+
196
+ .pv-decade-nav button.is-active,
197
+ .pv-decade-nav button:hover {
198
+ color: var(--n-pv-card);
199
+ background: var(--n-pv-copper);
200
+ border-color: var(--n-pv-copper);
201
+ }
202
+
203
+ .pv-year-grid {
204
+ display: grid;
205
+ grid-template-columns: repeat(4, 1fr);
206
+ gap: .25rem;
207
+ padding-top: .65rem;
208
+ }
209
+
210
+ .pv-year-grid button {
211
+ padding: .55rem .2rem;
212
+ color: var(--n-pv-muted);
213
+ background: transparent;
214
+ border: 0;
215
+ border-radius: .5rem;
216
+ cursor: pointer;
217
+ font: inherit;
218
+ }
219
+
220
+ .pv-year-grid button:hover,
221
+ .pv-year-grid button.is-selected {
222
+ color: var(--n-pv-card);
223
+ background: var(--n-pv-copper);
224
+ }
225
+
226
+ .pv-select-trigger:focus-visible,
227
+ .pv-year-menu button:focus-visible,
228
+ .pv-presets button:focus-visible,
229
+ .pv-reset:focus-visible {
230
+ outline: 3px solid var(--n-pv-mint);
231
+ outline-offset: 2px;
232
+ }
233
+
234
+ .pv-help {
235
+ display: block;
236
+ margin-top: .65rem;
237
+ color: var(--n-pv-muted);
238
+ font-size: .8rem;
239
+ }
240
+
241
+ .pv-reset {
242
+ margin-top: 2rem;
243
+ }
244
+
245
+ .pv-visual {
246
+ position: relative;
247
+ display: grid;
248
+ place-items: center;
249
+ min-height: 27rem;
250
+ padding: 2rem;
251
+ background: var(--n-pv-stage);
252
+ isolation: isolate;
253
+ overflow: hidden;
254
+ }
255
+
256
+ .pv-visual::before,
257
+ .pv-visual::after {
258
+ position: absolute;
259
+ border: 1px solid var(--n-pv-line);
260
+ border-radius: 50%;
261
+ content: '';
262
+ inset: 12%;
263
+ transform: rotate(-14deg);
264
+ }
265
+
266
+ .pv-visual::after {
267
+ inset: 22%;
268
+ transform: rotate(21deg);
269
+ }
270
+
271
+ .pv-skyline {
272
+ position: absolute;
273
+ right: 8%;
274
+ bottom: 0;
275
+ left: 8%;
276
+ display: flex;
277
+ align-items: end;
278
+ gap: .35rem;
279
+ opacity: .35;
280
+ }
281
+
282
+ .pv-skyline span {
283
+ flex: 1;
284
+ height: 2rem;
285
+ background: var(--n-pv-copper);
286
+ }
287
+
288
+ .pv-skyline span:nth-child(2) { height: 4rem; }
289
+ .pv-skyline span:nth-child(3) { height: 3rem; }
290
+ .pv-skyline span:nth-child(4) { height: 5.5rem; }
291
+ .pv-skyline span:nth-child(5) { height: 3.75rem; }
292
+ .pv-skyline span:nth-child(6) { height: 6.5rem; }
293
+
294
+ .pv-cash-scene {
295
+ z-index: 1;
296
+ display: grid;
297
+ place-items: center;
298
+ width: min(22rem, 100%);
299
+ min-height: 15rem;
300
+ }
301
+
302
+ .pv-cash-pieces {
303
+ display: flex;
304
+ align-items: end;
305
+ justify-content: center;
306
+ flex-wrap: wrap;
307
+ gap: .55rem;
308
+ width: min(24rem, 100%);
309
+ transform: rotate(-2deg);
310
+ }
311
+
312
+ .pv-cash-piece {
313
+ position: relative;
314
+ display: grid;
315
+ place-items: center;
316
+ flex: 0 0 auto;
317
+ height: 3rem;
318
+ padding: .25rem;
319
+ color: var(--n-pv-paper);
320
+ background: var(--n-pv-copper);
321
+ border: .25rem solid var(--n-pv-gold);
322
+ box-shadow: .35rem .45rem 0 var(--n-pv-copper-deep), 0 .7rem 1.2rem var(--n-pv-shadow);
323
+ transform: rotate(-5deg);
324
+ }
325
+
326
+ .pv-cash-piece:nth-child(even) {
327
+ transform: translateY(-.35rem) rotate(5deg);
328
+ }
329
+
330
+ .pv-cash-banknote {
331
+ border-radius: .35rem;
332
+ }
333
+
334
+ .pv-cash-coin {
335
+ aspect-ratio: 1;
336
+ border-radius: 50%;
337
+ }
338
+
339
+ .pv-cash-piece.is-used {
340
+ box-shadow: .35rem .45rem 0 var(--n-pv-copper-deep), 0 .9rem 1.5rem var(--n-pv-shadow);
341
+ z-index: 1;
342
+ transform: translateY(-.4rem) rotate(-2deg) scale(1.05);
343
+ }
344
+
345
+ .pv-cash-piece strong {
346
+ font-size: .85rem;
347
+ line-height: 1;
348
+ }
349
+
350
+ .pv-cash-piece small {
351
+ position: absolute;
352
+ top: -.55rem;
353
+ right: -.45rem;
354
+ min-width: 1.4rem;
355
+ padding: .2rem;
356
+ color: var(--n-pv-card);
357
+ background: var(--n-pv-mint);
358
+ border-radius: 99rem;
359
+ font-size: .65rem;
360
+ font-weight: 700;
361
+ text-align: center;
362
+ }
363
+
364
+ .pv-cash-value-1 {
365
+ width: 2.2rem;
366
+ height: 2.2rem;
367
+ }
368
+
369
+ .pv-cash-value-5 {
370
+ width: 2.5rem;
371
+ height: 2.5rem;
372
+ }
373
+
374
+ .pv-cash-value-10 {
375
+ width: 2.8rem;
376
+ height: 2.8rem;
377
+ }
378
+
379
+ .pv-cash-value-25 {
380
+ width: 3.1rem;
381
+ height: 3.1rem;
382
+ }
383
+
384
+ .pv-cash-value-50 {
385
+ width: 3.35rem;
386
+ height: 3.35rem;
387
+ }
388
+
389
+ .pv-cash-value-100 {
390
+ width: 3.6rem;
391
+ height: 3.6rem;
392
+ }
393
+
394
+ .pv-cash-value-200 {
395
+ width: 3.85rem;
396
+ height: 3.85rem;
397
+ }
398
+
399
+ .pv-cash-value-500 {
400
+ width: 4.1rem;
401
+ height: 4.1rem;
402
+ }
403
+
404
+ .pv-cash-value-1000 {
405
+ width: 5.7rem;
406
+ height: 3.2rem;
407
+ color: #245c38;
408
+ background: linear-gradient(135deg, #72b767, #dcead3 48%, #5b9f56);
409
+ border-color: #4b8848;
410
+ }
411
+
412
+ .pv-cash-value-2000 {
413
+ width: 6.3rem;
414
+ height: 3.5rem;
415
+ color: #71382d;
416
+ background: linear-gradient(135deg, #c67c68, #f1d7be 48%, #b55c4b);
417
+ border-color: #9c5547;
418
+ }
419
+
420
+ .pv-cash-value-5000 {
421
+ width: 6.9rem;
422
+ height: 3.8rem;
423
+ color: #754a25;
424
+ background: linear-gradient(135deg, #d9944e, #f4dfb9 48%, #c97935);
425
+ border-color: #ae6b30;
426
+ }
427
+
428
+ .pv-cash-value-10000 {
429
+ width: 7.5rem;
430
+ height: 4.1rem;
431
+ color: #3f5664;
432
+ background: linear-gradient(135deg, #9eb5bc, #e2e7dc 48%, #7899a5);
433
+ border-color: #6f8f99;
434
+ }
435
+
436
+ .pv-cash-value-1,
437
+ .pv-cash-value-100,
438
+ .pv-cash-value-500 {
439
+ background: linear-gradient(135deg, #d6a94f, #f4df9e 48%, #a66e2f);
440
+ }
441
+
442
+ .pv-cash-value-5,
443
+ .pv-cash-value-10,
444
+ .pv-cash-value-200 {
445
+ background: linear-gradient(135deg, #a98668, #e4d2bb 48%, #80624e);
446
+ }
447
+
448
+ .pv-cash-value-25::after {
449
+ position: absolute;
450
+ width: .65rem;
451
+ height: .65rem;
452
+ border: 2px solid rgba(91, 64, 45, .35);
453
+ border-radius: 50%;
454
+ background: var(--n-pv-stage);
455
+ box-shadow: inset 0 0 .15rem rgba(56, 39, 21, .25);
456
+ content: '';
457
+ }
458
+
459
+ .pv-cash-caption {
460
+ margin-top: 1rem;
461
+ color: var(--n-pv-copper-deep);
462
+ font-size: .75rem;
463
+ font-weight: 700;
464
+ letter-spacing: .04em;
465
+ }
466
+
467
+ .pv-orbit {
468
+ position: absolute;
469
+ width: 19rem;
470
+ height: 19rem;
471
+ border: 1px dashed var(--n-pv-copper);
472
+ border-radius: 50%;
473
+ animation: pv-spin 22s linear infinite;
474
+ }
475
+
476
+ .pv-orbit-dot {
477
+ position: absolute;
478
+ top: -.35rem;
479
+ left: 50%;
480
+ width: .7rem;
481
+ height: .7rem;
482
+ background: var(--n-pv-mint);
483
+ border-radius: 50%;
484
+ }
485
+
486
+ .pv-visual-caption {
487
+ position: absolute;
488
+ right: 2rem;
489
+ bottom: 2rem;
490
+ left: 2rem;
491
+ display: flex;
492
+ justify-content: space-between;
493
+ color: var(--n-pv-muted);
494
+ font-size: .78rem;
495
+ }
496
+
497
+ .pv-state {
498
+ position: absolute;
499
+ top: 2rem;
500
+ right: 2rem;
501
+ display: flex;
502
+ align-items: center;
503
+ gap: .5rem;
504
+ color: var(--n-pv-copper-deep);
505
+ font-size: .78rem;
506
+ font-weight: 700;
507
+ text-transform: capitalize;
508
+ }
509
+
510
+ .pv-state-dot {
511
+ width: .55rem;
512
+ height: .55rem;
513
+ background: var(--n-pv-mint);
514
+ border-radius: 50%;
515
+ }
516
+
517
+ .pv-results {
518
+ padding: 2rem 2.5rem 2.5rem;
519
+ background: var(--n-pv-card);
520
+ border-top: 1px solid var(--n-pv-line);
521
+ }
522
+
523
+ .pv-result-main {
524
+ display: flex;
525
+ align-items: baseline;
526
+ flex-wrap: wrap;
527
+ gap: .8rem;
528
+ padding-bottom: 1.5rem;
529
+ }
530
+
531
+ .pv-result-main strong {
532
+ color: var(--n-pv-copper-deep);
533
+ font-size: clamp(2.5rem, 6vw, 4.6rem);
534
+ line-height: .95;
535
+ }
536
+
537
+ .pv-result-note {
538
+ color: var(--n-pv-mint);
539
+ font-weight: 700;
540
+ }
541
+
542
+ .pv-result-grid {
543
+ display: grid;
544
+ grid-template-columns: repeat(4, 1fr);
545
+ gap: 1rem;
546
+ }
547
+
548
+ .pv-result-grid div {
549
+ display: grid;
550
+ gap: .4rem;
551
+ padding-top: .8rem;
552
+ border-top: 1px solid var(--n-pv-line);
553
+ }
554
+
555
+ .pv-result-grid strong { font-size: 1rem; }
556
+
557
+ @keyframes pv-spin { to { transform: rotate(360deg); } }
558
+
559
+ @media (max-width: 760px) {
560
+ .pv-stage { grid-template-columns: 1fr; }
561
+ .pv-visual { min-height: 23rem; }
562
+ .pv-results, .pv-controls { padding: 1.5rem; }
563
+ .pv-result-grid { grid-template-columns: repeat(2, 1fr); }
564
+ }
@@ -0,0 +1,15 @@
1
+ ---
2
+ import { SEORenderer } from '@jjlmoya/utils-shared';
3
+ import { pesetaValue } from './entry';
4
+ import type { KnownLocale } from '../../types';
5
+
6
+ interface Props {
7
+ locale?: KnownLocale;
8
+ }
9
+
10
+ const { locale = 'en' } = Astro.props;
11
+ const content = await pesetaValue.i18n[locale]?.();
12
+ if (!content) return null;
13
+ ---
14
+
15
+ {content.seo.length > 0 && <SEORenderer content={{ locale, sections: content.seo }} />}
@@ -0,0 +1,25 @@
1
+ import type { PesetaValueInput } from './logic';
2
+
3
+ const STORAGE_KEY = 'jjlmoya-peseta-value';
4
+
5
+ export interface SavedPesetaValueState extends PesetaValueInput {
6
+ preset?: number;
7
+ }
8
+
9
+ export function readPesetaValueState(): SavedPesetaValueState | null {
10
+ try {
11
+ const raw = window.localStorage.getItem(STORAGE_KEY);
12
+ if (!raw) return null;
13
+ const parsed = JSON.parse(raw) as SavedPesetaValueState;
14
+ if (!Number.isFinite(parsed.pesetas) || !Number.isInteger(parsed.originYear)) return null;
15
+ return parsed;
16
+ } catch {
17
+ return null;
18
+ }
19
+ }
20
+
21
+ export function writePesetaValueState(state: SavedPesetaValueState): void {
22
+ try {
23
+ window.localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
24
+ } catch {}
25
+ }
@@ -0,0 +1,33 @@
1
+ export interface PesetaValueUI extends Record<string, string> {
2
+ labelAmount: string;
3
+ labelAmountHint: string;
4
+ labelOriginYear: string;
5
+ labelOriginYearHint: string;
6
+ labelReferenceYear: string;
7
+ labelNominalEuros: string;
8
+ labelTodayValue: string;
9
+ labelPurchasingPowerChange: string;
10
+ labelNominalDifference: string;
11
+ labelConversionRate: string;
12
+ labelCpiMethod: string;
13
+ labelPreset100: string;
14
+ labelPreset1000: string;
15
+ labelPreset10000: string;
16
+ labelPreset100000: string;
17
+ labelPesetas: string;
18
+ labelEuro: string;
19
+ labelFrom: string;
20
+ labelTo: string;
21
+ labelOpenYears: string;
22
+ labelCloseYears: string;
23
+ labelDecade: string;
24
+ labelCoin: string;
25
+ labelBanknote: string;
26
+ labelCashPieces: string;
27
+ labelState: string;
28
+ labelStatePositive: string;
29
+ labelStateNeutral: string;
30
+ labelStateNegative: string;
31
+ labelReset: string;
32
+ currencyLocale: string;
33
+ }
@@ -0,0 +1,6 @@
1
+ ---
2
+ import { Bibliography as SharedBibliography } from '@jjlmoya/utils-shared';
3
+ import { bibliography } from './bibliography';
4
+ ---
5
+
6
+ <SharedBibliography links={bibliography} />
@@ -0,0 +1,16 @@
1
+ import type { BibliographyEntry } from '../../types';
2
+
3
+ export const bibliography: BibliographyEntry[] = [
4
+ {
5
+ name: 'Spanish Social Security contribution bases and rates for 2026',
6
+ url: 'https://www.boe.es/eli/es/o/2026/03/30/pjc297',
7
+ },
8
+ {
9
+ name: 'Spanish Tax Agency personal income tax withholding algorithm for 2026',
10
+ url: 'https://sede.agenciatributaria.gob.es/static_files/Sede/Programas_ayuda/Retenciones/2026/ALGORITMO_2026.pdf',
11
+ },
12
+ {
13
+ name: 'Spain 2026 occupational accident and disease premium table',
14
+ url: 'https://www.boe.es/buscar/act.php?id=BOE-A-2026-2548&p=20260204&tn=1',
15
+ },
16
+ ];