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",
"version": "1.0.0",
"dependencies": {
"@ztimson/momentum": "^1.1.9",
"@ztimson/utils": "^0.29.4",
"@ztimson/momentum": "^1.5.4",
"@ztimson/utils": "^0.30.2",
"vue": "^3.3.11",
"vue-router": "^4.2.5"
},
@@ -1350,29 +1350,20 @@
"license": "MIT"
},
"node_modules/@ztimson/momentum": {
"version": "1.1.9",
"resolved": "https://registry.npmjs.org/@ztimson/momentum/-/momentum-1.1.9.tgz",
"integrity": "sha512-dKLtssAk32ibqDPqUlxaB5CttQQbvNQL3atW1V6l1hiOFiZ9YJbnLPCGXjiCTThfebyRwGiDwigfljcOZfo2Uw==",
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/@ztimson/momentum/-/momentum-1.5.4.tgz",
"integrity": "sha512-AW+ooNOX6deBFEMXnoMjDygznd6EgcB0UNJ187Bb9C4lreE0qe4dTyZCEwXETp0eC8UNglA4VXPb4vXeougA4Q==",
"dependencies": {
"@ztimson/utils": "0.29.0"
"@ztimson/utils": "^0.30.2"
},
"bin": {
"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": {
"version": "0.29.4",
"resolved": "https://registry.npmjs.org/@ztimson/utils/-/utils-0.29.4.tgz",
"integrity": "sha512-INIADSrstEfr9sN0xQkBrcfnUNG9cWqRjmk+q4IFq323jIe/xSHBMunpq8HIBumpIk5rYfPmdHxFhutpUxwEFg==",
"version": "0.30.2",
"resolved": "https://registry.npmjs.org/@ztimson/utils/-/utils-0.30.2.tgz",
"integrity": "sha512-VD4uMVJnjTedGwvMdvX6KH/A/AE09feDtsEpo8mWpAtsaMRRGktcV9gpBXv/4jI+M7L2YEQgG+ukK0Yl1xpmmg==",
"license": "MIT",
"dependencies": {
"var-persist": "^1.0.1"
+2 -2
View File
@@ -11,8 +11,8 @@
"type-check": "vue-tsc --build --force"
},
"dependencies": {
"@ztimson/momentum": "^1.1.9",
"@ztimson/utils": "^0.29.4",
"@ztimson/momentum": "^1.5.4",
"@ztimson/utils": "^0.30.2",
"vue": "^3.3.11",
"vue-router": "^4.2.5"
},
+150 -46
View File
@@ -1,5 +1,4 @@
<script setup lang="ts">
import Icon from '@/components/icon.vue';
import {momentum} from '@/services/momentum.service';
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);
}
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() {
disable.value = true;
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;
send(d.name, d.email, d.subject, d.message).then(() => {
momentum.data.create('Contact', d).then(() => {
done.value = true;
}).catch(err => {
console.error(err);
@@ -62,43 +53,156 @@ function submit() {
</script>
<template>
<!-- Success Banner -->
<div v-if="done" class="alert alert-success">
<span>Success! We will be intouch shortly.</span>
</div>
<!-- Error Banner -->
<div v-if="errors?.banner" class="alert alert-danger">
Error: {{ (errors as any).banner }}
</div>
<!-- Contact Form -->
<form>
<div class="d-flex flex-column flex-md-row flex-wrap justify-content-between">
<!-- Name -->
<div class="mb-2 p-0 pe-md-1 p-md-0" style="flex: 1 0 0;">
<div class="input-group">
<span class="input-group-text"><icon name="user"/></span>
<input class="form-control" type="text" placeholder="Name" v-model="data.name" v-bind:class="{'is-invalid': errors?.name}" :disabled="disable" required>
<div>
<!-- Contact Form -->
<form class="cf">
<!-- Success Banner -->
<div v-if="done" class="cf-banner cf-banner--success">
<i class="fa fa-check-circle"/> Success! We will be in touch shortly.
</div>
<!-- Error Banner -->
<div v-if="errors?.banner" class="cf-banner cf-banner--error">
<i class="fa fa-exclamation-circle"/> Error: {{ (errors as any).banner }}
</div>
<div class="d-flex flex-c flex-md-r gap-2">
<!-- Name -->
<div class="cf-field">
<i class="fa fa-user cf-icon"/>
<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>
<!-- Email -->
<div class="mb-2 p-0 ps-md-1" style="flex: 1 0 0;">
<div class="input-group">
<span class="input-group-text"><icon name="envelope"/></span>
<input class="form-control" type="email" placeholder="Email" v-model="data.email" v-bind:class="{'is-invalid': errors?.email}" :disabled="disable" required>
</div>
<!-- Subject -->
<div class="cf-field mt-2">
<i class="fa fa-book cf-icon"/>
<input class="cf-input" type="text" placeholder="Subject" v-model="data.subject" :class="{'cf-input--error': errors?.subject}" :disabled="disable" required>
</div>
</div>
<!-- Subject -->
<div class="input-group mb-2">
<span class="input-group-text"><icon name="book"/></span>
<input class="form-control" type="text" placeholder="Subject" v-model="data.subject" v-bind:class="{'is-invalid': errors?.subject}" :disabled="disable" required>
</div>
<!-- Message Body -->
<textarea class="form-control" placeholder="Message" rows="5" v-model="data.message" v-bind:class="{'is-invalid': errors?.message}" :disabled="disable" required></textarea>
<!-- Buttons -->
<div class="text-end pt-3">
<button type="button" class="btn rounded-pill ms-3" @click="reset()">Reset</button>
<button type="button" class="btn btn-primary rounded-pill ms-3" @click="submit" :disabled="disable">Send Message</button>
</div>
</form>
<!-- Message Body -->
<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>
<!-- Buttons -->
<div class="cf-actions">
<button type="button" class="cf-btn" @click="reset()">Reset</button>
<button type="button" class="cf-btn cf-btn--primary" @click="submit" :disabled="disable">
<i class="fa fa-paper-plane"/> Send Message
</button>
</div>
</form>
</div>
</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>