Compare commits

3 Commits
Author SHA1 Message Date
ztimson 5a5718abe4 Updated contact form
Build Website / Build Container (push) Successful in 34s
Build Website / Build NPM Project (push) Successful in 37s
Build Website / Deploy Container (push) Successful in 3s
Build Website / Tag Version (push) Successful in 9s
2026-08-07 13:52:28 -04:00
ztimson 7b18ef039a Merge remote-tracking branch 'origin/develop' into develop
Build Website / Build NPM Project (push) Successful in 27s
Build Website / Tag Version (push) Successful in 19s
Build Website / Build Container (push) Successful in 22s
Build Website / Deploy Container (push) Successful in 4s
2026-07-26 14:40:07 -04:00
ztimson c672dba977 Updated momentum lib 2026-07-26 14:40:03 -04:00
3 changed files with 161 additions and 66 deletions
+9 -18
View File
@@ -8,8 +8,8 @@
"name": "zakscode", "name": "zakscode",
"version": "1.0.0", "version": "1.0.0",
"dependencies": { "dependencies": {
"@ztimson/momentum": "^1.1.9", "@ztimson/momentum": "^1.5.4",
"@ztimson/utils": "^0.29.4", "@ztimson/utils": "^0.30.2",
"vue": "^3.3.11", "vue": "^3.3.11",
"vue-router": "^4.2.5" "vue-router": "^4.2.5"
}, },
@@ -1350,29 +1350,20 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@ztimson/momentum": { "node_modules/@ztimson/momentum": {
"version": "1.1.9", "version": "1.5.4",
"resolved": "https://registry.npmjs.org/@ztimson/momentum/-/momentum-1.1.9.tgz", "resolved": "https://registry.npmjs.org/@ztimson/momentum/-/momentum-1.5.4.tgz",
"integrity": "sha512-dKLtssAk32ibqDPqUlxaB5CttQQbvNQL3atW1V6l1hiOFiZ9YJbnLPCGXjiCTThfebyRwGiDwigfljcOZfo2Uw==", "integrity": "sha512-AW+ooNOX6deBFEMXnoMjDygznd6EgcB0UNJ187Bb9C4lreE0qe4dTyZCEwXETp0eC8UNglA4VXPb4vXeougA4Q==",
"dependencies": { "dependencies": {
"@ztimson/utils": "0.29.0" "@ztimson/utils": "^0.30.2"
}, },
"bin": { "bin": {
"build-models": "bin/build-models.mjs" "build-models": "bin/build-models.mjs"
} }
}, },
"node_modules/@ztimson/momentum/node_modules/@ztimson/utils": {
"version": "0.29.0",
"resolved": "https://registry.npmjs.org/@ztimson/utils/-/utils-0.29.0.tgz",
"integrity": "sha512-qwI8xwJGsC7Lxk8WsEtvD8KYDIvagEa1AP8cjyQVZ3FUDHZlTocyuQYen4MU4keXzNAgfh9CcCsXweIyvjZt0Q==",
"license": "MIT",
"dependencies": {
"var-persist": "^1.0.1"
}
},
"node_modules/@ztimson/utils": { "node_modules/@ztimson/utils": {
"version": "0.29.4", "version": "0.30.2",
"resolved": "https://registry.npmjs.org/@ztimson/utils/-/utils-0.29.4.tgz", "resolved": "https://registry.npmjs.org/@ztimson/utils/-/utils-0.30.2.tgz",
"integrity": "sha512-INIADSrstEfr9sN0xQkBrcfnUNG9cWqRjmk+q4IFq323jIe/xSHBMunpq8HIBumpIk5rYfPmdHxFhutpUxwEFg==", "integrity": "sha512-VD4uMVJnjTedGwvMdvX6KH/A/AE09feDtsEpo8mWpAtsaMRRGktcV9gpBXv/4jI+M7L2YEQgG+ukK0Yl1xpmmg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"var-persist": "^1.0.1" "var-persist": "^1.0.1"
+2 -2
View File
@@ -11,8 +11,8 @@
"type-check": "vue-tsc --build --force" "type-check": "vue-tsc --build --force"
}, },
"dependencies": { "dependencies": {
"@ztimson/momentum": "^1.1.9", "@ztimson/momentum": "^1.5.4",
"@ztimson/utils": "^0.29.4", "@ztimson/utils": "^0.30.2",
"vue": "^3.3.11", "vue": "^3.3.11",
"vue-router": "^4.2.5" "vue-router": "^4.2.5"
}, },
+150 -46
View File
@@ -1,5 +1,4 @@
<script setup lang="ts"> <script setup lang="ts">
import Icon from '@/components/icon.vue';
import {momentum} from '@/services/momentum.service'; import {momentum} from '@/services/momentum.service';
import {ref} from 'vue'; import {ref} from 'vue';
@@ -30,14 +29,6 @@ function validateEmail(email: string) {
return /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/.test(email); return /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/.test(email);
} }
function send(name: string, email: string, subject: string, message: string) {
return momentum.email.create({
to: ['zaktimson@gmail.com', email],
subject: `ZaksCode: ${subject}`,
body: `From: ${name} &lt;${email}&gt;<br><br>${message}`
});
}
function submit() { function submit() {
disable.value = true; disable.value = true;
const d = data.value; const d = data.value;
@@ -51,7 +42,7 @@ function submit() {
}; };
if(errors.value.name || errors.value.email || errors.value.subject || errors.value.message) return disable.value = false; if(errors.value.name || errors.value.email || errors.value.subject || errors.value.message) return disable.value = false;
send(d.name, d.email, d.subject, d.message).then(() => { momentum.data.create('Contact', d).then(() => {
done.value = true; done.value = true;
}).catch(err => { }).catch(err => {
console.error(err); console.error(err);
@@ -62,43 +53,156 @@ function submit() {
</script> </script>
<template> <template>
<!-- Success Banner --> <div>
<div v-if="done" class="alert alert-success"> <!-- Contact Form -->
<span>Success! We will be intouch shortly.</span> <form class="cf">
</div> <!-- Success Banner -->
<!-- Error Banner --> <div v-if="done" class="cf-banner cf-banner--success">
<div v-if="errors?.banner" class="alert alert-danger"> <i class="fa fa-check-circle"/> Success! We will be in touch shortly.
Error: {{ (errors as any).banner }} </div>
</div> <!-- Error Banner -->
<!-- Contact Form --> <div v-if="errors?.banner" class="cf-banner cf-banner--error">
<form> <i class="fa fa-exclamation-circle"/> Error: {{ (errors as any).banner }}
<div class="d-flex flex-column flex-md-row flex-wrap justify-content-between"> </div>
<!-- Name --> <div class="d-flex flex-c flex-md-r gap-2">
<div class="mb-2 p-0 pe-md-1 p-md-0" style="flex: 1 0 0;"> <!-- Name -->
<div class="input-group"> <div class="cf-field">
<span class="input-group-text"><icon name="user"/></span> <i class="fa fa-user cf-icon"/>
<input class="form-control" type="text" placeholder="Name" v-model="data.name" v-bind:class="{'is-invalid': errors?.name}" :disabled="disable" required> <input class="cf-input" type="text" placeholder="Name" v-model="data.name" :class="{'cf-input--error': errors?.name}" :disabled="disable" required>
</div>
<!-- Email -->
<div class="cf-field">
<i class="fa fa-envelope cf-icon"/>
<input class="cf-input" type="email" placeholder="Email" v-model="data.email" :class="{'cf-input--error': errors?.email}" :disabled="disable" required>
</div> </div>
</div> </div>
<!-- Email --> <!-- Subject -->
<div class="mb-2 p-0 ps-md-1" style="flex: 1 0 0;"> <div class="cf-field mt-2">
<div class="input-group"> <i class="fa fa-book cf-icon"/>
<span class="input-group-text"><icon name="envelope"/></span> <input class="cf-input" type="text" placeholder="Subject" v-model="data.subject" :class="{'cf-input--error': errors?.subject}" :disabled="disable" required>
<input class="form-control" type="email" placeholder="Email" v-model="data.email" v-bind:class="{'is-invalid': errors?.email}" :disabled="disable" required>
</div>
</div> </div>
</div> <!-- Message Body -->
<!-- Subject --> <textarea class="cf-input cf-textarea mt-2" placeholder="Message" rows="5" v-model="data.message" :class="{'cf-input--error': errors?.message}" :disabled="disable" required></textarea>
<div class="input-group mb-2"> <!-- Buttons -->
<span class="input-group-text"><icon name="book"/></span> <div class="cf-actions">
<input class="form-control" type="text" placeholder="Subject" v-model="data.subject" v-bind:class="{'is-invalid': errors?.subject}" :disabled="disable" required> <button type="button" class="cf-btn" @click="reset()">Reset</button>
</div> <button type="button" class="cf-btn cf-btn--primary" @click="submit" :disabled="disable">
<!-- Message Body --> <i class="fa fa-paper-plane"/> Send Message
<textarea class="form-control" placeholder="Message" rows="5" v-model="data.message" v-bind:class="{'is-invalid': errors?.message}" :disabled="disable" required></textarea> </button>
<!-- Buttons --> </div>
<div class="text-end pt-3"> </form>
<button type="button" class="btn rounded-pill ms-3" @click="reset()">Reset</button> </div>
<button type="button" class="btn btn-primary rounded-pill ms-3" @click="submit" :disabled="disable">Send Message</button>
</div>
</form>
</template> </template>
<style scoped>
/* Fields */
.cf-field {
flex: 1;
position: relative;
display: flex;
align-items: center;
}
.cf-icon {
position: absolute;
left: 0.85rem;
color: #2c2c2c;
opacity: 0.4;
font-size: 0.85rem;
pointer-events: none;
}
.cf-input {
width: 100%;
padding: 0.65rem 0.85rem 0.65rem 2.25rem;
background: #fff;
border: 1.5px solid rgba(44, 44, 44, 0.15);
border-radius: 0;
color: #2c2c2c;
font-size: 0.95rem;
transition: border-color 0.2s, box-shadow 0.2s;
outline: none;
}
.cf-input::placeholder {
color: rgba(44, 44, 44, 0.35);
}
.cf-input:focus {
border-color: var(--theme-primary);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--theme-primary) 25%, transparent);
}
.cf-input:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.cf-input--error {
border-color: #c0392b !important;
}
.cf-textarea {
padding-left: 0.85rem;
resize: vertical;
min-height: 130px;
}
/* Banners */
.cf-banner {
padding: 0.75rem 1rem;
margin-bottom: 1rem;
font-size: 0.9rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.cf-banner--success {
background: #ecfdea;
border-left: 3px solid #55c02b;
color: #2c2c2c;
}
.cf-banner--error {
background: #fdecea;
border-left: 3px solid #c0392b;
color: #2c2c2c;
}
/* Buttons */
.cf-actions {
display: flex;
justify-content: flex-end;
gap: 0.75rem;
margin-top: .5rem;
}
.cf-btn {
padding: 0.6rem 1.4rem;
border: 1.5px solid rgba(44, 44, 44, 0.2);
border-radius: 0;
background: transparent;
color: #2c2c2c;
font-size: 0.9rem;
cursor: pointer;
transition: background 0.2s, border-color 0.2s;
&:hover {
filter: brightness(0.8);
}
&:disabled {
opacity: 0.55;
cursor: not-allowed;
}
}
.cf-btn--primary {
position: relative;
background: var(--theme-primary);
border-color: var(--theme-primary);
color: var(--theme-primary-contrast);
font-weight: 500;
}
</style>