chipchoc/src/App.svelte
Badri Sunderarajan 27b41a4523 Allow re-editing draft after table has been generated
This does two things:

1. If the TKs in the draft has changed, add/remove the corresponding
   columns from the table

2. Either way, update the preview so it matches the draft text

All this only happens once you click the "Refresh" button, because
we don't want to be putting *too much* load on the app also 😜
2021-05-28 20:45:56 +05:30

303 lines
6.2 KiB
Svelte

<script>
import Importabular from "importabular"
import showdown from "showdown"
let converter = new showdown.Converter()
let editor
let emailContent = 'Hi [TK name],\n\nIt\'s been a while since we heard from you. How are you doing? Your last article [TK article] did quite well, and we were wondering if you\'d be interested in writing a sequel?\n\nCheers, \nThe Snipette Team[TK PS]'
let sheetOne
let step = 0
let tkList = []
let cookiePreviewText = ''
let cookiePreviewRow = 0
function showEmail() {
step = 1
}
function detectFields(value) {
if (!value) return []
let matches = value.match(/\[TK (\w+?)\]/gm)
let tkList = []
if (!!matches) {
let propertyRegex = new RegExp(/\[TK (\w+)\]/)
for (let m of matches) {
tkList.push(propertyRegex.exec(m)[1])
}
}
// remove duplicates
tkList = [...new Set(tkList)]
return tkList
}
$: tkList = detectFields(emailContent)
function showSheet() {
// Figure out columns
let columns = []
if (!tkList.includes('email')) {
columns.push({
label: 'email',
description: 'Email address of the recipient',
placeholder: 'someone@members.snipettemag.com',
})
}
for (let tk of tkList) {
columns.push({
label: tk,
})
}
// First sheet
sheetOne = new Importabular({
node: editor,
columns: columns,
onChange(data) {
cookiePreviewText = getPreview()
}
})
step = 2
// for debugging only
window.sheetOne = sheetOne
}
/**
* Refresh sheet
*
* Destroys the current sheet, but saves the data first so if
* there are any columns in the new sheet that matches the old
* ones, it'll add them back. (Columns meaning TKs, of course)
*/
function refreshSheet() {
let oldData = sheetOne.getData()
let oldColumns = sheetOne.columns.map(c => c.label)
sheetOne.destroy()
// now, make it up again with the new TKs
showSheet()
// figure out the new data
let newData = []
let tkOrder = []
// first, we figure out how the old column order corresponds to
// the new one
for (let col of sheetOne.columns.map(c => c.label)) {
tkOrder.push(oldColumns.indexOf(col))
}
// now we fill this, row by row
for (let oldRow of oldData) {
// skip empty rows
if (oldRow.every(v => !v)) continue
// now, we figure out what to put for the new row
let newRow = []
for (let tkIndex of tkOrder) {
if (tkIndex <= -1) {
// new TK; leave it blank
newRow.push('')
} else {
// old TK; keep it filled!
newRow.push(oldRow[tkIndex])
}
}
// and finally, when all is done, we save it.
newData.push(newRow)
}
// fill it with the new data
sheetOne.setData(newData)
// don't forget to refresh the preview!
cookiePreviewText = getPreview()
}
function incrementPreviewRow() {
let sheetLength = sheetOne.getData().length
// make sure it's not too big...
if (cookiePreviewRow >= sheetLength-1) {
cookiePreviewRow = sheetLength - 1
} else if (cookiePreviewRow < 0) {
// ...and not too small
cookiePreviewRow = 0
} else {
// if not, then make it "just right"! :)
cookiePreviewRow++
}
}
function decrementPreviewRow() {
let sheetLength = sheetOne.getData().length
// make sure it's not too small...
if (cookiePreviewRow >= sheetLength) {
// ...and not too big
cookiePreviewRow = sheetLength - 1
} else if (cookiePreviewRow <= 0) {
// if not, then make it "just right"! :)
cookiePreviewRow = 0
} else {
cookiePreviewRow--
}
}
function getPreview(row) {
if (!row) row = cookiePreviewRow
let previewText = emailContent
// get the row we're working on
if (!sheetOne) return '' // no sheet to get data from :(
let data = sheetOne.getData()
if (row < 0 || data.length < row) return previewText // no row to apply :(
let r = data[row]
for (let tk of tkList) {
// figure out which column holds values for this TK
let tkIndex = window.sheetOne.columns.findIndex(t => t.label == tk)
// replace it!
previewText = previewText.replace(new RegExp(`\\[TK ${tk}\\]`, 'g'), r[tkIndex])
}
return previewText
}
// automatically update
$: cookiePreviewText = getPreview(cookiePreviewRow)
function next() {
step += 1
}
</script>
<main>
<img src="/assets/email-baker.png" class="main-pic" alt="Email Oven"/>
<h1>Chip <span class="highlight">Choc</span></h1>
<h2>Cookie-cutter emails made easy.</h2>
{#if step != 0}
<h3 class="mt-3">Step {step}</h3>
{/if}
{#if step == 0}
<button on:click={showEmail}>Start Drafting</button>
{/if}
{#if step >= 1}
<div class="instructions">
<p>Compose your email below, leaving [TK stuff] to be replaced in the table. Make sure each TK is a single word: no spaces allowed!</p>
</div>
<div class="email-content">
<textarea bind:value={emailContent}/>
<p>
<strong>Detected fields:</strong>
{#each tkList as tk (tk)}
<span class="tag">{tk}</span>
{/each}
</p>
</div>
{/if}
{#if step >= 2}
<div class="instructions">
<p>Now, fill in the fields for each user. (You can also copy-paste rows and columns directly from Air, if the ordering is right!)</p>
</div>
{/if}
{#if step == 1}
<button on:click={showSheet}>Next</button>
{/if}
{#if step > 1}
<button on:click={refreshSheet}>Refresh</button>
{/if}
<div id="editor" bind:this={editor}></div>
{#if step == 2 && !!cookiePreviewText}
<h4 class="mt-3">Preview</h4>
<div class="cookie-preview">{@html converter.makeHtml(cookiePreviewText)}</div>
<button on:click={decrementPreviewRow}>&lt; Prev</button>
<button on:click={incrementPreviewRow}>Next &gt;</button>
{/if}
</main>
<style>
main {
text-align: center;
padding: 1em;
max-width: 240px;
margin: 0 auto;
}
h1 {
margin-bottom: 0;
}
h1 + h2 {
margin-top: 0;
}
.main-pic {
max-width: 100%;
}
@media (min-width: 640px) {
main {
max-width: none;
}
.main-pic {
max-width: 240px;
}
}
.instructions {
max-width: 640px;
margin: 0 auto;
}
.email-content {
margin: 4em auto;
}
.email-content textarea {
width: 100%;
max-width: 640px;
font-family: inherit;
min-height: 15em;
}
.tag {
margin: 0.2em;
padding: 0.5em 1em 0.5em 1em;
background: #ffe3ff;
border-radius: 1em;
}
.cookie-preview {
text-align: left;
max-width: 640px;
margin: auto;
}
</style>