Skip to content
This repository has been archived by the owner on Jun 22, 2024. It is now read-only.

Commit

Permalink
chore: edit props with inputs for each type
Browse files Browse the repository at this point in the history
  • Loading branch information
Flowko committed Feb 3, 2024
1 parent 4446dc9 commit 102c869
Show file tree
Hide file tree
Showing 12 changed files with 698 additions and 75 deletions.
60 changes: 55 additions & 5 deletions client/components/CodeContainer.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
<script lang="ts" setup>
import { camelCase } from 'scule'
import JsonEditorVue from 'json-editor-vue'
import { copyTextToClipboard } from '@/util/copy-text-to-clipboard'
import 'vanilla-jsoneditor/themes/jse-theme-dark.css'
defineEmits(['setlang'])
const toast = useToast()
const { editorCode } = useTool()
const { template, email, props, renderEmail } = useEmail()
const { template, email, renderEmail } = useEmail()
const emailProps = ref(email.value.props)
function handleDownload(lang: 'html' | 'txt' | 'vue') {
const content = template.value[lang]
Expand Down Expand Up @@ -94,6 +98,10 @@ const items = computed(() => {
})
const tab = ref(0)
watchEffect(() => {
emailProps.value = email.value.props
})
</script>

<template>
Expand Down Expand Up @@ -124,13 +132,34 @@ const tab = ref(0)
<div v-if="item.code" class="w-full h-full" v-html="highlight(item.code, item.key)" />
<div v-else-if="item.key === 'props'" class="w-full h-full">
<UContainer class="py-5 flex flex-col gap-y-4">
<template v-for="(prop, idx) in props" :key="idx">
<UFormGroup v-if="prop.type === 'string'" :label="prop.label" :description="prop.description">
<template v-for="prop in email.props" :key="prop.label">
<UFormGroup v-if="prop.type === 'string'" size="lg" :label="prop.label" :description="prop.description">
<UInput v-model="prop.value" type="text" />
</UFormGroup>

<UButton label="Update" @click="renderEmail()" />
<UFormGroup v-if="prop.type === 'number'" size="lg" :label="prop.label" :description="prop.description">
<UInput v-model.number="prop.value" type="number" />
</UFormGroup>
<UFormGroup v-if="prop.type === 'boolean'" size="lg" :label="prop.label" :description="prop.description">
<UToggle v-model="prop.value" />
</UFormGroup>
<UFormGroup v-if="prop.type === 'object'" size="lg" :label="prop.label" :description="prop.description">
<JsonEditorVue
v-model="prop.value"
:class="[$colorMode.value === 'dark' ? 'jse-theme-dark' : 'light']"
class="json-editor-vue of-auto text-sm outline-none"
mode="tree" :navigation-bar="false" :indentation="2" :tab-size="2"
/>
</UFormGroup>
<UFormGroup v-if="prop.type === 'array'" size="lg" :label="prop.label" :description="prop.description">
<JsonEditorVue
v-model="prop.value"
:class="[$colorMode.value === 'dark' ? 'jse-theme-dark' : 'light']"
class="json-editor-vue of-auto text-sm outline-none"
mode="tree" :navigation-bar="false" :indentation="2" :tab-size="2"
/>
</UFormGroup>
</template>
<UButton size="lg" icon="i-ph-floppy-disk" block label="Update Props" @click="renderEmail(emailProps)" />
</UContainer>
</div>
</template>
Expand All @@ -150,4 +179,25 @@ const tab = ref(0)
overflow: auto;
white-space: break-spaces;
}
.dark,
.jse-theme-dark {
--jse-panel-background: #111 !important;
--jse-theme-color: #111 !important;
--jse-text-color-inverse: #fff !important;
--jse-main-border: none !important;
}
.json-editor-vue .no-main-menu {
border: none !important;
}
.json-editor-vue .jse-main {
min-height: 1em !important;
}
.json-editor-vue .jse-contents {
border-width: 0 !important;
border-radius: 5px !important;
}
</style>
62 changes: 2 additions & 60 deletions client/composables/useEmail.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,13 @@
import pretty from 'pretty'
import type { Result } from '@vue-email/compiler'
import { upperFirst } from 'scule'
import type { Email, Template } from '@/types/email'

function removeQuotes(inputString: any) {
// Check if the input is a string and has at least two characters
if (typeof inputString === 'string' && inputString.length >= 2) {
// Check if the string starts and ends with double quotes
if (inputString[0] === '"' && inputString[inputString.length - 1] === '"') {
// Remove the quotes and return the modified string
return inputString.slice(1, -1)
}
else {
// If the string doesn't have quotes at the start and end, return the original string
return inputString
}
}
else {
// If the input is not a valid string, return an empty string or handle it accordingly
return ''
}
}

export function useEmail() {
const emails = useState<Email[]>('emails')
const email = useState<Email>('email')
const sending = useState<boolean>('sending', () => false)
const refresh = useState<boolean>('refresh', () => false)
const template = useState<Template>('template')
const props = useState<{
label: string
value: any
type: string
description?: string
}[]>('props')

const { host } = useWindow()

Expand All @@ -51,15 +25,15 @@ export function useEmail() {
emails.value = data.value
}

const renderEmail = async () => {
const renderEmail = async (props?: Email['props']) => {
if (!email.value)
return null

const { data } = await useFetch<Result>(`/api/render/${email.value.filename}`, {
method: 'POST',
baseURL: host.value,
body: {
props: props.value,
props,
},
})

Expand All @@ -78,37 +52,6 @@ export function useEmail() {

if (found) {
email.value = found
try {
if (found.props) {
props.value = found.props.map((prop) => {
const value = removeQuotes(prop.default) || ''
const destructuredType = prop.type.split('|').map((type) => {
if (type === 'string')
return 'string'

if (type === 'number')
return 'number'

if (type === 'boolean')
return 'boolean'

if (type === 'object')
return 'object'

return 'string'
})

return {
label: upperFirst(prop.name),
type: destructuredType[0],
value,
}
})
}
}
catch (error) {
console.error(error)
}

await renderEmail()
}
Expand Down Expand Up @@ -171,7 +114,6 @@ export function useEmail() {
sending,
refresh,
template,
props,
getEmail,
sendTestEmail,
renderEmail,
Expand Down
33 changes: 33 additions & 0 deletions client/emails/github-access-token.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,31 @@ defineProps({
type: String,
default: 'John Doe',
},
string: {
type: String,
},
number: {
type: Number,
default: 0,
},
boolean: {
type: Boolean,
default: true,
},
array: {
type: Array,
default: () => [
{
key: 'value',
},
],
},
object: {
type: Object,
default: () => ({
key: 'value',
}),
},
})
const main = {
Expand Down Expand Up @@ -74,6 +99,14 @@ const footer = {
<strong>@{{ username }}</strong>, a personal access was created on your account.
</EText>

<p>
{{ string }}
{{ number }}
{{ boolean }}
{{ array }}
{{ object }}
</p>

<ESection :style="section">
<EText :style="text">
Hey <strong>{{ username }}</strong>!
Expand Down
3 changes: 3 additions & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@
"@types/splitpanes": "^2.2.6",
"@vueuse/core": "^10.7.2",
"@vueuse/nuxt": "^10.7.2",
"destr": "^2.0.2",
"html-to-text": "^9.0.5",
"json-editor-vue": "^0.12.0",
"json5": "^2.2.3",
"nuxt": "^3.9.3",
"pretty": "^2.0.0",
"scule": "^1.2.0",
Expand Down
60 changes: 58 additions & 2 deletions client/server/api/emails.get.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import path from 'node:path'
import { kebabCase, pascalCase } from 'scule'
import { kebabCase, pascalCase, upperFirst } from 'scule'

Check failure on line 2 in client/server/api/emails.get.ts

View workflow job for this annotation

GitHub Actions / ci

'upperFirst' is defined but never used
import { createComponentMetaCheckerByJsonConfig } from 'vue-component-meta'
import { destr } from 'destr'
import JSON5 from 'json5'
import type { Email } from '~/types/email'
import { createError, defineEventHandler, useStorage } from '#imports'

Expand Down Expand Up @@ -82,6 +84,58 @@ export default defineEventHandler(async () => {
return 0
})
emailProps = emailProps.map(stripeTypeScriptInternalTypesSchema)
const destructuredProps = emailProps.map((prop) => {
const destructuredType = prop.type.split('|').map((type) => {
type = type.trim()
const value = prop.default

if (type === 'string') {
return {
type: 'string',
value: destr(value) ?? '',
}
}

if (type === 'number') {
return {
type: 'number',
value: destr(value) || 0,
}
}

if (type === 'boolean') {
return {
type: 'boolean',
value: destr(value) || false,
}
}

if (type === 'object' || type.includes('Record') || type.includes('Record<')) {
return {
type: 'object',
value: value ? JSON5.parse(value) : {},
}
}

if (type === 'array' || type.includes('[]') || type.includes('Array') || type.includes('Array<')) {
return {
type: 'array',
value: value ? JSON5.parse(value) : [],
}
}

return {
type: 'string',
value: value ?? '',
}
})

return {
label: prop.name,
type: destructuredType[0].type,
value: destructuredType[0].value,
}
})

const content = (await useStorage('assets:emails').getItem(
email,
Expand All @@ -99,7 +153,7 @@ export default defineEventHandler(async () => {
size: emailData.size,
created: emailData.birthtime,
modified: emailData.mtime,
props: emailProps,
props: destructuredProps,
}
}),
)
Expand All @@ -114,6 +168,8 @@ export default defineEventHandler(async () => {
return emails
}
catch (error) {
console.error(error)

throw createError({
statusCode: 500,
statusMessage: 'Internal Server Error',
Expand Down
17 changes: 16 additions & 1 deletion client/server/api/render/[file].post.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { destr } from 'destr'
import { useCompiler } from '#vue-email'
import { createError, defineEventHandler } from '#imports'

Expand All @@ -9,7 +10,21 @@ export default defineEventHandler(async (event: any) => {
let props: any = null
if (body && body.props) {
props = body.props.reduce((acc: Record<string, any>, prop: any) => {
acc[prop.label.toLowerCase()] = prop.value
if (prop.type === 'string')
acc[prop.label] = destr(prop.value) || ''

if (prop.type === 'number')
acc[prop.label] = destr(prop.value) || 0

if (prop.type === 'boolean')
acc[prop.label] = destr(prop.value) || false

if (prop.type === 'object')
acc[prop.label] = destr(prop.value) || {}

if (prop.type === 'array')
acc[prop.label] = destr(prop.value) || []

return acc
}, {})
}
Expand Down
7 changes: 6 additions & 1 deletion client/types/email.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ export interface Email {
size: number
created: Date
modified: Date
props: PropertyMeta[]
props: {
label: string
value: any
type: string
description?: string
}[]
}

export interface Directory {
Expand Down
Loading

0 comments on commit 102c869

Please sign in to comment.