-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
5efe44e
commit fce1f23
Showing
14 changed files
with
7,849 additions
and
159 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
const { getAuthToken } = require('../src/lib/google-sheets/service') | ||
const { credentials, emailList: {spreadsheetId, spreadsheetTab} } = require('../src/lib/google-sheets/config') | ||
const { emailListSubmit } = require('../src/lib/google-sheets/data') | ||
|
||
async function main({email, name}) { | ||
const auth = await getAuthToken({credentials}) | ||
return await emailListSubmit({auth, spreadsheetId, spreadsheetTab, email, name}) | ||
} | ||
|
||
function error(err, statusCode = 500) { | ||
return { | ||
body: err.toString(), | ||
headers: { 'Content-Type': 'application/json' }, | ||
statusCode | ||
} | ||
} | ||
|
||
function ok(data, statusCode = 200) { | ||
return { | ||
body: JSON.stringify(data), | ||
headers: { 'Content-Type': 'application/json' }, | ||
statusCode | ||
} | ||
} | ||
|
||
exports.handler = async (event, _context, _callback) => main(JSON.parse(event.body)).then(ok).catch(error) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
const { getAuthToken } = require('../src/lib/google-sheets/service') | ||
const { credentials, messages: {spreadsheetId, spreadsheetTab} } = require('../src/lib/google-sheets/config') | ||
const { messageSubmit } = require('../src/lib/google-sheets/data') | ||
|
||
async function main({name, email, message}) { | ||
const auth = await getAuthToken({credentials}) | ||
return await messageSubmit({auth, spreadsheetId, spreadsheetTab, name, email, message}) | ||
} | ||
|
||
function error(err, statusCode = 500) { | ||
return { | ||
body: err.toString(), | ||
headers: { 'Content-Type': 'application/json' }, | ||
statusCode | ||
} | ||
} | ||
|
||
function ok(data, statusCode = 200) { | ||
return { | ||
body: JSON.stringify(data), | ||
headers: { 'Content-Type': 'application/json' }, | ||
statusCode | ||
} | ||
} | ||
|
||
exports.handler = async (event, _context, _callback) => main(JSON.parse(event.body)).then(ok).catch(error) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
[build] | ||
command = "make build" | ||
publish = "out" | ||
functions = "functions/" | ||
|
||
[[plugins]] | ||
package = "@netlify/plugin-nextjs" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import React from 'react' | ||
import axios from 'axios' | ||
import styles from '@/styles/footer-email-list-form.module.css' | ||
|
||
const formInit = () => { | ||
return { | ||
name: '', | ||
email: '', | ||
className: styles.form, | ||
status: 'enabled' | ||
} | ||
} | ||
|
||
const formReducer = (state, action) => { | ||
switch (action.type) { | ||
case 'change': | ||
return {...state, ...action.payload} | ||
case 'disabled': | ||
return {...state, status: 'disabled', className: styles.formDisabled} | ||
case 'success': | ||
return {...state, status: 'success', className: styles.formSuccess} | ||
case 'reset': | ||
default: | ||
return formInit() | ||
} | ||
} | ||
|
||
export default function EmailListForm() { | ||
const [form, dispatchForm] = React.useReducer(formReducer, formInit()) | ||
|
||
const handleChange = (field) => { | ||
return (event) => dispatchForm({type: 'change', payload: {[field]: event.target.value}}) | ||
} | ||
|
||
const handleSubmit = (event) => { | ||
event.preventDefault() | ||
|
||
dispatchForm({type: 'disabled'}) | ||
|
||
const {name, email} = form | ||
|
||
axios | ||
.post('/.netlify/functions/email-list', {name, email}) | ||
.then(_response => dispatchForm({type: 'success'})) | ||
} | ||
|
||
return ( | ||
<div className="; relative"> | ||
<form onSubmit={handleSubmit} className={form.className}> | ||
<label> | ||
<div className="; hidden">Name</div> | ||
<div><input required type="text" placeholder="Name" value={form.name} onChange={handleChange('name')} disabled={form.disabled} /></div> | ||
</label> | ||
<label> | ||
<div className="; hidden">Email Address</div> | ||
<div><input required type="email" placeholder="Email Address" value={form.email} onChange={handleChange('email')} disabled={form.disabled} /></div> | ||
</label> | ||
<div> | ||
<button type="submit" disabled={form.disabled} className={styles.submitButton}>Subscribe Now</button> | ||
</div> | ||
</form> | ||
|
||
{form.status == 'success' && ( | ||
<div className={styles.successOverlay}> | ||
<div className="; flex flex-col text-center justify-evenly w-full h-full text-lg"> | ||
<div>Thanks for Subscribing!</div> | ||
<div> | ||
<button className={styles.successOkButton} onClick={() => dispatchForm({type: 'reset'})}>OK</button> | ||
</div> | ||
</div> | ||
</div> | ||
)} | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import React from 'react' | ||
import axios from 'axios' | ||
import styles from '@/styles/message-form.module.css' | ||
|
||
const formInit = () => { | ||
return { | ||
name: '', | ||
email: '', | ||
message: '', | ||
className: styles.form, | ||
status: 'enabled' | ||
} | ||
} | ||
|
||
const formReducer = (state, action) => { | ||
switch (action.type) { | ||
case 'change': | ||
return {...state, ...action.payload} | ||
case 'disabled': | ||
return {...state, status: 'disabled', className: styles.formDisabled} | ||
case 'success': | ||
return {...state, status: 'success', className: styles.formSuccess} | ||
case 'reset': | ||
default: | ||
return formInit() | ||
} | ||
} | ||
|
||
export default function MessageForm() { | ||
const [form, dispatchForm] = React.useReducer(formReducer, formInit()) | ||
|
||
const handleChange = (field) => { | ||
return (event) => dispatchForm({type: 'change', payload: {[field]: event.target.value}}) | ||
} | ||
|
||
const handleSubmit = (event) => { | ||
event.preventDefault() | ||
|
||
dispatchForm({type: 'disabled'}) | ||
|
||
const {name, email, message} = form | ||
|
||
axios | ||
.post('/.netlify/functions/message', {name, email, message}) | ||
.then(_response => dispatchForm({type: 'success'})) | ||
} | ||
|
||
return ( | ||
<div className="; relative p-4"> | ||
<form onSubmit={handleSubmit} className={form.className}> | ||
<label> | ||
<div className="; hidden">Name</div> | ||
<div><input required type="text" placeholder="Name" value={form.name} onChange={handleChange('name')} disabled={form.disabled} /></div> | ||
</label> | ||
<label> | ||
<div className="; hidden">Email Address</div> | ||
<div><input required type="email" placeholder="Email Address" value={form.email} onChange={handleChange('email')} disabled={form.disabled} /></div> | ||
</label> | ||
<label> | ||
<div className="; hidden">Message</div> | ||
<div><textarea required type="email" placeholder="Message" rows="5" value={form.message} onChange={handleChange('message')} disabled={form.disabled} /></div> | ||
</label> | ||
<div> | ||
<button type="submit" disabled={form.disabled} className={styles.submitButton}>Submit</button> | ||
</div> | ||
</form> | ||
|
||
{form.status == 'success' && ( | ||
<div className={styles.successOverlay}> | ||
<div className="; flex flex-col text-center justify-evenly w-full h-full text-lg"> | ||
<div>Thanks for submitting!</div> | ||
<div> | ||
<button className={styles.successOkButton} onClick={() => dispatchForm({type: 'reset'})}>OK</button> | ||
</div> | ||
</div> | ||
</div> | ||
)} | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
module.exports = { | ||
credentials: JSON.parse(Buffer.from(process.env.GOOGLE_APPLICATION_CREDENTIAL_BODY, 'base64')), | ||
emailList: { | ||
spreadsheetId: process.env.GOOGLE_SHEET_ID_EMAIL_LIST, | ||
spreadsheetTab: 'Raw Input', | ||
}, | ||
messages: { | ||
spreadsheetId: process.env.GOOGLE_SHEET_ID_MESSAGES, | ||
spreadsheetTab: 'Raw Input', | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
const { DateTime } = require('luxon') | ||
const { appendSpreadSheetValues } = require('./service.js') | ||
|
||
const zone = 'America/Chicago' | ||
const format = 'M/d/yyyy h:mm:ss' | ||
|
||
const submitted = () => DateTime.local().setZone(zone).toFormat(format) | ||
|
||
async function emailListSubmit({auth, spreadsheetId, spreadsheetTab, name, email}) { | ||
const sheetName = spreadsheetTab | ||
const values = [ | ||
[submitted(), name, email] | ||
] | ||
|
||
const response = await appendSpreadSheetValues({spreadsheetId, sheetName, auth, values}) | ||
|
||
return response | ||
} | ||
|
||
async function messageSubmit({auth, spreadsheetId, spreadsheetTab, name, email, message}) { | ||
const sheetName = spreadsheetTab | ||
const values = [ | ||
[submitted(), name, email, message] | ||
] | ||
|
||
const response = await appendSpreadSheetValues({spreadsheetId, sheetName, auth, values}) | ||
|
||
return response | ||
} | ||
|
||
module.exports = { | ||
emailListSubmit, | ||
messageSubmit, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
const { google } = require('googleapis') | ||
const { GoogleAuth } = require('google-auth-library') | ||
const sheets = google.sheets('v4') | ||
const SCOPES = [ | ||
'https://www.googleapis.com/auth/drive', | ||
'https://www.googleapis.com/auth/spreadsheets' | ||
] | ||
|
||
async function getAuthToken({credentials}) { | ||
const auth = new GoogleAuth({ | ||
credentials, | ||
scopes: SCOPES | ||
}) | ||
return auth.getClient() | ||
} | ||
|
||
async function getSpreadSheet({spreadsheetId, auth}) { | ||
return sheets.spreadsheets.get({ | ||
spreadsheetId, | ||
auth, | ||
}) | ||
} | ||
|
||
async function getSpreadSheetValues({spreadsheetId, auth, sheetName}) { | ||
return sheets.spreadsheets.values.get({ | ||
spreadsheetId, | ||
auth, | ||
range: sheetName | ||
}) | ||
} | ||
|
||
async function appendSpreadSheetValues({spreadsheetId, auth, sheetName, values}) { | ||
const valueInputOption = 'USER_ENTERED' | ||
const resource = { | ||
majorDimension: 'ROWS', | ||
values, | ||
} | ||
return sheets.spreadsheets.values.append({ | ||
spreadsheetId, | ||
auth, | ||
range: sheetName, | ||
valueInputOption, | ||
resource, | ||
}) | ||
} | ||
|
||
module.exports = { | ||
getAuthToken, | ||
getSpreadSheet, | ||
getSpreadSheetValues, | ||
appendSpreadSheetValues, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.