@jjlmoya/utils-civic 1.7.0 → 1.9.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/.github/workflows/ci.yml +103 -0
- package/.gitignore +1 -0
- package/package.json +17 -5
- package/scripts/postinstall.mjs +2 -4
- package/src/category/index.ts +3 -1
- package/src/components/ProductionBreadcrumb.astro +132 -0
- package/src/components/ProductionWidget.astro +182 -0
- package/src/entries.ts +4 -0
- package/src/i18n/header-ui.ts +15 -0
- package/src/i18n/language-ui.ts +9 -0
- package/src/i18n/languages.ts +24 -0
- package/src/identity/brands.ts +5 -0
- package/src/index.ts +1 -0
- package/src/layouts/ProductionCategoryPage.astro +184 -0
- package/src/layouts/ProductionPage.astro +209 -0
- package/src/layouts/ProductionUtilityPage.astro +235 -0
- package/src/mfe/assets.ts +15 -0
- package/src/mfe/category-ui.ts +25 -0
- package/src/mfe/routes.ts +50 -0
- package/src/pages/[locale]/[utilities]/[categories]/[category]/[slug].astro +100 -0
- package/src/pages/[locale]/[utilities]/[categories]/[category].astro +44 -0
- package/src/pages/index.astro +1 -1
- package/src/pages/utilidades/[slug].astro +90 -0
- package/src/pages/utilidades/categorias/[category].astro +38 -0
- package/src/tests/locale_completeness.test.ts +1 -1
- package/src/tests/tool_validation.test.ts +1 -1
- package/src/tests/translation_copy.test.ts +1 -1
- package/src/tool/access-to-information-request-builder/access-to-information-request-builder.css +577 -0
- package/src/tool/access-to-information-request-builder/bibliography.astro +6 -0
- package/src/tool/access-to-information-request-builder/bibliography.ts +6 -0
- package/src/tool/access-to-information-request-builder/component.astro +106 -0
- package/src/tool/access-to-information-request-builder/contract.test.ts +17 -0
- package/src/tool/access-to-information-request-builder/controller.ts +135 -0
- package/src/tool/access-to-information-request-builder/dom-views.ts +59 -0
- package/src/tool/access-to-information-request-builder/entry.ts +27 -0
- package/src/tool/access-to-information-request-builder/evaluator.ts +27 -0
- package/src/tool/access-to-information-request-builder/i18n/de.ts +52 -0
- package/src/tool/access-to-information-request-builder/i18n/en.ts +76 -0
- package/src/tool/access-to-information-request-builder/i18n/es.ts +52 -0
- package/src/tool/access-to-information-request-builder/i18n/fr.ts +50 -0
- package/src/tool/access-to-information-request-builder/i18n/id.ts +50 -0
- package/src/tool/access-to-information-request-builder/i18n/it.ts +50 -0
- package/src/tool/access-to-information-request-builder/i18n/ja.ts +50 -0
- package/src/tool/access-to-information-request-builder/i18n/ko.ts +50 -0
- package/src/tool/access-to-information-request-builder/i18n/nl.ts +49 -0
- package/src/tool/access-to-information-request-builder/i18n/pl.ts +49 -0
- package/src/tool/access-to-information-request-builder/i18n/pt.ts +49 -0
- package/src/tool/access-to-information-request-builder/i18n/ru.ts +49 -0
- package/src/tool/access-to-information-request-builder/i18n/sv.ts +49 -0
- package/src/tool/access-to-information-request-builder/i18n/tr.ts +49 -0
- package/src/tool/access-to-information-request-builder/i18n/zh.ts +49 -0
- package/src/tool/access-to-information-request-builder/index.ts +11 -0
- package/src/tool/access-to-information-request-builder/logic.test.ts +68 -0
- package/src/tool/access-to-information-request-builder/logic.ts +138 -0
- package/src/tool/access-to-information-request-builder/seo.astro +15 -0
- package/src/tool/access-to-information-request-builder/storage.ts +27 -0
- package/src/tool/access-to-information-request-builder/ui.ts +154 -0
- package/src/tool/access-to-information-request-builder/validation.ts +7 -0
- package/src/tool/parliamentary-voting-analyzer/bibliography.astro +14 -0
- package/src/tool/parliamentary-voting-analyzer/bibliography.ts +6 -0
- package/src/tool/parliamentary-voting-analyzer/component.astro +63 -0
- package/src/tool/parliamentary-voting-analyzer/contract.test.ts +16 -0
- package/src/tool/parliamentary-voting-analyzer/controller.ts +65 -0
- package/src/tool/parliamentary-voting-analyzer/dom-views.ts +63 -0
- package/src/tool/parliamentary-voting-analyzer/entry.ts +27 -0
- package/src/tool/parliamentary-voting-analyzer/evaluator.ts +12 -0
- package/src/tool/parliamentary-voting-analyzer/i18n/de.ts +45 -0
- package/src/tool/parliamentary-voting-analyzer/i18n/en.ts +79 -0
- package/src/tool/parliamentary-voting-analyzer/i18n/es.ts +49 -0
- package/src/tool/parliamentary-voting-analyzer/i18n/fr.ts +43 -0
- package/src/tool/parliamentary-voting-analyzer/i18n/id.ts +45 -0
- package/src/tool/parliamentary-voting-analyzer/i18n/it.ts +45 -0
- package/src/tool/parliamentary-voting-analyzer/i18n/ja.ts +45 -0
- package/src/tool/parliamentary-voting-analyzer/i18n/ko.ts +45 -0
- package/src/tool/parliamentary-voting-analyzer/i18n/nl.ts +45 -0
- package/src/tool/parliamentary-voting-analyzer/i18n/pl.ts +45 -0
- package/src/tool/parliamentary-voting-analyzer/i18n/pt.ts +45 -0
- package/src/tool/parliamentary-voting-analyzer/i18n/ru.ts +45 -0
- package/src/tool/parliamentary-voting-analyzer/i18n/sv.ts +45 -0
- package/src/tool/parliamentary-voting-analyzer/i18n/tr.ts +45 -0
- package/src/tool/parliamentary-voting-analyzer/i18n/zh.ts +45 -0
- package/src/tool/parliamentary-voting-analyzer/index.ts +11 -0
- package/src/tool/parliamentary-voting-analyzer/logic.test.ts +52 -0
- package/src/tool/parliamentary-voting-analyzer/logic.ts +279 -0
- package/src/tool/parliamentary-voting-analyzer/parliamentary-voting-analyzer.css +491 -0
- package/src/tool/parliamentary-voting-analyzer/seo.astro +14 -0
- package/src/tool/parliamentary-voting-analyzer/storage.ts +25 -0
- package/src/tool/parliamentary-voting-analyzer/ui.ts +98 -0
- package/src/tools.ts +4 -0
- package/src/types.ts +2 -4
- package/src/worker.ts +9 -0
- package/tsconfig.json +14 -5
- package/src/pages/[locale]/[slug].astro +0 -166
- package/src/pages/[locale].astro +0 -253
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
|
|
2
|
+
import { bibliography } from '../bibliography';
|
|
3
|
+
import type { AccessRequestLocaleContent } from '../entry';
|
|
4
|
+
import type { AccessRequestUI } from '../ui';
|
|
5
|
+
|
|
6
|
+
const ui: AccessRequestUI = {
|
|
7
|
+
eyebrow: 'CIVIC PAPER TRAIL', introHeading: 'Forma frågan innan du skickar den.', introText: 'Börja med mottagaren, ange en begärbar informationsmängd per rad och lägg sedan till gränserna som gör handlingen sökbar.', targetHeading: 'Ange mottagare', targetPrompt: 'Vem kan svara?', questionPrompt: 'En mängd i taget', boundaryHeading: 'Dra gränserna', boundaryPrompt: 'Vad gör den sökbar?', reviewWarningsHeading: 'Granska före utskick', recipientLabel: 'Myndighet eller mottagare', recipientPlaceholder: 'Avdelning, myndighet, arkiv eller organisation', subjectLabel: 'Ämne', subjectPlaceholder: 'Handlingarna eller uppgifterna du vill hitta', itemsLabel: 'Begärda uppgifter', itemPlaceholder: 'Be om en identifierbar informationsmängd, handling eller kolumn', addItem: 'Lägg till en begäransrad', removeItem: 'Ta bort raden', periodLabel: 'Tidsperiod', periodFrom: 'Från', periodTo: 'Till', geographyLabel: 'Geografiskt område', geographyPlaceholder: 'Stad, distrikt, plats, programområde eller hela landet', formatLabel: 'Önskat format', formatPlaceholder: 'Välj leveransformat', formatEmail: 'Elektronisk kopia via e-post', formatCsv: 'Maskinläsbar tabell som CSV', formatPdf: 'Sökbar PDF eller handling', formatOriginal: 'Ursprungligt format som myndigheten har', deliveryLabel: 'Leveransuppgifter', deliveryPlaceholder: 'E-post, postadress eller säkert sätt att svara', contactLabel: 'Valfri kontakt', contactPlaceholder: 'Telefon, referensnummer eller önskad svarskanal', attachmentsLabel: 'Bilagor eller identifierare', attachmentsPlaceholder: 'Filnamn, diarienummer, länkar eller sammanhang som hjälper sökningen', noAttachments: 'Inga bilagor eller identifierare behövs', savedCopy: 'Jag har sparat en kopia av den slutliga texten', presetLabel: 'Börja med ett avgränsat exempel', presetRecords: 'Möteshandlingar', presetSpending: 'Utgiftshandlingar', presetMeetings: 'Klagomål på tjänster', generateAction: 'Skapa utkast', resetAction: 'Rensa utkast', resultHeading: 'Utkast till begäran', resultIntro: 'Kontrollera alla fakta och redigera sluttexten innan du kopierar. Råd och varningar ligger utanför texten du skickar.', copyAction: 'Kopiera Markdown', printAction: 'Skriv ut eller spara som PDF', copied: 'Kopierad till urklipp', noResult: 'Lägg till mottagare, ämne och minst en rad för att se spåret.', checklistHeading: 'Spårbarhet före utskick', completeLabel: 'Klar', reviewLabel: 'Granska', checklistRecipient: 'Mottagare angiven', checklistScope: 'Varje rad anger en begärbar mängd', checklistPeriod: 'Tidsperiod avgränsad', checklistFormat: 'Önskat format angivet', checklistAttachments: 'Bilagor och identifierare beaktade', checklistCopy: 'Kopia sparad för egna anteckningar', methodHeading: 'Tillämpad metod', methodText: 'Byggaren gör ett brett ämne till en spårbar begäran: en informationsmängd per rad, en namngiven mottagare, ett tydligt datumintervall, en geografisk gräns, ett önskat format och en användbar svarsväg. Den ordnar bara det du skriver och lägger aldrig till rättslig grund, tidsfrist, myndighet eller fakta.', limitsHeading: 'Vad verktyget inte avgör', limitsText: 'Det identifierar inte behörig myndighet, fastställer ingen lagstadgad tidsfrist, garanterar inte utlämnande, skickar inte begäran, klassificerar inte handlingar enligt lokal lag och ger inte juridisk rådgivning. Kontrollera mottagarens aktuella instruktioner.', edgeCasesHeading: 'Specialfall och datavarningar', edgeCasesText: 'Breda uttryck som "alla dokument" kan vara svåra att söka. Undvik odefinierade termer, blandade betydelser av datum, onödiga personuppgifter och önskemål om en ny analys när du behöver en befintlig handling. Alla format kanske inte finns.', statusReady: 'Utkastet innehåller ett komplett minimispår.', statusReview: 'Utkastet fungerar som startpunkt men behöver granskas.', missingRecipient: 'Ange myndighet eller mottagare före utskick.', missingItems: 'Lägg till minst en specifik informationsmängd.', missingPeriod: 'Lägg till start- och slutdatum eller förklara gränsen som saknas.', broadItem: 'Den här raden kan vara för bred. Ange handlingstyp, fält, händelse eller mätbar mängd.', missingFormat: 'Välj hur uppgifterna ska levereras.', missingGeography: 'Lägg till en plats eller förklara varför begäran gäller hela landet.', missingDelivery: 'Lägg till en väg som mottagaren kan använda för att svara.', noCopyWarning: 'Spara sluttext och uppgifter om utskicket före sändning.', itemNumber: 'Begäransrad',
|
|
8
|
+
};
|
|
9
|
+
const softwareApplication: SoftwareApplication = { '@type': 'SoftwareApplication', name: 'Byggare för begäran om information', applicationCategory: 'UtilitiesApplication', operatingSystem: 'Any', description: 'Skapa en exakt och spårbar informationsbegäran med omfattning, datum, format och svarsväg.', url: 'https://gamebob.dev/sv/byggare-begaran-om-informationsatkomst', offers: { '@type': 'Offer', price: '0', priceCurrency: 'USD' } };
|
|
10
|
+
const faqPage: FAQPage = { '@type': 'FAQPage', mainEntity: [
|
|
11
|
+
{ '@type': 'Question', name: 'Vad bör jag begära?', acceptedAnswer: { '@type': 'Answer', text: 'Be om en identifierbar informationsmängd, handling, kolumn eller mätbar räkning. Dela upp fristående mängder i olika rader.' } },
|
|
12
|
+
{ '@type': 'Question', name: 'Vet verktyget vilken myndighet jag ska kontakta?', acceptedAnswer: { '@type': 'Answer', text: 'Nej. Du måste hitta den troliga mottagaren och kontrollera aktuella instruktioner. Byggaren avgör inte behörighet, tidsfrister eller undantag.' } },
|
|
13
|
+
{ '@type': 'Question', name: 'Varför spelar datum och geografiskt område roll?', acceptedAnswer: { '@type': 'Answer', text: 'De avgränsar sökningen och minskar oklarheter. Använd samma betydelse för datumen och ange plats eller program.' } },
|
|
14
|
+
{ '@type': 'Question', name: 'Kan jag begära personliga eller känsliga uppgifter?', acceptedAnswer: { '@type': 'Answer', text: 'Ta bara med uppgifter som behövs och är lämpliga. Reglerna skiljer sig åt, så kontrollera mottagarens vägledning.' } },
|
|
15
|
+
] };
|
|
16
|
+
const howTo: HowTo = { '@type': 'HowTo', name: 'Skriva en exakt informationsbegäran', step: [
|
|
17
|
+
{ '@type': 'HowToStep', name: 'Namnge mottagaren', text: 'Skriv in myndigheten, avdelningen, arkivet eller organisationen som troligen har uppgifterna och kontrollera aktuell kanal.' },
|
|
18
|
+
{ '@type': 'HowToStep', name: 'Dela uppgifterna', text: 'Skriv en begärbar mängd per rad. Ange handlingstyp, fält, händelse eller antal.' },
|
|
19
|
+
{ '@type': 'HowToStep', name: 'Avgränsa sökningen', text: 'Lägg till startdatum, slutdatum, geografiskt område och identifierare.' },
|
|
20
|
+
{ '@type': 'HowToStep', name: 'Välj leverans', text: 'Ange önskat format och en pålitlig svarsväg utan att anta att en ny fil skapas.' },
|
|
21
|
+
{ '@type': 'HowToStep', name: 'Granska och spara en kopia', text: 'Läs Markdown, hantera varningarna, spara text och utskicksuppgifter och använd den verifierade kanalen.' },
|
|
22
|
+
] };
|
|
23
|
+
export const content: AccessRequestLocaleContent = { slug: 'byggare-begaran-om-informationsatkomst', title: 'Byggare för begäran om information', description: 'Skriv en exakt informationsbegäran med mottagare, tydliga rader, datum, geografiskt område, format och checklista.', ui, seo: [
|
|
24
|
+
{ type: 'title', text: 'Skriv en informationsbegäran som går att hitta', level: 2 },
|
|
25
|
+
{ type: 'paragraph', html: 'En begäran kan vara artig och ändå svår att besvara när ämnet är brett, datum saknas eller olika frågor blandas. Byggaren gör kända detaljer till ett kontrollerbart spår: mottagare, sökta handlingar, period, plats och svarssätt.' },
|
|
26
|
+
{ type: 'title', text: 'Ge sökningen en gräns', level: 2 },
|
|
27
|
+
{ type: 'paragraph', html: 'Använd samma betydelse för periodens båda datum, till exempel publicering, möte eller betalning. Lägg till plats, program, anläggning eller organisation. Om en gräns verkligen är okänd, visa det i checklistan och gissa inte.' },
|
|
28
|
+
{ type: 'table', headers: ['Detalj', 'Användbar formulering', 'Beslut som stöds'], rows: [['Informationsmängd', 'Slutliga dagordningar och godkända protokoll', 'Vilka handlingar ska sökas?'], ['Period', 'Från 2025-01-01 till 2025-12-31', 'Vilka handlingar ingår?'], ['Geografiskt område', 'Norra distriktets serviceområde', 'Vilken plats eller vilket program?'], ['Format', 'Maskinläsbar tabell som CSV', 'Hur kan resultatet kontrolleras?']] },
|
|
29
|
+
{ type: 'title', text: 'Granska utkastet före utskick', level: 2 },
|
|
30
|
+
{ type: 'paragraph', html: 'Den skapade texten håller dina fakta samlade och placerar råd i en separat granskningsrad. En komplett punkt betyder att miniminivån finns; en varning ber dig ange handling, fält, händelse eller mätbar mängd tydligare.' },
|
|
31
|
+
{ type: 'list', items: ['Bekräfta att mottagaren tar emot denna typ av begäran nu.', 'Kontrollera att varje rad gäller en identifierbar mängd och inte ett helt ämne.', 'Använd en sluten period och en geografisk gräns som passar frågan.', 'Ange format utan att anta att myndigheten måste skapa en ny fil.', 'Spara bilagor, identifierare, sluttext och utskickskanal.'] },
|
|
32
|
+
{ type: 'tip', title: 'Specifik betyder inte fullständig', html: 'En kort begäran med handlingstyp, period och bra identifierare är ofta lättare att söka än en lång berättelse. Behåll sammanhang som hjälper till att hitta handlingarna.' },
|
|
33
|
+
{ type: 'title', text: 'Känn till verktygets gränser', level: 2 },
|
|
34
|
+
{ type: 'paragraph', html: 'Detta är ett landoberoende skrivstöd. Det identifierar inte myndighet, beräknar inte tidsfrist, garanterar inte utlämnande, skickar inte begäran och ger ingen juridisk rådgivning. Mottagarens aktuella regler gäller.' },
|
|
35
|
+
{ type: 'tip', title: 'Spara beviskedjan', html: 'Spara den exakta texten, datum, mottagare, bilagor och kvittens. Då kan du jämföra svaret med frågan du faktiskt skickade.' },
|
|
36
|
+
{ type: 'title', text: 'Förbereda ett användbart svar', level: 2 },
|
|
37
|
+
{ type: 'paragraph', html: 'Ange de fält eller den handlingstyp som behövs och spara begäran tillsammans med alla svar du får.' },
|
|
38
|
+
], faq: [
|
|
39
|
+
{ question: 'Vad bör jag begära?', answer: 'Be om en identifierbar handling, kolumn, informationsmängd eller räkning och dela upp fristående mängder.' },
|
|
40
|
+
{ question: 'Vet verktyget vilken myndighet som är rätt?', answer: 'Nej. Kontrollera mottagare, behörighet, tidsfrist, undantag och kanal i aktuella lokala instruktioner.' },
|
|
41
|
+
{ question: 'Varför är datum och plats viktiga?', answer: 'De avgränsar sökningen. Använd samma betydelse för datumen och ange plats eller program.' },
|
|
42
|
+
{ question: 'Kan jag begära känsliga uppgifter?', answer: 'Ta bara med nödvändiga uppgifter och kontrollera aktuella regler om tillgång och integritet.' },
|
|
43
|
+
], bibliography, howTo: [
|
|
44
|
+
{ name: 'Namnge mottagaren', text: 'Skriv in den troliga myndigheten och kontrollera aktuell kanal.' },
|
|
45
|
+
{ name: 'Dela uppgifterna', text: 'Skriv en identifierbar handling, mängd eller räkning per rad.' },
|
|
46
|
+
{ name: 'Avgränsa sökningen', text: 'Lägg till konsekventa datum, område och identifierare.' },
|
|
47
|
+
{ name: 'Välj leverans', text: 'Ange format och svarsväg utan att anta en ny fil.' },
|
|
48
|
+
{ name: 'Granska och spara en kopia', text: 'Hantera varningar och spara text tillsammans med utskicksuppgifterna.' },
|
|
49
|
+
], schemas: [softwareApplication, faqPage, howTo] as unknown as Record<string, unknown>[] };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
|
|
2
|
+
import { bibliography } from '../bibliography';
|
|
3
|
+
import type { AccessRequestLocaleContent } from '../entry';
|
|
4
|
+
import type { AccessRequestUI } from '../ui';
|
|
5
|
+
|
|
6
|
+
const ui: AccessRequestUI = {
|
|
7
|
+
eyebrow: 'CIVIC PAPER TRAIL', introHeading: 'Soruyu göndermeden önce şekillendirin.', introText: 'Alıcıyla başlayın, her satıra istenebilir tek bir bilgi kümesi yazın ve kaydı bulunabilir kılan sınırları ekleyin.', targetHeading: 'Alıcıyı belirleyin', targetPrompt: 'Kim yanıtlayabilir?', questionPrompt: 'Her seferinde bir küme', boundaryHeading: 'Sınırları çizin', boundaryPrompt: 'Onu bulunabilir yapan nedir?', reviewWarningsHeading: 'Göndermeden önce gözden geçirin', recipientLabel: 'Kurum veya alıcı', recipientPlaceholder: 'Birim, kurum, arşiv veya kuruluş', subjectLabel: 'Konu', subjectPlaceholder: 'Bulmak istediğiniz kayıt veya bilgiler', itemsLabel: 'İstenen bilgiler', itemPlaceholder: 'Tanımlanabilir tek bir bilgi kümesi, kayıt veya alan isteyin', addItem: 'İstek satırı ekle', removeItem: 'Bu satırı kaldır', periodLabel: 'Zaman aralığı', periodFrom: 'Başlangıç', periodTo: 'Bitiş', geographyLabel: 'Coğrafi kapsam', geographyPlaceholder: 'Şehir, ilçe, tesis, program alanı veya ülke geneli', formatLabel: 'Tercih edilen biçim', formatPlaceholder: 'Teslim biçimi seçin', formatEmail: 'E-posta ile elektronik kopya', formatCsv: 'CSV gibi makine tarafından okunabilir tablo', formatPdf: 'Aranabilir PDF veya belge', formatOriginal: 'Kurumun elindeki özgün biçim', deliveryLabel: 'Teslim ayrıntıları', deliveryPlaceholder: 'E-posta, posta adresi veya güvenli yanıt yolu', contactLabel: 'İsteğe bağlı iletişim', contactPlaceholder: 'Telefon, referans numarası veya tercih edilen yanıt kanalı', attachmentsLabel: 'Ekler veya tanımlayıcılar', attachmentsPlaceholder: 'Dosya adları, kayıt numaraları, bağlantılar veya aramaya yardımcı bağlam', noAttachments: 'Ek veya tanımlayıcı gerekmiyor', savedCopy: 'Son metnin bir kopyasını kaydettim', presetLabel: 'Odaklanmış bir örnekle başlayın', presetRecords: 'Toplantı kayıtları', presetSpending: 'Harcama kayıtları', presetMeetings: 'Hizmet şikâyetleri', generateAction: 'İstek taslağı oluştur', resetAction: 'Taslağı temizle', resultHeading: 'İstek taslağı', resultIntro: 'Her bilgiyi kontrol edin ve kopyalamadan önce son metni düzenleyin. Rehber ve uyarılar göndereceğiniz metnin dışında kalır.', copyAction: 'Markdown kopyala', printAction: 'Yazdır veya PDF kaydet', copied: 'Panoya kopyalandı', noResult: 'İzi görmek için alıcı, konu ve en az bir istek satırı ekleyin.', checklistHeading: 'Göndermeden önce izlenebilirlik', completeLabel: 'Hazır', reviewLabel: 'Gözden geçir', checklistRecipient: 'Alıcı belirtildi', checklistScope: 'Her satır bir istenebilir kümeyi tanımlıyor', checklistPeriod: 'Zaman aralığı sınırlandı', checklistFormat: 'Tercih edilen biçim belirtildi', checklistAttachments: 'Ekler ve tanımlayıcılar hesaba katıldı', checklistCopy: 'Kayıt için kopya saklandı', methodHeading: 'Uygulanan yöntem', methodText: 'Oluşturucu, geniş bir konuyu izlenebilir bir isteğe dönüştürür: satır başına bir bilgi kümesi, adı verilmiş bir alıcı, açık tarih aralığı, coğrafi sınır, tercih edilen biçim ve kullanılabilir yanıt yolu. Yalnızca verdiğiniz ayrıntıları düzenler; hukuki dayanak, süre, kurum veya olgu eklemez.', limitsHeading: 'Bu aracın karar vermediği konular', limitsText: 'Yetkili kurumu belirlemez, yasal süre hesaplamaz, açıklamayı garanti etmez, isteği göndermez, kaydı yerel hukuka göre sınıflandırmaz ve hukuki danışmanlık vermez. Göndermeden önce alıcının güncel talimatlarını kontrol edin.', edgeCasesHeading: 'Özel durumlar ve veri uyarıları', edgeCasesText: '"Tüm belgeler" gibi geniş ifadeleri aramak zor olabilir. Tanımsız terimlerden, farklı tarih anlamlarını karıştırmaktan, gereksiz kişisel verilerden ve mevcut kayıt yerine yeni analiz istemekten kaçının. Her biçim kullanılamayabilir.', statusReady: 'Taslak gerekli asgari izi eksiksiz içeriyor.', statusReview: 'Taslak başlangıç için kullanılabilir ancak incelenmeli.', missingRecipient: 'Göndermeden önce kurumu veya alıcıyı yazın.', missingItems: 'En az bir özel bilgi kümesi ekleyin.', missingPeriod: 'Başlangıç ve bitiş tarihi ekleyin veya eksik sınırı açıklayın.', broadItem: 'Bu satır fazla geniş olabilir. Kayıt türünü, alanı, olayı veya ölçülebilir kümeyi belirtin.', missingFormat: 'Bilginin nasıl teslim edileceğini seçin.', missingGeography: 'Bir yer ekleyin veya isteğin neden ülke genelinde olduğunu açıklayın.', missingDelivery: 'Alıcının yanıt verebileceği bir yol ekleyin.', noCopyWarning: 'Göndermeden önce son metni ve başvuru ayrıntılarını kaydedin.', itemNumber: 'İstek satırı',
|
|
8
|
+
};
|
|
9
|
+
const softwareApplication: SoftwareApplication = { '@type': 'SoftwareApplication', name: 'Bilgi edinme talebi oluşturucu', applicationCategory: 'UtilitiesApplication', operatingSystem: 'Any', description: 'Kapsamı, tarihleri, biçimi ve yanıt yolunu belirli, izlenebilir bir bilgi talebi hazırlayın.', url: 'https://gamebob.dev/tr/bilgi-edinme-talebi-olusturucu', offers: { '@type': 'Offer', price: '0', priceCurrency: 'USD' } };
|
|
10
|
+
const faqPage: FAQPage = { '@type': 'FAQPage', mainEntity: [
|
|
11
|
+
{ '@type': 'Question', name: 'Ne istemeliyim?', acceptedAnswer: { '@type': 'Answer', text: 'Tanımlanabilir bir bilgi kümesi, kayıt, alan veya ölçülebilir sayı isteyin. Bağımsız kümeleri ayrı satırlara bölün.' } },
|
|
12
|
+
{ '@type': 'Question', name: 'Araç hangi kuruma başvuracağımı biliyor mu?', acceptedAnswer: { '@type': 'Answer', text: 'Hayır. Muhtemel alıcıyı bulmalı ve güncel talimatlarını kontrol etmelisiniz. Oluşturucu yetkiyi, süreyi veya istisnaları belirlemez.' } },
|
|
13
|
+
{ '@type': 'Question', name: 'Tarih ve coğrafi kapsam neden önemlidir?', acceptedAnswer: { '@type': 'Answer', text: 'Arama sınırını daraltır ve belirsizliği azaltır. Tarihlere aynı anlamı verin ve yeri veya programı belirtin.' } },
|
|
14
|
+
{ '@type': 'Question', name: 'Kişisel veya hassas bilgi isteyebilir miyim?', acceptedAnswer: { '@type': 'Answer', text: 'Yalnızca gerekli ve uygun ayrıntıları ekleyin. Erişim ve gizlilik kuralları değişir, bu nedenle alıcının rehberini inceleyin.' } },
|
|
15
|
+
] };
|
|
16
|
+
const howTo: HowTo = { '@type': 'HowTo', name: 'Belirli bir bilgi talebi hazırlama', step: [
|
|
17
|
+
{ '@type': 'HowToStep', name: 'Alıcıyı yazın', text: 'Bilgiyi tuttuğunu düşündüğünüz kurumu, birimi, arşivi veya kuruluşu girin ve güncel başvuru kanalını doğrulayın.' },
|
|
18
|
+
{ '@type': 'HowToStep', name: 'Bilgiyi ayırın', text: 'Her satıra istenebilir bir küme yazın. Kayıt türünü, alanları, olayı veya sayıyı belirtin.' },
|
|
19
|
+
{ '@type': 'HowToStep', name: 'Aramayı sınırlandırın', text: 'Başlangıç ve bitiş tarihlerini, coğrafi kapsamı ve tanımlayıcıları ekleyin.' },
|
|
20
|
+
{ '@type': 'HowToStep', name: 'Teslim ayrıntılarını seçin', text: 'Tercih edilen biçimi ve güvenilir yanıt yolunu belirtin; yeni bir dosya oluşturulacağını varsaymayın.' },
|
|
21
|
+
{ '@type': 'HowToStep', name: 'İnceleyin ve kopya saklayın', text: 'Markdown metnini okuyun, uyarıları çözün, metni ve başvuru ayrıntılarını saklayın ve doğrulanmış kanalı kullanın.' },
|
|
22
|
+
] };
|
|
23
|
+
export const content: AccessRequestLocaleContent = { slug: 'bilgi-edinme-talebi-olusturucu', title: 'Bilgi edinme talebi oluşturucu', description: 'Alıcı, belirli istek satırları, tarihler, coğrafi kapsam, biçim ve gönderim öncesi kontrol listesiyle bilgi talebi hazırlayın.', ui, seo: [
|
|
24
|
+
{ type: 'title', text: 'Bulunabilir bir bilgi talebi yazın', level: 2 },
|
|
25
|
+
{ type: 'paragraph', html: 'Konu genişse, tarihler yoksa veya farklı sorular birleştirilmişse bir talep kibar olsa bile yanıtlanması zor olabilir. Bu oluşturucu, bildiğiniz ayrıntıları alıcı, aranan kayıtlar, dönem, yer ve yanıt biçimiyle denetlenebilir bir ize dönüştürür.' },
|
|
26
|
+
{ type: 'title', text: 'Aramaya sınır koyun', level: 2 },
|
|
27
|
+
{ type: 'paragraph', html: 'Dönemin iki ucunda yayımlanma, toplantı veya ödeme tarihi gibi aynı anlamı kullanın. Yer, program, tesis veya kuruluş ekleyin. Sınır gerçekten bilinmiyorsa kontrol listesinde görünür bırakın ve tahmin etmeyin.' },
|
|
28
|
+
{ type: 'table', headers: ['Ayrıntı', 'Yararlı ifade', 'Desteklediği karar'], rows: [['Bilgi kümesi', 'Son gündemler ve onaylı tutanaklar', 'Hangi kayıtlar aranmalı?'], ['Dönem', '2025-01-01 ile 2025-12-31 arası', 'Hangi kayıtlar dahil?'], ['Coğrafi kapsam', 'Kuzey bölgesi hizmet alanı', 'Hangi yer veya program?'], ['Biçim', 'CSV gibi makine tarafından okunabilir tablo', 'Sonuç nasıl kontrol edilir?']] },
|
|
29
|
+
{ type: 'title', text: 'Göndermeden önce taslağı inceleyin', level: 2 },
|
|
30
|
+
{ type: 'paragraph', html: 'Oluşturulan metin olgularınızı bir arada tutar ve rehberi ayrı bir inceleme alanına koyar. Tamamlanan madde asgari ayrıntının bulunduğunu, uyarı ise kayıt, alan, olay veya ölçülebilir kümeyi daha açık yazmanız gerektiğini gösterir.' },
|
|
31
|
+
{ type: 'list', items: ['Alıcının şu anda bu tür talepleri kabul ettiğini doğrulayın.', 'Her satırın bütün bir konu yerine tek bir tanımlanabilir küme istediğini kontrol edin.', 'Kapalı bir tarih aralığı ve soruyla uyumlu coğrafi sınır kullanın.', 'Kurumun yeni bir dosya oluşturmak zorunda olduğunu varsaymadan biçim belirtin.', 'Ekleri, tanımlayıcıları, son metni ve başvuru kanalını kaydedin.'] },
|
|
32
|
+
{ type: 'tip', title: 'Belirli olmak eksiksiz olmak değildir', html: 'Kayıt türü, dönem ve tanımlayıcıları olan kısa bir talep uzun bir anlatıdan daha kolay aranabilir. Yalnızca kaydı bulmaya yardımcı olan bağlamı saklayın.' },
|
|
33
|
+
{ type: 'title', text: 'Oluşturucunun sınırlarını bilin', level: 2 },
|
|
34
|
+
{ type: 'paragraph', html: 'Bu, ülkeye özgü olmayan bir yazım yardımcısıdır. Yetkili kurumu, yasal süreyi, açıklamayı, biçimi veya başvuru yöntemini belirlemez ve hukuki danışmanlık vermez. Bu konularda alıcının güncel kuralları geçerlidir.' },
|
|
35
|
+
{ type: 'tip', title: 'Kanıt izini saklayın', html: 'Gönderdiğiniz metni, tarihi, alıcıyı, ekleri ve alındıyı kaydedin. Böylece yanıtı gerçekten sorduğunuz soruyla karşılaştırabilirsiniz.' },
|
|
36
|
+
{ type: 'title', text: 'Kullanılabilir bir yanıta hazırlanma', level: 2 },
|
|
37
|
+
{ type: 'paragraph', html: 'Amacınız için gerekli alanları veya kayıt türünü belirtin ve talep ile aldığınız yanıtları birlikte saklayın.' },
|
|
38
|
+
], faq: [
|
|
39
|
+
{ question: 'Ne istemeliyim?', answer: 'Tanımlanabilir bir kayıt, alan, bilgi kümesi veya sayı isteyin ve bağımsız kümeleri ayırın.' },
|
|
40
|
+
{ question: 'Araç doğru kurumu biliyor mu?', answer: 'Hayır. Güncel yerel talimatlarla alıcıyı, yetkiyi, süreyi, istisnaları ve kanalı kontrol edin.' },
|
|
41
|
+
{ question: 'Tarih ve yer neden önemli?', answer: 'Aramayı sınırlar. Tarih anlamlarını eşleştirin ve yeri veya programı belirtin.' },
|
|
42
|
+
{ question: 'Hassas bilgi isteyebilir miyim?', answer: 'Yalnızca gerekli ayrıntıları ekleyin ve güncel erişim ile gizlilik kurallarını inceleyin.' },
|
|
43
|
+
], bibliography, howTo: [
|
|
44
|
+
{ name: 'Alıcıyı yazın', text: 'Muhtemel kurumu girin ve güncel kanalını doğrulayın.' },
|
|
45
|
+
{ name: 'Bilgiyi ayırın', text: 'Her satıra bir kayıt, alan veya tanımlanabilir küme yazın.' },
|
|
46
|
+
{ name: 'Aramayı sınırlandırın', text: 'Uygun tarihleri, kapsamı ve tanımlayıcıları ekleyin.' },
|
|
47
|
+
{ name: 'Teslim ayrıntılarını seçin', text: 'Yeni dosya varsaymadan biçim ve yanıt yolunu belirtin.' },
|
|
48
|
+
{ name: 'İnceleyin ve kopya saklayın', text: 'Uyarıları çözün ve metni başvuru bilgileriyle kaydedin.' },
|
|
49
|
+
], schemas: [softwareApplication, faqPage, howTo] as unknown as Record<string, unknown>[] };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
|
|
2
|
+
import { bibliography } from '../bibliography';
|
|
3
|
+
import type { AccessRequestLocaleContent } from '../entry';
|
|
4
|
+
import type { AccessRequestUI } from '../ui';
|
|
5
|
+
|
|
6
|
+
const ui: AccessRequestUI = {
|
|
7
|
+
eyebrow: 'CIVIC PAPER TRAIL', introHeading: '发送前先把问题说清楚。', introText: '先填写接收方,每行写一个可请求的信息集合,再补充帮助查找记录的边界。', targetHeading: '确定接收方', targetPrompt: '谁可以回答?', questionPrompt: '一次一个集合', boundaryHeading: '划定边界', boundaryPrompt: '什么能让它容易找到?', reviewWarningsHeading: '发送前检查', recipientLabel: '机构或接收方', recipientPlaceholder: '部门、机关、档案馆或组织', subjectLabel: '主题', subjectPlaceholder: '你想查找的记录或信息', itemsLabel: '请求的信息', itemPlaceholder: '请求一个可识别的信息集合、记录或字段', addItem: '添加请求行', removeItem: '删除此行', periodLabel: '时间范围', periodFrom: '开始', periodTo: '结束', geographyLabel: '地理范围', geographyPlaceholder: '城市、地区、地点、项目区域或全国', formatLabel: '首选格式', formatPlaceholder: '选择交付格式', formatEmail: '通过电子邮件提供电子副本', formatCsv: 'CSV等机器可读表格', formatPdf: '可搜索的PDF或文档', formatOriginal: '机构持有的原始格式', deliveryLabel: '交付信息', deliveryPlaceholder: '电子邮箱、邮寄地址或安全的回复方式', contactLabel: '可选联系方式', contactPlaceholder: '电话、参考编号或首选回复渠道', attachmentsLabel: '附件或标识', attachmentsPlaceholder: '文件名、记录编号、链接或帮助定位的背景', noAttachments: '不需要附件或标识', savedCopy: '我已保存最终文本的副本', presetLabel: '从具体示例开始', presetRecords: '会议记录', presetSpending: '支出记录', presetMeetings: '服务投诉', generateAction: '生成请求草稿', resetAction: '清除草稿', resultHeading: '请求草稿', resultIntro: '复制前检查每个事实并编辑最终文本。指南和警告不会进入你发送的文本。', copyAction: '复制Markdown', printAction: '打印或保存为PDF', copied: '已复制到剪贴板', noResult: '添加接收方、主题和至少一行请求后即可看到记录线索。', checklistHeading: '发送前的可追溯性', completeLabel: '准备好了', reviewLabel: '检查', checklistRecipient: '已填写接收方', checklistScope: '每行都指向一个可请求的集合', checklistPeriod: '已限定时间范围', checklistFormat: '已说明首选格式', checklistAttachments: '已考虑附件和标识', checklistCopy: '已保存记录副本', methodHeading: '采用的方法', methodText: '这个工具把宽泛的主题变成可追溯的请求:每行一个信息集合、明确的接收方、清晰的日期范围、地理边界、首选格式和可用的回复路径。它只整理你提供的细节,不会添加法律依据、期限、机构或事实判断。', limitsHeading: '这个工具不会替你决定什么', limitsText: '它不会确定主管机关、计算法定期限、保证公开、提交请求、依据当地法律给记录分类,也不提供法律建议。发送前请查看接收机构的最新说明。', edgeCasesHeading: '特殊情况和数据提醒', edgeCasesText: '"所有文件"这类宽泛说法可能很难搜索。避免未定义的术语、含义混杂的日期、不必要的个人信息,以及在需要现有记录时请求机构进行新分析。某些格式可能无法提供。', statusReady: '草稿已包含完整的最低限度线索。', statusReview: '草稿可以作为起点,但仍需要检查。', missingRecipient: '发送前请填写机构或接收方。', missingItems: '请至少添加一个具体的信息集合。', missingPeriod: '请添加开始和结束日期,或说明缺少的边界。', broadItem: '这一行可能过于宽泛。请写明记录类型、字段、事件或可衡量的集合。', missingFormat: '请选择信息的交付方式。', missingGeography: '请添加地点,或说明请求为何适用于全国。', missingDelivery: '请添加接收方可以用来回复的路径。', noCopyWarning: '发送前请保存最终文本和提交信息。', itemNumber: '请求行',
|
|
8
|
+
};
|
|
9
|
+
const softwareApplication: SoftwareApplication = { '@type': 'SoftwareApplication', name: '信息公开请求生成器', applicationCategory: 'UtilitiesApplication', operatingSystem: 'Any', description: '创建范围、日期、格式和回复路径清晰且可追溯的信息请求。', url: 'https://gamebob.dev/zh/access-to-information-request-builder', offers: { '@type': 'Offer', price: '0', priceCurrency: 'USD' } };
|
|
10
|
+
const faqPage: FAQPage = { '@type': 'FAQPage', mainEntity: [
|
|
11
|
+
{ '@type': 'Question', name: '我应该请求什么?', acceptedAnswer: { '@type': 'Answer', text: '请求一个可识别的信息集合、记录、字段或可衡量的数量。把无关的集合分到不同请求行中。' } },
|
|
12
|
+
{ '@type': 'Question', name: '这个工具知道该联系哪个机构吗?', acceptedAnswer: { '@type': 'Answer', text: '不知道。你需要自己确认可能的接收方并查看最新说明。工具不会判断管辖范围、期限或例外。' } },
|
|
13
|
+
{ '@type': 'Question', name: '为什么日期和地理范围很重要?', acceptedAnswer: { '@type': 'Answer', text: '它们可以缩小搜索范围并减少歧义。两端日期使用相同含义,并写明地点或项目范围。' } },
|
|
14
|
+
{ '@type': 'Question', name: '可以请求个人或敏感信息吗?', acceptedAnswer: { '@type': 'Answer', text: '只加入必要且适当的细节。访问和隐私规则因地区而异,请先查看接收方的指引。' } },
|
|
15
|
+
] };
|
|
16
|
+
const howTo: HowTo = { '@type': 'HowTo', name: '撰写明确的信息请求', step: [
|
|
17
|
+
{ '@type': 'HowToStep', name: '写明接收方', text: '输入可能持有信息的机构、部门、档案馆或组织,并核实当前的请求渠道。' },
|
|
18
|
+
{ '@type': 'HowToStep', name: '拆分信息', text: '每行写一个可请求的集合,注明记录类型、字段、事件或数量。' },
|
|
19
|
+
{ '@type': 'HowToStep', name: '限定搜索', text: '添加开始日期、结束日期、地理范围以及帮助定位材料的标识。' },
|
|
20
|
+
{ '@type': 'HowToStep', name: '选择交付细节', text: '写明首选格式和可靠的回复路径,但不要假定机构必须创建新文件。' },
|
|
21
|
+
{ '@type': 'HowToStep', name: '检查并保存副本', text: '阅读Markdown,处理检查项,保存文本和提交信息,然后通过已核实的渠道发送。' },
|
|
22
|
+
] };
|
|
23
|
+
export const content: AccessRequestLocaleContent = { slug: 'access-to-information-request-builder', title: '信息公开请求生成器', description: '填写接收方、具体请求行、日期、地理范围、格式和发送前检查项,生成清晰的信息请求。', ui, seo: [
|
|
24
|
+
{ type: 'title', text: '写一份容易查找的信息请求', level: 2 },
|
|
25
|
+
{ type: 'paragraph', html: '当主题过于宽泛、缺少日期或混合了多个无关问题时,请求即使礼貌也可能难以回答。这个工具把你已经知道的接收方、目标记录、时间、地点和回复方式整理成可检查的线索。' },
|
|
26
|
+
{ type: 'title', text: '为搜索设置边界', level: 2 },
|
|
27
|
+
{ type: 'paragraph', html: '时间范围两端使用相同含义,例如发布日期、会议日期或付款日期。补充地点、项目、场所或组织。如果边界确实未知,就让它在检查项中保持可见,不要猜测。' },
|
|
28
|
+
{ type: 'table', headers: ['请求细节', '有用的表达', '支持的判断'], rows: [['信息集合', '最终议程和已批准的会议记录', '应搜索哪些记录?'], ['时间范围', '2025年1月1日至12月31日', '哪些记录属于范围?'], ['地理范围', '北部地区服务区域', '涉及哪个地点或项目?'], ['格式', 'CSV等机器可读表格', '如何复用或核对结果?']] },
|
|
29
|
+
{ type: 'title', text: '发送前检查草稿', level: 2 },
|
|
30
|
+
{ type: 'paragraph', html: '生成的文本会把事实放在一起,并把指南放在单独的检查区域。完成项表示最低限度的细节已经存在;警告则提示你更准确地写出记录、字段、事件或可衡量的集合。' },
|
|
31
|
+
{ type: 'list', items: ['确认接收方目前接受这种类型的请求。', '确认每行请求的是一个可识别的信息集合,而不是整个主题。', '使用封闭的日期范围和与问题匹配的地理边界。', '说明首选格式,但不要假定机构必须创建新文件。', '记录附件、标识、最终文本和提交渠道。'] },
|
|
32
|
+
{ type: 'tip', title: '具体不等于面面俱到', html: '带有记录类型、时间范围和标识的简短请求,通常比长篇叙述更容易搜索。保留有助于定位记录的背景即可。' },
|
|
33
|
+
{ type: 'title', text: '了解工具的判断边界', level: 2 },
|
|
34
|
+
{ type: 'paragraph', html: '这是一个不依赖国家的写作辅助工具。它不会确定主管机关、计算法定期限、保证公开、提交请求、选择格式或提供法律建议。这些问题应以接收机构的最新规则为准。' },
|
|
35
|
+
{ type: 'tip', title: '保存证据线索', html: '保存实际提交的文本、日期、接收方、附件和回执。这样可以把回复与真正提出的问题进行比较。' },
|
|
36
|
+
{ type: 'title', text: '准备可用的回复', level: 2 },
|
|
37
|
+
{ type: 'paragraph', html: '写明目标所需的字段或记录类型,并把请求文本与收到的回复一起保存。' },
|
|
38
|
+
], faq: [
|
|
39
|
+
{ question: '我应该请求什么?', answer: '请求可识别的记录、字段、信息集合或数量,并把无关集合分开。' },
|
|
40
|
+
{ question: '工具知道正确机构吗?', answer: '不知道。根据最新的本地指引确认接收方、管辖范围、期限、例外和渠道。' },
|
|
41
|
+
{ question: '日期和地点为什么重要?', answer: '它们限定搜索。让日期含义一致,并写明地点或项目。' },
|
|
42
|
+
{ question: '可以请求敏感信息吗?', answer: '只加入必要细节,并查看最新的访问和隐私规则。' },
|
|
43
|
+
], bibliography, howTo: [
|
|
44
|
+
{ name: '写明接收方', text: '输入可能的机构并核实当前渠道。' },
|
|
45
|
+
{ name: '拆分信息', text: '每行写一个可识别的记录、字段或集合。' },
|
|
46
|
+
{ name: '限定搜索', text: '添加一致的日期、范围和标识。' },
|
|
47
|
+
{ name: '选择交付细节', text: '写明格式和回复路径,不假定有新文件。' },
|
|
48
|
+
{ name: '检查并保存副本', text: '处理警告并保存文本和提交信息。' },
|
|
49
|
+
], schemas: [softwareApplication, faqPage, howTo] as unknown as Record<string, unknown>[] };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ToolDefinition } from '../../types';
|
|
2
|
+
import { accessToInformationRequestBuilder } from './entry';
|
|
3
|
+
|
|
4
|
+
export * from './entry';
|
|
5
|
+
export * from './logic';
|
|
6
|
+
export const ACCESS_TO_INFORMATION_REQUEST_BUILDER_TOOL: ToolDefinition = {
|
|
7
|
+
entry: accessToInformationRequestBuilder,
|
|
8
|
+
Component: () => import('./component.astro'),
|
|
9
|
+
SEOComponent: () => import('./seo.astro'),
|
|
10
|
+
BibliographyComponent: () => import('./bibliography.astro'),
|
|
11
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { buildRequestMarkdown, createPreset, emptyDraft, evaluateDraft, type RequestDraft } from './logic';
|
|
3
|
+
|
|
4
|
+
const completeDraft = (changes: Partial<RequestDraft> = {}): RequestDraft => ({
|
|
5
|
+
...emptyDraft,
|
|
6
|
+
recipient: 'Records office',
|
|
7
|
+
subject: 'Road maintenance payments',
|
|
8
|
+
items: ['A table of payments made for road maintenance'],
|
|
9
|
+
periodFrom: '2025-01-01',
|
|
10
|
+
periodTo: '2025-12-31',
|
|
11
|
+
geography: 'North district',
|
|
12
|
+
preferredFormat: 'CSV',
|
|
13
|
+
delivery: 'reply@example.test',
|
|
14
|
+
noAttachments: true,
|
|
15
|
+
savedCopy: true,
|
|
16
|
+
...changes,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe('access request logic', () => {
|
|
20
|
+
it('recognises a complete, bounded request', () => {
|
|
21
|
+
const result = evaluateDraft(completeDraft());
|
|
22
|
+
expect(result.valid).toBe(true);
|
|
23
|
+
expect(result.warnings).toEqual([]);
|
|
24
|
+
expect(result.checklist.every((item) => item.complete)).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('reports empty required fields and missing context', () => {
|
|
28
|
+
const result = evaluateDraft(emptyDraft);
|
|
29
|
+
expect(result.valid).toBe(false);
|
|
30
|
+
expect(result.warnings).toEqual(['missing-recipient', 'missing-items', 'missing-period', 'missing-format', 'missing-geography', 'missing-delivery', 'no-copy']);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('flags broad requests and incomplete date boundaries', () => {
|
|
34
|
+
const result = evaluateDraft(completeDraft({ items: ['all documents'], periodFrom: '2025-02-01', periodTo: '' }));
|
|
35
|
+
expect(result.warnings).toContain('broad-item');
|
|
36
|
+
expect(result.warnings).toContain('missing-period');
|
|
37
|
+
expect(result.checklist.find((item) => item.key === 'scope')?.complete).toBe(false);
|
|
38
|
+
expect(result.checklist.find((item) => item.key === 'period')?.caution).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('keeps optional fields out of the generated text when empty', () => {
|
|
42
|
+
const markdown = buildRequestMarkdown(completeDraft({ geography: '', attachments: '', contact: '' }));
|
|
43
|
+
expect(markdown).not.toContain('Geographic scope');
|
|
44
|
+
expect(markdown).not.toContain('Attachments or identifiers');
|
|
45
|
+
expect(markdown).not.toContain('Additional contact information');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('escapes markdown-sensitive user text and preserves multiple lines', () => {
|
|
49
|
+
const markdown = buildRequestMarkdown(completeDraft({
|
|
50
|
+
subject: 'Files *about* [water] <2025>',
|
|
51
|
+
items: ['Line one', 'Line two with `code` and _marks_'],
|
|
52
|
+
}));
|
|
53
|
+
expect(markdown).toContain('Files \\*about\\* \\[water\\] \\<2025\\>');
|
|
54
|
+
expect(markdown).toContain('- Line two with \\`code\\` and \\_marks\\_');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('uses partial periods without inventing dates', () => {
|
|
58
|
+
const markdown = buildRequestMarkdown(completeDraft({ periodFrom: '', periodTo: '2025-12-31' }));
|
|
59
|
+
expect(markdown).toContain('**Time period:** 2025-12-31');
|
|
60
|
+
expect(markdown).not.toContain('undefined');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('creates focused presets for each request family', () => {
|
|
64
|
+
expect(createPreset('records').preferredFormat).toContain('PDF');
|
|
65
|
+
expect(createPreset('spending').preferredFormat).toContain('CSV');
|
|
66
|
+
expect(createPreset('meetings').preferredFormat).toContain('email');
|
|
67
|
+
});
|
|
68
|
+
});
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
export interface RequestDraft {
|
|
2
|
+
recipient: string;
|
|
3
|
+
subject: string;
|
|
4
|
+
items: string[];
|
|
5
|
+
periodFrom: string;
|
|
6
|
+
periodTo: string;
|
|
7
|
+
geography: string;
|
|
8
|
+
preferredFormat: string;
|
|
9
|
+
delivery: string;
|
|
10
|
+
contact: string;
|
|
11
|
+
attachments: string;
|
|
12
|
+
noAttachments: boolean;
|
|
13
|
+
savedCopy: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type ChecklistKey = 'recipient' | 'scope' | 'period' | 'format' | 'attachments' | 'copy';
|
|
17
|
+
|
|
18
|
+
export interface ChecklistItem {
|
|
19
|
+
key: ChecklistKey;
|
|
20
|
+
complete: boolean;
|
|
21
|
+
caution: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface DraftEvaluation {
|
|
25
|
+
valid: boolean;
|
|
26
|
+
warnings: string[];
|
|
27
|
+
checklist: ChecklistItem[];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const emptyDraft: RequestDraft = {
|
|
31
|
+
recipient: '',
|
|
32
|
+
subject: '',
|
|
33
|
+
items: [''],
|
|
34
|
+
periodFrom: '',
|
|
35
|
+
periodTo: '',
|
|
36
|
+
geography: '',
|
|
37
|
+
preferredFormat: '',
|
|
38
|
+
delivery: '',
|
|
39
|
+
contact: '',
|
|
40
|
+
attachments: '',
|
|
41
|
+
noAttachments: false,
|
|
42
|
+
savedCopy: false,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
function clean(value: string): string {
|
|
46
|
+
return value.trim();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function hasBoundedPeriod(draft: RequestDraft): boolean {
|
|
50
|
+
return Boolean(clean(draft.periodFrom) && clean(draft.periodTo) && draft.periodFrom <= draft.periodTo);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function hasSpecificItem(item: string): boolean {
|
|
54
|
+
const value = clean(item).toLowerCase();
|
|
55
|
+
return value.length > 0 && !['all documents', 'everything', 'all information', 'all records'].includes(value);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function hasBroadLanguage(item: string): boolean {
|
|
59
|
+
const value = clean(item).toLowerCase();
|
|
60
|
+
return value.length > 220 || /\b(all|every|anything|whatever)\b/.test(value);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function makeChecklist(draft: RequestDraft, validItems: string[]): ChecklistItem[] {
|
|
64
|
+
return [
|
|
65
|
+
{ key: 'recipient', complete: Boolean(clean(draft.recipient)), caution: false },
|
|
66
|
+
{ key: 'scope', complete: validItems.length > 0 && validItems.every(hasSpecificItem), caution: validItems.some(hasBroadLanguage) },
|
|
67
|
+
{ key: 'period', complete: hasBoundedPeriod(draft), caution: Boolean(clean(draft.periodFrom) || clean(draft.periodTo)) && !hasBoundedPeriod(draft) },
|
|
68
|
+
{ key: 'format', complete: Boolean(clean(draft.preferredFormat)), caution: false },
|
|
69
|
+
{ key: 'attachments', complete: Boolean(clean(draft.attachments) || draft.noAttachments), caution: false },
|
|
70
|
+
{ key: 'copy', complete: draft.savedCopy, caution: false },
|
|
71
|
+
];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function evaluateDraft(draft: RequestDraft): DraftEvaluation {
|
|
75
|
+
const validItems = draft.items.map(clean).filter(Boolean);
|
|
76
|
+
const checklist = makeChecklist(draft, validItems);
|
|
77
|
+
const warnings = getWarnings(draft, validItems);
|
|
78
|
+
return { valid: hasMinimumFields(draft, validItems), warnings, checklist };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function getWarnings(draft: RequestDraft, validItems: string[]): string[] {
|
|
82
|
+
const checks: [boolean, string][] = [
|
|
83
|
+
[!clean(draft.recipient), 'missing-recipient'],
|
|
84
|
+
[validItems.length === 0, 'missing-items'],
|
|
85
|
+
[!hasBoundedPeriod(draft), 'missing-period'],
|
|
86
|
+
[validItems.some(hasBroadLanguage), 'broad-item'],
|
|
87
|
+
[!clean(draft.preferredFormat), 'missing-format'],
|
|
88
|
+
[!clean(draft.geography), 'missing-geography'],
|
|
89
|
+
[!clean(draft.delivery), 'missing-delivery'],
|
|
90
|
+
[!draft.savedCopy, 'no-copy'],
|
|
91
|
+
];
|
|
92
|
+
return checks.filter(([condition]) => condition).map(([, code]) => code);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function hasMinimumFields(draft: RequestDraft, validItems: string[]): boolean {
|
|
96
|
+
return Boolean(clean(draft.recipient) && clean(draft.subject) && validItems.length > 0);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function escapeMarkdown(value: string): string {
|
|
100
|
+
return value.replace(/\\/g, '\\\\').replace(/([*_`[\]<>])/g, '\\$1').trim();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function addField(lines: string[], label: string, value: string): void {
|
|
104
|
+
const cleanValue = escapeMarkdown(value);
|
|
105
|
+
if (cleanValue) lines.push(`**${label}:** ${cleanValue}`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function buildRequestMarkdown(draft: RequestDraft): string {
|
|
109
|
+
const lines = ['# Information request', ''];
|
|
110
|
+
addField(lines, 'To', draft.recipient);
|
|
111
|
+
addField(lines, 'Subject', draft.subject);
|
|
112
|
+
if (lines.length > 2) lines.push('');
|
|
113
|
+
lines.push('I would like to request the following information:', '');
|
|
114
|
+
draft.items.map(clean).filter(Boolean).forEach((item) => lines.push(`- ${escapeMarkdown(item)}`));
|
|
115
|
+
lines.push('');
|
|
116
|
+
addField(lines, 'Time period', formatPeriod(draft));
|
|
117
|
+
addField(lines, 'Geographic scope', draft.geography);
|
|
118
|
+
addField(lines, 'Preferred format', draft.preferredFormat);
|
|
119
|
+
addField(lines, 'Delivery details', draft.delivery);
|
|
120
|
+
addField(lines, 'Attachments or identifiers', draft.attachments);
|
|
121
|
+
if (draft.contact.trim()) addField(lines, 'Additional contact information', draft.contact);
|
|
122
|
+
lines.push('', 'Please let me know if any part of this request needs clarification.', '', 'Thank you.');
|
|
123
|
+
return lines.join('\n');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function formatPeriod(draft: RequestDraft): string {
|
|
127
|
+
const from = clean(draft.periodFrom);
|
|
128
|
+
const to = clean(draft.periodTo);
|
|
129
|
+
if (from && to) return `${from} to ${to}`;
|
|
130
|
+
return from || to;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function createPreset(kind: 'records' | 'spending' | 'meetings'): RequestDraft {
|
|
134
|
+
const base = { ...emptyDraft, items: [...emptyDraft.items] };
|
|
135
|
+
if (kind === 'records') return { ...base, subject: 'Minutes and agendas for public meetings', items: ['Final agendas and approved minutes for public meetings held by the named body'], geography: 'The named body\'s meeting jurisdiction', preferredFormat: 'Searchable PDF or document' };
|
|
136
|
+
if (kind === 'spending') return { ...base, subject: 'Payments for the named programme', items: ['A transaction-level table of payments made for the named programme, including date, supplier, description, and amount'], geography: 'The named programme area', preferredFormat: 'Machine-readable table such as CSV' };
|
|
137
|
+
return { ...base, subject: 'Service complaints and responses', items: ['The number of complaints received about the named service and the number resolved, grouped by month'], geography: 'The named service area', preferredFormat: 'Electronic copy by email' };
|
|
138
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { SEORenderer } from '@jjlmoya/utils-shared';
|
|
3
|
+
import { accessToInformationRequestBuilder } 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 = accessToInformationRequestBuilder.i18n[locale] ?? accessToInformationRequestBuilder.i18n.en;
|
|
12
|
+
const content = loader ? await loader() : undefined;
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
{content ? <SEORenderer content={{ locale, sections: content.seo }} /> : null}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { emptyDraft, type RequestDraft } from './logic';
|
|
2
|
+
|
|
3
|
+
const storageKey = 'jjlmoya-access-request-draft';
|
|
4
|
+
|
|
5
|
+
export function loadDraft(): RequestDraft | null {
|
|
6
|
+
try {
|
|
7
|
+
const raw = localStorage.getItem(storageKey);
|
|
8
|
+
if (!raw) return null;
|
|
9
|
+
const parsed = JSON.parse(raw) as Partial<RequestDraft>;
|
|
10
|
+
if (!Array.isArray(parsed.items)) return null;
|
|
11
|
+
return { ...emptyDraft, ...parsed, items: parsed.items } as RequestDraft;
|
|
12
|
+
} catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function saveDraft(draft: RequestDraft): void {
|
|
18
|
+
try {
|
|
19
|
+
localStorage.setItem(storageKey, JSON.stringify(draft));
|
|
20
|
+
} catch {}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function clearDraft(): void {
|
|
24
|
+
try {
|
|
25
|
+
localStorage.removeItem(storageKey);
|
|
26
|
+
} catch {}
|
|
27
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
export interface AccessRequestUI {
|
|
2
|
+
[key: string]: string;
|
|
3
|
+
eyebrow: string;
|
|
4
|
+
introHeading: string;
|
|
5
|
+
introText: string;
|
|
6
|
+
targetHeading: string;
|
|
7
|
+
targetPrompt: string;
|
|
8
|
+
questionPrompt: string;
|
|
9
|
+
boundaryHeading: string;
|
|
10
|
+
boundaryPrompt: string;
|
|
11
|
+
reviewWarningsHeading: string;
|
|
12
|
+
recipientLabel: string;
|
|
13
|
+
recipientPlaceholder: string;
|
|
14
|
+
subjectLabel: string;
|
|
15
|
+
subjectPlaceholder: string;
|
|
16
|
+
itemsLabel: string;
|
|
17
|
+
itemPlaceholder: string;
|
|
18
|
+
addItem: string;
|
|
19
|
+
removeItem: string;
|
|
20
|
+
periodLabel: string;
|
|
21
|
+
periodFrom: string;
|
|
22
|
+
periodTo: string;
|
|
23
|
+
geographyLabel: string;
|
|
24
|
+
geographyPlaceholder: string;
|
|
25
|
+
formatLabel: string;
|
|
26
|
+
formatPlaceholder: string;
|
|
27
|
+
formatEmail: string;
|
|
28
|
+
formatCsv: string;
|
|
29
|
+
formatPdf: string;
|
|
30
|
+
formatOriginal: string;
|
|
31
|
+
deliveryLabel: string;
|
|
32
|
+
deliveryPlaceholder: string;
|
|
33
|
+
contactLabel: string;
|
|
34
|
+
contactPlaceholder: string;
|
|
35
|
+
attachmentsLabel: string;
|
|
36
|
+
attachmentsPlaceholder: string;
|
|
37
|
+
noAttachments: string;
|
|
38
|
+
savedCopy: string;
|
|
39
|
+
presetLabel: string;
|
|
40
|
+
presetRecords: string;
|
|
41
|
+
presetSpending: string;
|
|
42
|
+
presetMeetings: string;
|
|
43
|
+
generateAction: string;
|
|
44
|
+
resetAction: string;
|
|
45
|
+
resultHeading: string;
|
|
46
|
+
resultIntro: string;
|
|
47
|
+
copyAction: string;
|
|
48
|
+
printAction: string;
|
|
49
|
+
copied: string;
|
|
50
|
+
noResult: string;
|
|
51
|
+
checklistHeading: string;
|
|
52
|
+
completeLabel: string;
|
|
53
|
+
reviewLabel: string;
|
|
54
|
+
checklistRecipient: string;
|
|
55
|
+
checklistScope: string;
|
|
56
|
+
checklistPeriod: string;
|
|
57
|
+
checklistFormat: string;
|
|
58
|
+
checklistAttachments: string;
|
|
59
|
+
checklistCopy: string;
|
|
60
|
+
methodHeading: string;
|
|
61
|
+
methodText: string;
|
|
62
|
+
limitsHeading: string;
|
|
63
|
+
limitsText: string;
|
|
64
|
+
edgeCasesHeading: string;
|
|
65
|
+
edgeCasesText: string;
|
|
66
|
+
statusReady: string;
|
|
67
|
+
statusReview: string;
|
|
68
|
+
missingRecipient: string;
|
|
69
|
+
missingItems: string;
|
|
70
|
+
missingPeriod: string;
|
|
71
|
+
broadItem: string;
|
|
72
|
+
missingFormat: string;
|
|
73
|
+
missingGeography: string;
|
|
74
|
+
missingDelivery: string;
|
|
75
|
+
noCopyWarning: string;
|
|
76
|
+
itemNumber: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export const ui: AccessRequestUI = {
|
|
80
|
+
eyebrow: 'CIVIC PAPER TRAIL',
|
|
81
|
+
introHeading: 'Shape the question before it leaves your desk.',
|
|
82
|
+
introText: 'Start with the destination, name one requestable set per line, then add the boundaries that make the record findable.',
|
|
83
|
+
targetHeading: 'Set the destination',
|
|
84
|
+
targetPrompt: 'Who can answer?',
|
|
85
|
+
questionPrompt: 'One set at a time',
|
|
86
|
+
boundaryHeading: 'Draw the boundaries',
|
|
87
|
+
boundaryPrompt: 'What makes it findable?',
|
|
88
|
+
reviewWarningsHeading: 'Review before sending',
|
|
89
|
+
recipientLabel: 'Authority or recipient',
|
|
90
|
+
recipientPlaceholder: 'Department, agency, archive, or organisation',
|
|
91
|
+
subjectLabel: 'Subject',
|
|
92
|
+
subjectPlaceholder: 'The records or information you want to locate',
|
|
93
|
+
itemsLabel: 'Information requested',
|
|
94
|
+
itemPlaceholder: 'Ask for one identifiable information set, record, or field',
|
|
95
|
+
addItem: 'Add another request line',
|
|
96
|
+
removeItem: 'Remove this line',
|
|
97
|
+
periodLabel: 'Time period',
|
|
98
|
+
periodFrom: 'From',
|
|
99
|
+
periodTo: 'To',
|
|
100
|
+
geographyLabel: 'Geographic scope',
|
|
101
|
+
geographyPlaceholder: 'City, district, site, programme area, or nationwide',
|
|
102
|
+
formatLabel: 'Preferred format',
|
|
103
|
+
formatPlaceholder: 'Choose a delivery format',
|
|
104
|
+
formatEmail: 'Electronic copy by email',
|
|
105
|
+
formatCsv: 'Machine-readable table such as CSV',
|
|
106
|
+
formatPdf: 'Searchable PDF or document',
|
|
107
|
+
formatOriginal: 'Original format held by the authority',
|
|
108
|
+
deliveryLabel: 'Delivery details',
|
|
109
|
+
deliveryPlaceholder: 'Email address, postal details, or a safe way to reply',
|
|
110
|
+
contactLabel: 'Optional contact information',
|
|
111
|
+
contactPlaceholder: 'Phone, reference number, or preferred reply channel',
|
|
112
|
+
attachmentsLabel: 'Attachments or identifiers',
|
|
113
|
+
attachmentsPlaceholder: 'File names, record numbers, links, or context that helps locate the records',
|
|
114
|
+
noAttachments: 'No attachments or identifiers are needed',
|
|
115
|
+
savedCopy: 'I have saved a copy of this final text',
|
|
116
|
+
presetLabel: 'Start from a focused example',
|
|
117
|
+
presetRecords: 'Meeting records',
|
|
118
|
+
presetSpending: 'Spending records',
|
|
119
|
+
presetMeetings: 'Service complaints',
|
|
120
|
+
generateAction: 'Build request draft',
|
|
121
|
+
resetAction: 'Clear draft',
|
|
122
|
+
resultHeading: 'Request draft',
|
|
123
|
+
resultIntro: 'Review every fact and edit the final text before copying. Guidance and warnings stay outside the text you send.',
|
|
124
|
+
copyAction: 'Copy Markdown',
|
|
125
|
+
printAction: 'Print or save as PDF',
|
|
126
|
+
copied: 'Copied to clipboard',
|
|
127
|
+
noResult: 'Add a recipient, subject, and at least one request line to see the paper trail.',
|
|
128
|
+
checklistHeading: 'Pre-send traceability',
|
|
129
|
+
completeLabel: 'Ready',
|
|
130
|
+
reviewLabel: 'Review',
|
|
131
|
+
checklistRecipient: 'Recipient is named',
|
|
132
|
+
checklistScope: 'Each line identifies one requestable set',
|
|
133
|
+
checklistPeriod: 'Time period is bounded',
|
|
134
|
+
checklistFormat: 'Preferred format is stated',
|
|
135
|
+
checklistAttachments: 'Attachments and identifiers are accounted for',
|
|
136
|
+
checklistCopy: 'A copy is saved for your records',
|
|
137
|
+
methodHeading: 'Method applied',
|
|
138
|
+
methodText: 'The builder turns a broad topic into a traceable request: one information set per line, a named recipient, a clear date range, a geographic boundary, a preferred format, and a usable reply route. It only rearranges details you provide and never adds a legal basis, deadline, authority, or factual claim.',
|
|
139
|
+
limitsHeading: 'What this tool does not do',
|
|
140
|
+
limitsText: 'It does not identify the competent authority, determine a statutory deadline, guarantee disclosure, file the request, classify a record under local law, or provide legal advice. Check the receiving body\'s current instructions before sending.',
|
|
141
|
+
edgeCasesHeading: 'Edge cases and data warnings',
|
|
142
|
+
edgeCasesText: 'Broad phrases such as "all documents" can be hard to search. Avoid undefined terms, mixed date meanings, unnecessary personal data, and requests for a new analysis when an existing record is what you need. Formats may not be available, and jurisdiction-specific identity or contact rules may apply.',
|
|
143
|
+
statusReady: 'The draft has a complete minimum trail.',
|
|
144
|
+
statusReview: 'The draft is usable as a starting point but needs review.',
|
|
145
|
+
missingRecipient: 'Name the authority or recipient before sending.',
|
|
146
|
+
missingItems: 'Add at least one specific information set.',
|
|
147
|
+
missingPeriod: 'Add a start and end date, or explain the missing boundary.',
|
|
148
|
+
broadItem: 'This line may be too broad. Name a record type, field, event, or measurable set.',
|
|
149
|
+
missingFormat: 'Choose how the information should be delivered.',
|
|
150
|
+
missingGeography: 'Add a place or state why the request is nationwide.',
|
|
151
|
+
missingDelivery: 'Add a route the recipient can use to reply.',
|
|
152
|
+
noCopyWarning: 'Save the final text and submission details before sending.',
|
|
153
|
+
itemNumber: 'Request line',
|
|
154
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export const validation = {
|
|
2
|
+
reviewedAt: '2026-09-03',
|
|
3
|
+
methodology: 'Editorial and functional review of request scoping, date boundaries, delivery details, local-only processing, and generated Markdown output.',
|
|
4
|
+
sources: ['https://ico.org.uk/for-the-public/official-information/preparing-and-submitting-your-information-request/', 'https://www.cnil.fr/fr/definition/droit-dacces-aux-documents-administratifs'],
|
|
5
|
+
referenceCases: ['A request with one record set per line and bounded dates', 'A request with a partial period that remains visible for review', 'A request that includes optional delivery and contact details'],
|
|
6
|
+
limitations: 'The tool does not determine the competent authority, legal deadline, disclosure outcome, jurisdiction-specific filing rules, or legal advice.',
|
|
7
|
+
};
|