/admin: Backend to store custom email subject lines

This commit is contained in:
David Ernst
2025-11-26 11:48:34 -06:00
parent 363d28b8f1
commit fb5e0346d6
3 changed files with 37 additions and 8 deletions

View File

@@ -1,3 +1,4 @@
import { buildSubject } from 'api/invite-voters'
import { NextApiRequest, NextApiResponse } from 'next'
import UAParser from 'ua-parser-js'
@@ -8,6 +9,7 @@ import { QueueLog } from './invite-voters'
export type AdminData = {
ballot_design?: string
ballot_design_finalized?: boolean
custom_invitation_subject?: string
custom_invitation_text?: string
election_id?: string
election_manager?: string
@@ -88,6 +90,7 @@ export default async (req: NextApiRequest, res: NextApiResponse) => {
const {
ballot_design,
ballot_design_finalized,
custom_invitation_subject,
custom_invitation_text,
election_manager,
election_title,
@@ -101,6 +104,7 @@ export default async (req: NextApiRequest, res: NextApiResponse) => {
} as {
ballot_design?: string
ballot_design_finalized?: boolean
custom_invitation_subject?: string
custom_invitation_text?: string
election_manager?: string
election_title?: string
@@ -223,6 +227,7 @@ export default async (req: NextApiRequest, res: NextApiResponse) => {
return res.status(200).send({
ballot_design,
ballot_design_finalized,
custom_invitation_subject: custom_invitation_subject || buildSubject(election_title),
custom_invitation_text,
election_id,
election_manager,

View File

@@ -0,0 +1,20 @@
import { NextApiRequest, NextApiResponse } from 'next'
import { firebase } from '../../../_services'
import { checkJwtOwnsElection } from '../../../validate-admin-jwt'
export default async (req: NextApiRequest, res: NextApiResponse) => {
const { election_id } = req.query as { election_id: string }
const { custom_invitation_subject } = req.body
// Confirm they're a valid admin that created this election
const jwt = await checkJwtOwnsElection(req, res, election_id)
if (!jwt.valid) return
// Update the custom invitation text in the database
await firebase.firestore().collection('elections').doc(election_id).update({
custom_invitation_subject,
})
res.status(200).json({ message: 'Custom invitation subject updated' })
}

View File

@@ -1,20 +1,24 @@
import { useState } from 'react'
import { api } from 'src/api-helper'
import { useStored } from '../useStored'
import { revalidate, useStored } from '../useStored'
export const CustomInvitationSubject = () => {
const { custom_invite_subject = 'Vote Invitation' } = useStored()
const [subject, setSubject] = useState(custom_invite_subject)
const { custom_invitation_subject = 'Loading...', election_id } = useStored()
return (
<div
className="p-2 mb-1 rounded cursor-pointer hover:bg-gray-100"
onClick={() => {
const newSubject = prompt('Modify email subject:', subject)
if (newSubject) setSubject(newSubject)
onClick={async () => {
const newSubject = prompt('Modify email subject:', custom_invitation_subject)
if (!newSubject) return
await api(`election/${election_id}/admin/update-invitation-subject`, {
custom_invitation_subject: newSubject,
})
revalidate(election_id)
}}
>
<span className="opacity-50">Email subject:</span> {subject}
<span className="opacity-50">Email subject:</span> {custom_invitation_subject}
</div>
)
}