Skip to content

Commit 1cbbfa4

Browse files
authored
Merge pull request #2085 from codeforboston/main
Deploy to PROD 3/24/26 Part 2
2 parents 2637677 + 1b7099d commit 1cbbfa4

9 files changed

Lines changed: 175 additions & 5 deletions

File tree

components/bill/Summary.tsx

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,14 @@ export const ViewMessage = styled.div`
104104
}
105105
`
106106

107+
const BALLOT_SUMMARY_CHAR_LIMIT = 1000
108+
107109
export const Summary = ({
108110
bill,
109111
className
110112
}: BillProps & { className?: string }) => {
111113
const [showBillDetails, setShowBillDetails] = useState(false)
114+
const [showFullSummary, setShowFullSummary] = useState(false)
112115
const handleShowBillDetails = () => setShowBillDetails(true)
113116
const handleHideBillDetails = () => setShowBillDetails(false)
114117
const billText = bill?.content?.DocumentText
@@ -221,7 +224,36 @@ export const Summary = ({
221224
)}
222225
{bill.summary !== undefined && isBallotMeasure ? (
223226
<BallotSummaryRow className={`mx-1 mb-3`}>
224-
{bill.summary}
227+
{bill.summary.length > BALLOT_SUMMARY_CHAR_LIMIT ? (
228+
<>
229+
{bill.summary.slice(0, BALLOT_SUMMARY_CHAR_LIMIT)}{" "}
230+
<StyledButton
231+
variant="link"
232+
onClick={() => setShowFullSummary(true)}
233+
>
234+
{t("bill.view_full_summary")}
235+
</StyledButton>
236+
<Modal
237+
show={showFullSummary}
238+
onHide={() => setShowFullSummary(false)}
239+
size="lg"
240+
>
241+
<Modal.Header
242+
closeButton
243+
onClick={() => setShowFullSummary(false)}
244+
>
245+
<Modal.Title>{bill?.id}</Modal.Title>
246+
</Modal.Header>
247+
<Modal.Body className="bg-white">
248+
<FormattedBillDetails>
249+
{bill.summary}
250+
</FormattedBillDetails>
251+
</Modal.Body>
252+
</Modal>
253+
</>
254+
) : (
255+
bill.summary
256+
)}
225257
</BallotSummaryRow>
226258
) : (
227259
<Row className="mx-1 mb-3">{bill.summary}</Row>

components/db/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export * from "./bills"
22
export * from "./createTableHook"
33
export * from "./members"
4+
export * from "./news"
45
export * from "./profile"
56
export * from "./testimony"
67
export * from "./useUpcomingBills"

components/db/news.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { collection, getDocs, orderBy, Timestamp } from "firebase/firestore"
2+
import { useAsync } from "react-async-hook"
3+
import { firestore } from "../firebase"
4+
5+
export type NewsType = "article" | "award" | "book"
6+
7+
export type NewsItem = {
8+
id: string
9+
url: string
10+
title: string
11+
author: string
12+
type: NewsType
13+
description?: string
14+
publishDate: string
15+
createdAt: Timestamp
16+
}
17+
18+
export async function listNews(): Promise<NewsItem[]> {
19+
const newsRef = collection(firestore, "news")
20+
const result = await getDocs(newsRef)
21+
return result.docs.map(d => ({ id: d.id, ...d.data() } as NewsItem))
22+
}
23+
24+
export function useNews() {
25+
return useAsync(listNews, [])
26+
}

components/moderation/News.tsx

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import React, { useEffect } from "react"
2+
import { collection, getFirestore, onSnapshot } from "firebase/firestore"
3+
import {
4+
Create,
5+
Datagrid,
6+
DateField,
7+
DateInput,
8+
Edit,
9+
EditButton,
10+
FunctionField,
11+
List,
12+
SelectInput,
13+
SimpleForm,
14+
TextField,
15+
TextInput,
16+
useRefresh
17+
} from "react-admin"
18+
19+
const typeChoices = [
20+
{ id: "article", name: "Article" },
21+
{ id: "award", name: "Award" },
22+
{ id: "book", name: "Book" }
23+
]
24+
25+
export function ListNews() {
26+
const firestore = getFirestore()
27+
const refresh = useRefresh()
28+
29+
useEffect(() => {
30+
const newsRef = collection(firestore, "news")
31+
const unsubscribe = onSnapshot(
32+
newsRef,
33+
() => refresh(),
34+
(e: Error) => console.log(e)
35+
)
36+
37+
return () => unsubscribe()
38+
}, [firestore, refresh])
39+
40+
return (
41+
<List>
42+
<Datagrid rowClick="edit" bulkActionButtons={false}>
43+
<TextField source="id" label="News ID" />
44+
<TextField source="title" label="Title" />
45+
<TextField source="author" label="Author" />
46+
<TextField source="type" label="Type" />
47+
<DateField source="publishDate" label="Publish Date" />
48+
<DateField source="createdAt" showTime />
49+
<EditButton label="Edit" />
50+
</Datagrid>
51+
</List>
52+
)
53+
}
54+
55+
export function EditNews() {
56+
return (
57+
<Edit>
58+
<SimpleForm>
59+
<TextInput source="url" fullWidth />
60+
<SelectInput source="type" choices={typeChoices} />
61+
<TextInput source="author" />
62+
<TextInput source="title" fullWidth />
63+
<TextInput source="description" multiline fullWidth />
64+
<DateInput source="publishDate" />
65+
</SimpleForm>
66+
</Edit>
67+
)
68+
}
69+
70+
export function CreateNews() {
71+
return (
72+
<Create
73+
redirect="list"
74+
transform={(data: Record<string, unknown>) => ({
75+
...data,
76+
createdAt: new Date()
77+
})}
78+
>
79+
<SimpleForm>
80+
<TextInput source="url" fullWidth />
81+
<SelectInput source="type" choices={typeChoices} />
82+
<TextInput source="author" />
83+
<TextInput source="title" fullWidth />
84+
<TextInput source="description" multiline fullWidth />
85+
<DateInput source="publishDate" />
86+
</SimpleForm>
87+
</Create>
88+
)
89+
}
90+
91+
export default { ListNews, EditNews, CreateNews }

components/moderation/dataProviderDbCalls.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,13 @@ export async function createMyOne(
136136
): Promise<CreateResult> {
137137
console.log("creating my one")
138138
const { data, meta } = params
139-
const ref = doc(firestore, resource, data.id)
140-
await setDoc(ref, data)
141-
return { data: data }
139+
const ref = data.id
140+
? doc(firestore, resource, data.id)
141+
: doc(collection(firestore, resource))
142+
const id = ref.id
143+
const newData = { ...data, id }
144+
await setDoc(ref, newData)
145+
return { data: newData }
142146
}
143147

144148
export const getMyListGroup = async (

components/moderation/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export * from "./types"
22
export * from "./ListPublishedTestimony"
3+
export * from "./News"
34
export * from "./ListReports"
45
export * from "./EditReports"
56
export * from "./ListProfiles"

components/moderation/moderation.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { QueryClient, QueryClientProvider } from "react-query"
55
import { EditReports, ListReports } from "./"
66
import { ListProfiles } from "./ListProfiles"
77
import { ScrapeHearingList } from "./ScrapeHearing"
8+
import { ListNews, EditNews, CreateNews } from "./"
89
import {
910
createMyOne,
1011
getMyListGroup,
@@ -54,6 +55,13 @@ const App = () => {
5455
list={ScrapeHearingList}
5556
options={{ label: "Scrape Hearing" }}
5657
/>
58+
<Resource
59+
name="news"
60+
list={ListNews}
61+
edit={EditNews}
62+
create={CreateNews}
63+
options={{ label: "In the News" }}
64+
/>
5765
</Admin>
5866
</QueryClientProvider>
5967
)

firestore.rules

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ service cloud.firestore {
6969
// Only admins can do anything with it
7070
allow read, write: if request.auth.token.get("role", "user") == "admin"
7171
}
72+
73+
// Admin-managed news collection used by the admin UI and public news page
74+
match /news/{nid} {
75+
allow read: if true;
76+
allow write: if request.auth.token.get("role", "user") == "admin";
77+
}
7278
match /users/{uid} {
7379
allow read, write: if request.auth.token.get("role", "user") == "admin"
7480
match /draftTestimony/{id} {

public/locales/en/common.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@
6565
"smart_tag": "AI Smart Tag",
6666
"status_and_history": "Status & History",
6767
"status_history": "Status History",
68-
"view_bill": "View Bill Text"
68+
"view_bill": "View Bill Text",
69+
"view_full_summary": "View Full Summary"
6970
},
7071
"bill_updates": "Bill Updates",
7172
"browse_bills": "browse bills",

0 commit comments

Comments
 (0)