1+ import axios from "axios"
2+ import { useState , useEffect } from "react"
3+
4+ interface Document {
5+ docTitle : string
6+ docNumber : string
7+ docYear : number
8+ isEmpty : boolean
9+ docVersion : string
10+ }
11+
12+ interface YearData {
13+ year : number
14+ totalDocuments : number
15+ documentsWithContent : number
16+ emptyDocuments : number
17+ contentPercentage : number
18+ documents : Document [ ]
19+ }
20+
21+ const YearsPage = ( ) => {
22+ const [ yearData , setYearData ] = useState < YearData [ ] > ( [ ] )
23+ const [ loading , setLoading ] = useState ( true )
24+ const [ error , setError ] = useState < string | null > ( null )
25+
26+ console . log ( 'YEARS' )
27+
28+ useEffect ( ( ) => {
29+ const fetchAllYears = async ( ) => {
30+ try {
31+ setLoading ( true )
32+ const years = Array . from ( { length : 20 } , ( _ , i ) => 1960 + i ) // 1960-1970
33+
34+ const promises = years . map ( async ( year ) => {
35+ try {
36+ const response = await axios . get ( `/api/statute/${ year } /fin` )
37+ const documents : Document [ ] = response . data
38+
39+ const totalDocuments = documents . length
40+ const documentsWithContent = documents . filter ( doc => ! doc . isEmpty ) . length
41+ const emptyDocuments = documents . filter ( doc => doc . isEmpty ) . length
42+ const contentPercentage = totalDocuments > 0 ? Math . round ( ( documentsWithContent / totalDocuments ) * 100 ) : 0
43+
44+ return {
45+ year,
46+ totalDocuments,
47+ documentsWithContent,
48+ emptyDocuments,
49+ contentPercentage,
50+ documents
51+ }
52+ } catch ( error ) {
53+ console . error ( `Error fetching data for year ${ year } :` , error )
54+ return {
55+ year,
56+ totalDocuments : 0 ,
57+ documentsWithContent : 0 ,
58+ emptyDocuments : 0 ,
59+ contentPercentage : 0 ,
60+ documents : [ ]
61+ }
62+ }
63+ } )
64+
65+ const results = await Promise . all ( promises )
66+ setYearData ( results . sort ( ( a , b ) => b . year - a . year ) ) // Sort by year descending
67+ setError ( null )
68+ } catch ( error ) {
69+ console . error ( 'Error fetching year data:' , error )
70+ setError ( 'Virhe tietojen lataamisessa' )
71+ } finally {
72+ setLoading ( false )
73+ }
74+ }
75+
76+ fetchAllYears ( )
77+ } , [ ] )
78+
79+ const tableStyle : React . CSSProperties = {
80+ width : '100%' ,
81+ borderCollapse : 'collapse' ,
82+ marginTop : '20px' ,
83+ backgroundColor : 'white' ,
84+ boxShadow : '0 2px 8px rgba(0,0,0,0.1)' ,
85+ borderRadius : '8px' ,
86+ overflow : 'hidden'
87+ }
88+
89+ const headerStyle : React . CSSProperties = {
90+ backgroundColor : '#0C6FC0' ,
91+ color : 'white' ,
92+ padding : '12px 16px' ,
93+ textAlign : 'left' ,
94+ fontWeight : 'bold' ,
95+ fontSize : '14px'
96+ }
97+
98+ const cellStyle : React . CSSProperties = {
99+ padding : '12px 16px' ,
100+ borderBottom : '1px solid #e0e0e0' ,
101+ fontSize : '14px'
102+ }
103+
104+ const numberCellStyle : React . CSSProperties = {
105+ ...cellStyle ,
106+ textAlign : 'center' ,
107+ fontFamily : 'monospace' ,
108+ fontWeight : 'bold'
109+ }
110+
111+ const yearCellStyle : React . CSSProperties = {
112+ ...cellStyle ,
113+ fontWeight : 'bold' ,
114+ color : '#0C6FC0' ,
115+ fontSize : '16px'
116+ }
117+
118+ const percentageCellStyle : React . CSSProperties = {
119+ ...cellStyle ,
120+ textAlign : 'center' ,
121+ fontWeight : 'bold'
122+ }
123+
124+ const getPercentageColor = ( percentage : number ) : string => {
125+ if ( percentage >= 80 ) return '#00b894'
126+ if ( percentage >= 60 ) return '#fdcb6e'
127+ if ( percentage >= 40 ) return '#e17055'
128+ return '#d63031'
129+ }
130+
131+ const totalStats = {
132+ totalDocuments : yearData . reduce ( ( sum , year ) => sum + year . totalDocuments , 0 ) ,
133+ documentsWithContent : yearData . reduce ( ( sum , year ) => sum + year . documentsWithContent , 0 ) ,
134+ emptyDocuments : yearData . reduce ( ( sum , year ) => sum + year . emptyDocuments , 0 )
135+ }
136+
137+ const overallPercentage = totalStats . totalDocuments > 0
138+ ? Math . round ( ( totalStats . documentsWithContent / totalStats . totalDocuments ) * 100 )
139+ : 0
140+
141+ if ( loading ) {
142+ return (
143+ < div style = { { padding : '40px' , textAlign : 'center' } } >
144+ < div > Ladataan vuosien 1960-1970 tietoja...</ div >
145+ < div style = { { marginTop : '10px' , fontSize : '12px' , color : '#666' } } >
146+ Tämä voi kestää hetken...
147+ </ div >
148+ </ div >
149+ )
150+ }
151+
152+ if ( error ) {
153+ return (
154+ < div style = { { padding : '40px' , textAlign : 'center' , color : '#d63031' } } >
155+ < div > { error } </ div >
156+ </ div >
157+ )
158+ }
159+
160+ return (
161+ < div style = { { padding : '20px' } } >
162+ < h2 style = { { marginBottom : '20px' , color : '#333' } } >
163+ Lainsäädäntö vuosilta 1960-1970
164+ </ h2 >
165+
166+ { /* Summary Stats */ }
167+ < div style = { {
168+ display : 'grid' ,
169+ gridTemplateColumns : 'repeat(auto-fit, minmax(200px, 1fr))' ,
170+ gap : '16px' ,
171+ marginBottom : '30px'
172+ } } >
173+ < div style = { {
174+ backgroundColor : '#f8f9fa' ,
175+ padding : '20px' ,
176+ borderRadius : '8px' ,
177+ textAlign : 'center' ,
178+ border : '1px solid #e9ecef'
179+ } } >
180+ < div style = { { fontSize : '24px' , fontWeight : 'bold' , color : '#0C6FC0' } } >
181+ { totalStats . totalDocuments }
182+ </ div >
183+ < div style = { { fontSize : '14px' , color : '#666' } } > Yhteensä dokumentteja</ div >
184+ </ div >
185+ < div style = { {
186+ backgroundColor : '#d1f2eb' ,
187+ padding : '20px' ,
188+ borderRadius : '8px' ,
189+ textAlign : 'center' ,
190+ border : '1px solid #00b894'
191+ } } >
192+ < div style = { { fontSize : '24px' , fontWeight : 'bold' , color : '#00b894' } } >
193+ { totalStats . documentsWithContent }
194+ </ div >
195+ < div style = { { fontSize : '14px' , color : '#666' } } > Sisältöä sisältävät</ div >
196+ </ div >
197+ < div style = { {
198+ backgroundColor : '#ffeaa7' ,
199+ padding : '20px' ,
200+ borderRadius : '8px' ,
201+ textAlign : 'center' ,
202+ border : '1px solid #fdcb6e'
203+ } } >
204+ < div style = { { fontSize : '24px' , fontWeight : 'bold' , color : '#d63031' } } >
205+ { totalStats . emptyDocuments }
206+ </ div >
207+ < div style = { { fontSize : '14px' , color : '#666' } } > Tyhjät dokumentit</ div >
208+ </ div >
209+ < div style = { {
210+ backgroundColor : '#ddd6fe' ,
211+ padding : '20px' ,
212+ borderRadius : '8px' ,
213+ textAlign : 'center' ,
214+ border : '1px solid #8b5cf6'
215+ } } >
216+ < div style = { {
217+ fontSize : '24px' ,
218+ fontWeight : 'bold' ,
219+ color : getPercentageColor ( overallPercentage )
220+ } } >
221+ { overallPercentage } %
222+ </ div >
223+ < div style = { { fontSize : '14px' , color : '#666' } } > Sisältöprosentti</ div >
224+ </ div >
225+ </ div >
226+
227+ { /* Year-by-year table */ }
228+ < table style = { tableStyle } >
229+ < thead >
230+ < tr >
231+ < th style = { headerStyle } > Vuosi</ th >
232+ < th style = { headerStyle } > Yhteensä</ th >
233+ < th style = { headerStyle } > Sisältöä</ th >
234+ < th style = { headerStyle } > Tyhjiä</ th >
235+ < th style = { headerStyle } > Sisältö %</ th >
236+ </ tr >
237+ </ thead >
238+ < tbody >
239+ { yearData . map ( ( data , index ) => (
240+ < tr
241+ key = { data . year }
242+ style = { {
243+ backgroundColor : index % 2 === 0 ? '#fafafa' : 'white'
244+ } }
245+ onMouseEnter = { ( e ) => {
246+ e . currentTarget . style . backgroundColor = '#f0f8ff'
247+ } }
248+ onMouseLeave = { ( e ) => {
249+ e . currentTarget . style . backgroundColor = index % 2 === 0 ? '#fafafa' : 'white'
250+ } }
251+ >
252+ < td style = { yearCellStyle } > { data . year } </ td >
253+ < td style = { numberCellStyle } > { data . totalDocuments } </ td >
254+ < td style = { numberCellStyle } > { data . documentsWithContent } </ td >
255+ < td style = { numberCellStyle } > { data . emptyDocuments } </ td >
256+ < td style = { {
257+ ...percentageCellStyle ,
258+ color : getPercentageColor ( data . contentPercentage )
259+ } } >
260+ { data . contentPercentage } %
261+ </ td >
262+ </ tr >
263+ ) ) }
264+ </ tbody >
265+ </ table >
266+
267+ { yearData . length === 0 && (
268+ < div style = { { textAlign : 'center' , padding : '40px' , color : '#666' } } >
269+ Ei tietoja saatavilla
270+ </ div >
271+ ) }
272+ </ div >
273+ )
274+ }
275+
276+ export default YearsPage
0 commit comments