@jjlmoya/utils-civic 1.5.0 → 1.7.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.
- package/package.json +1 -1
- package/src/category/index.ts +3 -1
- package/src/entries.ts +4 -0
- package/src/index.ts +1 -0
- package/src/tests/locale_completeness.test.ts +1 -1
- package/src/tests/tool_validation.test.ts +1 -1
- package/src/tool/participatory-budget-allocator/bibliography.astro +6 -0
- package/src/tool/participatory-budget-allocator/bibliography.ts +8 -0
- package/src/tool/participatory-budget-allocator/component.astro +61 -0
- package/src/tool/participatory-budget-allocator/contract.test.ts +13 -0
- package/src/tool/participatory-budget-allocator/controller.ts +108 -0
- package/src/tool/participatory-budget-allocator/dom-views.test.ts +14 -0
- package/src/tool/participatory-budget-allocator/dom-views.ts +58 -0
- package/src/tool/participatory-budget-allocator/entry.ts +27 -0
- package/src/tool/participatory-budget-allocator/evaluator.ts +24 -0
- package/src/tool/participatory-budget-allocator/i18n/de.ts +44 -0
- package/src/tool/participatory-budget-allocator/i18n/en.ts +79 -0
- package/src/tool/participatory-budget-allocator/i18n/es.ts +43 -0
- package/src/tool/participatory-budget-allocator/i18n/fr.ts +43 -0
- package/src/tool/participatory-budget-allocator/i18n/id.ts +43 -0
- package/src/tool/participatory-budget-allocator/i18n/it.ts +43 -0
- package/src/tool/participatory-budget-allocator/i18n/ja.ts +43 -0
- package/src/tool/participatory-budget-allocator/i18n/ko.ts +43 -0
- package/src/tool/participatory-budget-allocator/i18n/nl.ts +43 -0
- package/src/tool/participatory-budget-allocator/i18n/pl.ts +43 -0
- package/src/tool/participatory-budget-allocator/i18n/pt.ts +43 -0
- package/src/tool/participatory-budget-allocator/i18n/ru.ts +43 -0
- package/src/tool/participatory-budget-allocator/i18n/sv.ts +43 -0
- package/src/tool/participatory-budget-allocator/i18n/tr.ts +43 -0
- package/src/tool/participatory-budget-allocator/i18n/zh.ts +43 -0
- package/src/tool/participatory-budget-allocator/index.ts +11 -0
- package/src/tool/participatory-budget-allocator/logic.test.ts +48 -0
- package/src/tool/participatory-budget-allocator/logic.ts +146 -0
- package/src/tool/participatory-budget-allocator/participatory-budget-allocator.css +426 -0
- package/src/tool/participatory-budget-allocator/seo.astro +14 -0
- package/src/tool/participatory-budget-allocator/storage.ts +32 -0
- package/src/tool/participatory-budget-allocator/ui.ts +88 -0
- package/src/tool/public-budget-analyzer/bibliography.astro +15 -0
- package/src/tool/public-budget-analyzer/bibliography.ts +7 -0
- package/src/tool/public-budget-analyzer/component.astro +52 -0
- package/src/tool/public-budget-analyzer/controller.ts +84 -0
- package/src/tool/public-budget-analyzer/dom-views.ts +52 -0
- package/src/tool/public-budget-analyzer/entry.ts +27 -0
- package/src/tool/public-budget-analyzer/evaluator.ts +15 -0
- package/src/tool/public-budget-analyzer/i18n/de.ts +115 -0
- package/src/tool/public-budget-analyzer/i18n/en.ts +78 -0
- package/src/tool/public-budget-analyzer/i18n/es.ts +115 -0
- package/src/tool/public-budget-analyzer/i18n/fr.ts +115 -0
- package/src/tool/public-budget-analyzer/i18n/id.ts +115 -0
- package/src/tool/public-budget-analyzer/i18n/it.ts +115 -0
- package/src/tool/public-budget-analyzer/i18n/ja.ts +115 -0
- package/src/tool/public-budget-analyzer/i18n/ko.ts +115 -0
- package/src/tool/public-budget-analyzer/i18n/nl.ts +115 -0
- package/src/tool/public-budget-analyzer/i18n/pl.ts +115 -0
- package/src/tool/public-budget-analyzer/i18n/pt.ts +115 -0
- package/src/tool/public-budget-analyzer/i18n/ru.ts +115 -0
- package/src/tool/public-budget-analyzer/i18n/sv.ts +115 -0
- package/src/tool/public-budget-analyzer/i18n/tr.ts +115 -0
- package/src/tool/public-budget-analyzer/i18n/zh.ts +115 -0
- package/src/tool/public-budget-analyzer/index.ts +11 -0
- package/src/tool/public-budget-analyzer/logic.test.ts +81 -0
- package/src/tool/public-budget-analyzer/logic.ts +249 -0
- package/src/tool/public-budget-analyzer/public-budget-analyzer.css +461 -0
- package/src/tool/public-budget-analyzer/seo.astro +15 -0
- package/src/tool/public-budget-analyzer/storage.ts +28 -0
- package/src/tool/public-budget-analyzer/ui.ts +78 -0
- package/src/tools.ts +4 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { SEORenderer } from '@jjlmoya/utils-shared';
|
|
3
|
+
import { publicBudgetAnalyzer } from './entry';
|
|
4
|
+
import type { KnownLocale } from '../../types';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
locale: KnownLocale;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { locale } = Astro.props as Props;
|
|
11
|
+
const loader = publicBudgetAnalyzer.i18n[locale] ?? publicBudgetAnalyzer.i18n.en;
|
|
12
|
+
const content = loader ? await loader() : undefined;
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
{content ? <SEORenderer content={{ locale, sections: content.seo }} /> : null}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const storageKey = 'jjlmoya-public-budget-analyzer';
|
|
2
|
+
|
|
3
|
+
export interface SavedBudgetState {
|
|
4
|
+
format: 'csv' | 'json';
|
|
5
|
+
text: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function loadSavedBudget(): SavedBudgetState | undefined {
|
|
9
|
+
try {
|
|
10
|
+
const value = window.localStorage.getItem(storageKey);
|
|
11
|
+
if (!value) return undefined;
|
|
12
|
+
const parsed: unknown = JSON.parse(value);
|
|
13
|
+
if (!parsed || typeof parsed !== 'object') return undefined;
|
|
14
|
+
const candidate = parsed as Record<string, unknown>;
|
|
15
|
+
if ((candidate.format !== 'csv' && candidate.format !== 'json') || typeof candidate.text !== 'string') return undefined;
|
|
16
|
+
return { format: candidate.format, text: candidate.text };
|
|
17
|
+
} catch {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function saveBudgetState(state: SavedBudgetState): void {
|
|
23
|
+
try {
|
|
24
|
+
window.localStorage.setItem(storageKey, JSON.stringify(state));
|
|
25
|
+
} catch {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
export interface PublicBudgetUI {
|
|
2
|
+
[key: string]: string;
|
|
3
|
+
uploadLabel: string;
|
|
4
|
+
uploadHelp: string;
|
|
5
|
+
loadExample: string;
|
|
6
|
+
supportedFormat: string;
|
|
7
|
+
tableHeading: string;
|
|
8
|
+
categoryLabel: string;
|
|
9
|
+
amountLabel: string;
|
|
10
|
+
populationLabel: string;
|
|
11
|
+
sourceRowsLabel: string;
|
|
12
|
+
analyzeEmpty: string;
|
|
13
|
+
resultHeading: string;
|
|
14
|
+
totalLabel: string;
|
|
15
|
+
perCapitaLabel: string;
|
|
16
|
+
variationLabel: string;
|
|
17
|
+
topCategoryLabel: string;
|
|
18
|
+
shareLabel: string;
|
|
19
|
+
sourceLabel: string;
|
|
20
|
+
noData: string;
|
|
21
|
+
warningsHeading: string;
|
|
22
|
+
errorsHeading: string;
|
|
23
|
+
methodHeading: string;
|
|
24
|
+
methodText: string;
|
|
25
|
+
limitsHeading: string;
|
|
26
|
+
limitsText: string;
|
|
27
|
+
edgeCasesHeading: string;
|
|
28
|
+
edgeCasesText: string;
|
|
29
|
+
downloadCsv: string;
|
|
30
|
+
printAction: string;
|
|
31
|
+
rowsLabel: string;
|
|
32
|
+
categoriesLabel: string;
|
|
33
|
+
positiveTotalLabel: string;
|
|
34
|
+
previousTotalLabel: string;
|
|
35
|
+
invalidFile: string;
|
|
36
|
+
fileReadError: string;
|
|
37
|
+
exampleName: string;
|
|
38
|
+
noPreviousData: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const ui: PublicBudgetUI = {
|
|
42
|
+
uploadLabel: 'Drop a budget CSV or JSON file here',
|
|
43
|
+
uploadHelp: 'Required fields: category and amount. Optional fields: population, period, priorAmount, currency, and parentCategory.',
|
|
44
|
+
loadExample: 'Load example budget',
|
|
45
|
+
supportedFormat: 'CSV or JSON, processed only in this browser',
|
|
46
|
+
tableHeading: 'Source rows and category totals',
|
|
47
|
+
categoryLabel: 'Category',
|
|
48
|
+
amountLabel: 'Amount',
|
|
49
|
+
populationLabel: 'Population',
|
|
50
|
+
sourceRowsLabel: 'Source rows',
|
|
51
|
+
analyzeEmpty: 'Choose a file or load the example to inspect a budget.',
|
|
52
|
+
resultHeading: 'Budget landscape',
|
|
53
|
+
totalLabel: 'Total spending',
|
|
54
|
+
perCapitaLabel: 'Spending per person',
|
|
55
|
+
variationLabel: 'Change from prior period',
|
|
56
|
+
topCategoryLabel: 'Largest category',
|
|
57
|
+
shareLabel: 'Share of total',
|
|
58
|
+
sourceLabel: 'Source rows',
|
|
59
|
+
noData: 'No valid budget rows are available yet.',
|
|
60
|
+
warningsHeading: 'Warnings to review',
|
|
61
|
+
errorsHeading: 'Rows not included',
|
|
62
|
+
methodHeading: 'Method applied',
|
|
63
|
+
methodText: 'The tool validates the required fields, preserves each source row, groups exact category paths, and calculates totals from the normalized amounts. Category share is amount divided by the total when the total is positive. Per capita spending divides the total by the first consistent positive population denominator. Period change is (current minus prior) divided by prior, and is omitted when the prior amount is zero.',
|
|
64
|
+
limitsHeading: 'What this tool does not do',
|
|
65
|
+
limitsText: 'It does not prove that a budget is complete, comparable, legal, efficient, fair, unbiased, or correctly classified. It does not audit the source, infer policy quality, reconcile currencies, or identify whether a row is an appropriation, obligation, payment, or forecast.',
|
|
66
|
+
edgeCasesHeading: 'Edge cases and data warnings',
|
|
67
|
+
edgeCasesText: 'Mixed currencies, changing populations, duplicate categories, negative adjustments, parent rows beside child rows, locale-specific number formats, and zero prior amounts need human review. The browser keeps the original row number so every aggregate can be traced back to its input.',
|
|
68
|
+
downloadCsv: 'Download reviewed table',
|
|
69
|
+
printAction: 'Print analysis',
|
|
70
|
+
rowsLabel: 'valid rows',
|
|
71
|
+
categoriesLabel: 'categories',
|
|
72
|
+
positiveTotalLabel: 'positive amounts visualized',
|
|
73
|
+
previousTotalLabel: 'Previous total',
|
|
74
|
+
invalidFile: 'The file could not be understood as a non-empty CSV or JSON table.',
|
|
75
|
+
fileReadError: 'The file could not be read in this browser.',
|
|
76
|
+
exampleName: 'Community budget sample',
|
|
77
|
+
noPreviousData: 'No comparable prior-period amount was supplied.',
|
|
78
|
+
};
|
package/src/tools.ts
CHANGED
|
@@ -5,6 +5,8 @@ import { CIVIC_PRIORITY_MATRIX_TOOL } from './tool/civic-priority-matrix';
|
|
|
5
5
|
import { ELECTION_SEAT_APPORTIONMENT_CALCULATOR_TOOL } from './tool/election-seat-apportionment-calculator';
|
|
6
6
|
import { REFERENDUM_THRESHOLD_CALCULATOR_TOOL } from './tool/referendum-threshold-calculator';
|
|
7
7
|
import { VOTE_SEAT_DISPROPORTIONALITY_ANALYZER_TOOL } from './tool/vote-seat-disproportionality-analyzer';
|
|
8
|
+
import { PARTICIPATORY_BUDGET_ALLOCATOR_TOOL } from './tool/participatory-budget-allocator';
|
|
9
|
+
import { PUBLIC_BUDGET_ANALYZER_TOOL } from './tool/public-budget-analyzer';
|
|
8
10
|
|
|
9
11
|
export const ALL_TOOLS: ToolDefinition[] = [
|
|
10
12
|
COALITION_MAJORITY_CALCULATOR_TOOL,
|
|
@@ -12,4 +14,6 @@ export const ALL_TOOLS: ToolDefinition[] = [
|
|
|
12
14
|
ELECTION_SEAT_APPORTIONMENT_CALCULATOR_TOOL,
|
|
13
15
|
REFERENDUM_THRESHOLD_CALCULATOR_TOOL,
|
|
14
16
|
VOTE_SEAT_DISPROPORTIONALITY_ANALYZER_TOOL,
|
|
17
|
+
PARTICIPATORY_BUDGET_ALLOCATOR_TOOL,
|
|
18
|
+
PUBLIC_BUDGET_ANALYZER_TOOL,
|
|
15
19
|
];
|