Full process template for citizen initiative campaign, from digital signatures collection to PDF document rendering for legislative submission.
Table of contents
- Ready to use campaign static website (Astro, Svelte, Tailwind and DaisyUI)
- An online form with signature pad
- Signatures counter, from both offline and online channel
- Support offline sign locations list
- Privacy policy template
- GitHub Action's Workflow file for build and deploy to Github Pages
- Database configuration (Firebase, and Google Sheets with Sheethuahua)
- Firebase rules for spamming protection for online submission
- Firebase emulator with mocked data for local development
- Google Sheets template for human-curated data
- Post-campaign scripts (pdf-lib and Node.js)
- Download online submission signatories' data as CSV files
- Render CSV data on the pdf template for legislative submission
Configuration file is provided, but everything can be customized via code.
Required Node.js v20 or higher with NPM package manager.
npx degit github:wevisdemo/e-initiative-template <project-name>The template will be downloaded to the project-name folder.
For online signature submission
- Create a new project in Firebase Console
- Set up Authentication
2.1. Enable "Email/Password" and "Anonymous" sign-in methods.
2.2. Add user and provide email/password for admin account. Then add the same information in local
.env.productionfile (create it in the project folder if it does not exist) withADMIN_EMAILandADMIN_PASSWORDkeys. 2.3. Copy admin's User UID for the next step. - Set up Firestore Database
3.1 Enable the Firestore
3.2 Copy rules from
firestore.rulesand update admin's UID from the previous step. (Only update rules on the console,firestore.rulesis used by the local emulator) - Obtain Firebase config
3.1 Add a new web app from project settings > General > Your Apps
3.2 Copy
firebaseConfigas a one-line JSON format and put it inPUBLIC_FIREBASE_CONFIGof local.env.productionfile.
At the end, your .env.production file should look like this:
PUBLIC_FIREBASE_CONFIG={"apiKey": "???", "authDomain": "???", "projectId": "???", "storageBucket": "???", "messagingSenderId": "???", "appId": "???"}
ADMIN_EMAIL=???
ADMIN_PASSWORD=???Note: there is a .env.development with mocked data for working with the Firebase emulator in local development environment. You don't need to change anything there.
For human-curated data, including offline signatures count and voluntary sign locations. Skip this step if your campaign don't need both function.
- Duplicate Google Sheets template. It contain 2 sheets:
- "offline-signature" sheets for recoding offline signature count in each day. Total number is the summation of every row.
- "locations" is the sign location showing in the location page.
- Grant "Viewer" permission to "Anyone with the link" in Share menu
- Obtain Sheets ID from the URL
https://docs.google.com/spreadsheets/d/{sheetsID}/and add it to thesheets.idine-initiative.config.mjs - (Optional) If you allow anyone to submit a voluntary offline sign location.
4.1. Create a Google Form with fields corresponded to the columns in
locationssheet. 4.2. In the form response settings, link the form response to your duplicated sheets. A response sheet will be created with each question in the header. 4.2. Rename column header to be likelocationssheet. Doesn't need to have the same order, but it must corresponded to each question. 4.3 Remove oldlocationssheet and rename form response sheet to belocationsinstead.
Most of the campaign information can be config via e-initiative.config.mjs. Explanation can be found by hovering at the configuration keys (on JSDoc supported IDE) or the type definition file. Configuration should be update first so that you will know what is not covered and require editing the source code.
- Website content can be directly edited through source code. Index page entry point to get started is
src/pages/index.astro. - UI components are from either Daisy UI v3 or custom made available in
src/components. Svelte is mainly used but you can install additional UI Framework integration for Astro - Color theme defined in
e-initiative.config.mjsis available as a Tailwind class. For VSCode user, Tailwind extension is recommended for Tailwind class auto-completion. - Typography CSS global utility classes are defined in
src/styles/typography.cssand ready to be used. - Design system is corresponded to the Figma
- Don't forget to review the privacy policy in
src/pages/privacy-policy.astroand remove the "mark" tags (for highlighting) after updating the content.
To start Astro development server and Firebase emulator:
npm run devDuring the development mode, any changes to Firebase will be in local emulator, leaving production database untouched. Mock data will be initialized in Firestore emulator for post-campaign script testing. The .env.development is used.
To build website for production:
npm run buildStatic site will be generated to /dist and we can preview production build with
npm run previewNote that production build will use .env.production and the real Firebase, not emulator.
Since Astro use Static Site Generation (SSG), no data from Firebase and Google Sheets will be updated after the build. So we recommended to use CI/CD tools that support schedule deployment such as Github Actions. Then we can periodically re-build and re-deploy in a given period. Don't forget to add all the production environment variables to the CI/CD.
Template also include GitHub Action's workflow file .github/workflows/demo.yaml. Please remove it before pushing to GitHub if you don't want to use it. But if you do, don't forget to add all the production environment variables as a repository secrets and remove PUBLIC_DEMO_MODE env from the Build with Astro step.
Your PDF output template might not have the same field value/position as our given template. Before using the thoudsand of production records, we recommend testing with one record of mock data first.
To get mock data (Firebase emulator must be running from dev or dev:firebase command):
npm run download:localOutput CSV files will be generated to /out directory: one version with base64 signature data, one version without it. Then start the renderer in watch mode:
npm run render:watchThe output PDF files will be re-generated to /out directory everytime any file has changed. Keep adjusting the renderer config in e-initiative.config.mjs until the output PDF looks pretty.
First, download the production signatories' data in CSV format (duplicated or invalid records will be filtered out):
npm run download:prodThen, render to the PDF files:
npm run renderBoth CSV and PDF files will be generated to /out directory, replacing existing data if exist (eg. from mock data).
Citizens have the rights to propose a law draft to the parliament, when they can gather enough signatures (more about Thailand legislative process). The initiative leader usually follow this pattern:
-
Pre-campaign
- Create a law draft
- Design campaign content and activities
-
Campaign
- Promote the initiative
- Collect citizen signatures through offline/online channel
-
Post campaign
- Submit the draft together with all the signatures to the parliament
This project is licensed under Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) with WeVis Ltd. and Punch Up Ltd. as licensors.