11import { observer } from 'mobx-react' ;
2- import { FC , useContext } from 'react' ;
3- import { Badge , Card , Col , Row } from 'react-bootstrap' ;
2+ import { FC , useContext , useEffect , useState } from 'react' ;
3+ import { Badge , Card , Col , Container , Row } from 'react-bootstrap' ;
44
5- import { Organization } from '../../models/Organization' ;
5+ import { Organization , OrganizationModel } from '../../models/Organization' ;
66import { I18nContext } from '../../models/Translation' ;
77
8- export interface ChinaPublicInterestLandscapeProps {
9- tagMap : Record < string , Organization [ ] > ;
10- }
8+ const organizationModel = new OrganizationModel ( ) ;
119
12- export const ChinaPublicInterestLandscape : FC < ChinaPublicInterestLandscapeProps > = observer (
13- ( { tagMap } ) => {
14- const { t } = useContext ( I18nContext ) ;
10+ export const ChinaPublicInterestLandscape : FC = observer ( ( ) => {
11+ const { t } = useContext ( I18nContext ) ;
12+ const [ tagMap , setTagMap ] = useState < Record < string , Organization [ ] > > ( { } ) ;
13+ const [ loading , setLoading ] = useState ( false ) ;
1514
16- const tagEntries = Object . entries ( tagMap ) . sort ( ( [ , a ] , [ , b ] ) => b . length - a . length ) ;
15+ useEffect ( ( ) => {
16+ const loadData = async ( ) => {
17+ setLoading ( true ) ;
18+ try {
19+ const groupedData = await organizationModel . groupAllByTags ( ) ;
20+ setTagMap ( groupedData ) ;
21+ } finally {
22+ setLoading ( false ) ;
23+ }
24+ } ;
1725
18- return (
19- < div >
20- { tagEntries . length === 0 && (
21- < Card >
22- < Card . Body className = "text-center py-5" >
23- < Card . Title > { t ( 'no_data_available' ) } </ Card . Title >
24- < Card . Text > { t ( 'landscape_data_loading_message' ) } </ Card . Text >
25- </ Card . Body >
26- </ Card >
27- ) }
26+ loadData ( ) ;
27+ } , [ ] ) ;
2828
29- { tagEntries . map ( ( [ tag , organizations ] ) => (
30- < Card key = { tag } className = "mb-4" >
31- < Card . Header className = "d-flex justify-content-between align-items-center" >
32- < h5 className = "mb-0" > { tag } </ h5 >
33- < Badge bg = "primary" > { organizations . length } { t ( 'organizations' ) } </ Badge >
34- </ Card . Header >
35- < Card . Body >
36- < Row className = "g-3" >
37- { organizations . map ( org => (
38- < Col key = { org . id } md = { 6 } lg = { 4 } >
39- < Card className = "h-100" >
40- < Card . Body >
41- < Card . Title className = "h6" > { org . name } </ Card . Title >
42- { org . description && (
43- < Card . Text className = "small text-muted" >
44- { org . description . length > 100
45- ? `${ org . description . substring ( 0 , 100 ) } ...`
46- : org . description }
47- </ Card . Text >
48- ) }
49- < div className = "mt-2" >
50- { org . city && (
51- < Badge bg = "secondary" className = "me-1" >
52- { org . city }
53- </ Badge >
29+ const tagEntries = Object . entries ( tagMap ) . sort ( ( [ , a ] , [ , b ] ) => b . length - a . length ) ;
30+
31+ return (
32+ < Container className = "py-4" >
33+ < Row className = "mb-4" >
34+ < Col >
35+ < h1 > { t ( 'china_public_interest_landscape' ) } </ h1 >
36+ </ Col >
37+ </ Row >
38+
39+ { loading && (
40+ < Row >
41+ < Col className = "text-center" >
42+ < div className = "spinner-border" role = "status" >
43+ < span className = "visually-hidden" > Loading...</ span >
44+ </ div >
45+ </ Col >
46+ </ Row >
47+ ) }
48+
49+ { ! loading && tagEntries . length === 0 && (
50+ < Row >
51+ < Col >
52+ < Card >
53+ < Card . Body className = "text-center py-5" >
54+ < Card . Title > { t ( 'no_data_available' ) } </ Card . Title >
55+ < Card . Text > { t ( 'landscape_data_loading_message' ) } </ Card . Text >
56+ </ Card . Body >
57+ </ Card >
58+ </ Col >
59+ </ Row >
60+ ) }
61+
62+ { ! loading && tagEntries . map ( ( [ tag , organizations ] ) => (
63+ < Row key = { tag } className = "mb-4" >
64+ < Col >
65+ < Card >
66+ < Card . Header className = "d-flex justify-content-between align-items-center" >
67+ < h5 className = "mb-0" > { tag } </ h5 >
68+ < Badge bg = "primary" > { organizations . length } { t ( 'organizations' ) } </ Badge >
69+ </ Card . Header >
70+ < Card . Body >
71+ < Row className = "g-3" >
72+ { organizations . map ( org => (
73+ < Col key = { org . id } md = { 6 } lg = { 4 } >
74+ < Card className = "h-100" >
75+ < Card . Body >
76+ < Card . Title className = "h6" > { org . name } </ Card . Title >
77+ { org . description && (
78+ < Card . Text className = "small text-muted" >
79+ { org . description . length > 100
80+ ? `${ org . description . substring ( 0 , 100 ) } ...`
81+ : org . description }
82+ </ Card . Text >
5483 ) }
55- { org . type && (
56- < Badge bg = "info" className = "me-1" >
57- { org . type }
58- </ Badge >
84+ < div className = "mt-2" >
85+ { org . city && (
86+ < Badge bg = "secondary" className = "me-1" >
87+ { org . city }
88+ </ Badge >
89+ ) }
90+ { org . type && (
91+ < Badge bg = "info" className = "me-1" >
92+ { org . type }
93+ </ Badge >
94+ ) }
95+ </ div >
96+ { org . website && (
97+ < Card . Link
98+ href = { org . website }
99+ target = "_blank"
100+ className = "small"
101+ >
102+ { t ( 'visit_website' ) }
103+ </ Card . Link >
59104 ) }
60- </ div >
61- { org . website && (
62- < Card . Link
63- href = { org . website }
64- target = "_blank"
65- className = "small"
66- >
67- { t ( 'visit_website' ) }
68- </ Card . Link >
69- ) }
70- </ Card . Body >
71- </ Card >
72- </ Col >
73- ) ) }
74- </ Row >
75- </ Card . Body >
76- </ Card >
77- ) ) }
78- </ div >
79- ) ;
80- } ,
81- ) ;
105+ </ Card . Body >
106+ </ Card >
107+ </ Col >
108+ ) ) }
109+ </ Row >
110+ </ Card . Body >
111+ </ Card >
112+ </ Col >
113+ </ Row >
114+ ) ) }
115+ </ Container >
116+ ) ;
117+ } ) ;
0 commit comments