implement simple messaging
This commit is contained in:
parent
c2f3d8d414
commit
ab347a47b2
4 changed files with 121 additions and 0 deletions
|
|
@ -7,6 +7,7 @@
|
||||||
block?: boolean;
|
block?: boolean;
|
||||||
children: Snippet;
|
children: Snippet;
|
||||||
color?: DaisyColor;
|
color?: DaisyColor;
|
||||||
|
full?: boolean;
|
||||||
glass?: boolean;
|
glass?: boolean;
|
||||||
outline?: boolean;
|
outline?: boolean;
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
|
|
@ -20,6 +21,7 @@
|
||||||
block = false,
|
block = false,
|
||||||
children,
|
children,
|
||||||
color,
|
color,
|
||||||
|
full = false,
|
||||||
glass = false,
|
glass = false,
|
||||||
outline = false,
|
outline = false,
|
||||||
onClick,
|
onClick,
|
||||||
|
|
@ -36,6 +38,7 @@
|
||||||
class:btn-outline={outline}
|
class:btn-outline={outline}
|
||||||
class:btn-block={block}
|
class:btn-block={block}
|
||||||
class:btn-wide={wide}
|
class:btn-wide={wide}
|
||||||
|
class:w-full={full}
|
||||||
class:glass
|
class:glass
|
||||||
class:btn-xs={size === 'xs'}
|
class:btn-xs={size === 'xs'}
|
||||||
class:btn-sm={size === 'sm'}
|
class:btn-sm={size === 'sm'}
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,4 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<a href="/app/sms" class="btn btn-ghost">SMS</a>
|
||||||
49
src/routes/app/sms/+page.server.ts
Normal file
49
src/routes/app/sms/+page.server.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { PhoneRegex } from '$lib/regex';
|
||||||
|
import { logger } from '$lib/server/logger';
|
||||||
|
import { twilioClient } from '$lib/server/twilio';
|
||||||
|
import { TwilioConfig } from '$lib/server/twilio/twilio.config';
|
||||||
|
import { fail, type Actions } from '@sveltejs/kit';
|
||||||
|
import zod from 'zod';
|
||||||
|
|
||||||
|
export const actions = {
|
||||||
|
push: async (event) => {
|
||||||
|
const form = await event.request.formData();
|
||||||
|
|
||||||
|
if (!form.has('phone')) {
|
||||||
|
return fail(400, { error: 'phone_missing' });
|
||||||
|
}
|
||||||
|
if (!form.get('message')) {
|
||||||
|
return fail(400, { error: 'message_missing' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
success: phoneSuccess,
|
||||||
|
data: phone,
|
||||||
|
error: phoneError,
|
||||||
|
} = zod.string().regex(PhoneRegex).safeParse(form.get('phone'));
|
||||||
|
if (!phoneSuccess) {
|
||||||
|
logger.error(phoneError);
|
||||||
|
return fail(400, { error: 'invalid_phone' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = form.get('message');
|
||||||
|
if (typeof message !== 'string') {
|
||||||
|
return fail(400, { error: 'invalid_message' });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await twilioClient.messages.create({
|
||||||
|
to: phone,
|
||||||
|
body: message,
|
||||||
|
from: TwilioConfig.twilio_phone_number,
|
||||||
|
});
|
||||||
|
logger.debug(result);
|
||||||
|
} catch (e) {
|
||||||
|
logger.error(e);
|
||||||
|
fail(500, { success: false });
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
} satisfies Actions;
|
||||||
67
src/routes/app/sms/+page.svelte
Normal file
67
src/routes/app/sms/+page.svelte
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { enhance } from '$app/forms';
|
||||||
|
import { Button } from '$lib/components/Actions';
|
||||||
|
import { Textarea, TextInput } from '$lib/components/DataInput';
|
||||||
|
import { fade } from 'svelte/transition';
|
||||||
|
import type { ActionData } from './$types';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
form: ActionData;
|
||||||
|
};
|
||||||
|
let { form }: Props = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#snippet PhoneIcon()}
|
||||||
|
<i class="fi fi-sr-phone-flip"></i>
|
||||||
|
{/snippet}
|
||||||
|
|
||||||
|
{#snippet MessageLabel()}
|
||||||
|
<i class="fi fi-sr-comment-alt"></i> Message
|
||||||
|
{/snippet}
|
||||||
|
|
||||||
|
{#snippet alert(message: string)}
|
||||||
|
<div role="alert" class="alert alert-error absolute -top-20" transition:fade>
|
||||||
|
<i class="fi fi-bs-octagon-xmark h-6 w-6 shrink-0"></i>
|
||||||
|
<span>{message}</span>
|
||||||
|
</div>
|
||||||
|
{/snippet}
|
||||||
|
|
||||||
|
<div class="page" transition:fade>
|
||||||
|
<div class="card bg-base-200 p-4 shadow-xl">
|
||||||
|
<div class="card-title justify-center">
|
||||||
|
<h2 class="text-2xl font-semibold">Send a Message</h2>
|
||||||
|
{#if form?.error}
|
||||||
|
{@render alert(form.error)}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<form id="sms" method="POST" action="?/push" use:enhance>
|
||||||
|
<div class="card-body">
|
||||||
|
<TextInput
|
||||||
|
type="tel"
|
||||||
|
name="phone"
|
||||||
|
start={PhoneIcon}
|
||||||
|
placeholder="Phone"
|
||||||
|
bordered
|
||||||
|
fade
|
||||||
|
/>
|
||||||
|
<Textarea
|
||||||
|
color="primary"
|
||||||
|
label={MessageLabel}
|
||||||
|
size="lg"
|
||||||
|
error={form?.error}
|
||||||
|
name="message"
|
||||||
|
form="sms"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="card-actions justify-center px-8">
|
||||||
|
<Button color="primary" type="submit" full>Send Message</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.page {
|
||||||
|
@apply flex flex-col items-center justify-around gap-24 py-[10%];
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
Add table
Reference in a new issue