feat(forms): add inline blur validation for name and email
This commit is contained in:
parent
1079e8212f
commit
10f8cab6e3
3 changed files with 66 additions and 0 deletions
|
|
@ -32,7 +32,10 @@
|
||||||
class="form-input"
|
class="form-input"
|
||||||
type="text"
|
type="text"
|
||||||
required
|
required
|
||||||
|
@blur="validateName"
|
||||||
|
@input="if (fieldErrors.name) fieldErrors.name = ''"
|
||||||
>
|
>
|
||||||
|
<p v-if="fieldErrors.name" class="field-error">{{ fieldErrors.name }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label" for="accept-email">Email</label>
|
<label class="form-label" for="accept-email">Email</label>
|
||||||
|
|
@ -201,6 +204,13 @@ const form = reactive({
|
||||||
agreedToGuidelines: false,
|
agreedToGuidelines: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Inline blur validation (UI feedback only — does not block submission)
|
||||||
|
const fieldErrors = reactive({ name: "" });
|
||||||
|
|
||||||
|
const validateName = () => {
|
||||||
|
fieldErrors.name = form.name.trim() ? "" : "Please enter your name.";
|
||||||
|
};
|
||||||
|
|
||||||
const isFormValid = computed(() => {
|
const isFormValid = computed(() => {
|
||||||
return (
|
return (
|
||||||
form.name &&
|
form.name &&
|
||||||
|
|
@ -447,6 +457,12 @@ textarea.form-input {
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.field-error {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--ember);
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
/* ---- CIRCLE RADIOS ---- */
|
/* ---- CIRCLE RADIOS ---- */
|
||||||
.circle-radios {
|
.circle-radios {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,10 @@
|
||||||
class="form-input"
|
class="form-input"
|
||||||
type="text"
|
type="text"
|
||||||
required
|
required
|
||||||
|
@blur="validateName"
|
||||||
|
@input="if (fieldErrors.name) fieldErrors.name = ''"
|
||||||
>
|
>
|
||||||
|
<p v-if="fieldErrors.name" class="field-error">{{ fieldErrors.name }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label" for="join-email">Email</label>
|
<label class="form-label" for="join-email">Email</label>
|
||||||
|
|
@ -139,7 +142,10 @@
|
||||||
type="email"
|
type="email"
|
||||||
placeholder="you@example.com"
|
placeholder="you@example.com"
|
||||||
required
|
required
|
||||||
|
@blur="validateEmail"
|
||||||
|
@input="if (fieldErrors.email) fieldErrors.email = ''"
|
||||||
>
|
>
|
||||||
|
<p v-if="fieldErrors.email" class="field-error">{{ fieldErrors.email }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">Circle</label>
|
<label class="form-label">Circle</label>
|
||||||
|
|
@ -346,6 +352,23 @@ const errorMessage = ref("");
|
||||||
const successMessage = ref("");
|
const successMessage = ref("");
|
||||||
const cadence = ref("monthly"); // 'monthly' | 'annual'
|
const cadence = ref("monthly"); // 'monthly' | 'annual'
|
||||||
|
|
||||||
|
// Inline blur validation (UI feedback only — does not block submission)
|
||||||
|
const fieldErrors = reactive({ name: "", email: "" });
|
||||||
|
|
||||||
|
const validateName = () => {
|
||||||
|
fieldErrors.name = form.name.trim() ? "" : "Please enter your name.";
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateEmail = () => {
|
||||||
|
const value = form.email.trim();
|
||||||
|
if (!value) {
|
||||||
|
fieldErrors.email = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const ok = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
|
||||||
|
fieldErrors.email = ok ? "" : "Please enter a valid email address.";
|
||||||
|
};
|
||||||
|
|
||||||
// Flow overlay state — drives the post-submit full-viewport UI.
|
// Flow overlay state — drives the post-submit full-viewport UI.
|
||||||
// 'idle' = overlay hidden; user is editing the form.
|
// 'idle' = overlay hidden; user is editing the form.
|
||||||
// 'creating-customer' | 'opening-payment' | 'processing-payment'
|
// 'creating-customer' | 'opening-payment' | 'processing-payment'
|
||||||
|
|
@ -762,6 +785,11 @@ onUnmounted(() => {
|
||||||
.form-input::placeholder {
|
.form-input::placeholder {
|
||||||
color: var(--text-faint);
|
color: var(--text-faint);
|
||||||
}
|
}
|
||||||
|
.field-error {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--ember);
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
/* ---- CIRCLE RADIOS ---- */
|
/* ---- CIRCLE RADIOS ---- */
|
||||||
.circle-radios {
|
.circle-radios {
|
||||||
|
|
|
||||||
|
|
@ -173,9 +173,12 @@
|
||||||
type="email"
|
type="email"
|
||||||
placeholder="you@example.com"
|
placeholder="you@example.com"
|
||||||
autofocus
|
autofocus
|
||||||
|
@blur="validateNewEmail"
|
||||||
|
@input="if (fieldErrors.email) fieldErrors.email = ''"
|
||||||
@keydown.enter="handleUpdateEmail"
|
@keydown.enter="handleUpdateEmail"
|
||||||
@keydown.escape="cancelEmailEdit"
|
@keydown.escape="cancelEmailEdit"
|
||||||
>
|
>
|
||||||
|
<p v-if="fieldErrors.email" class="field-error">{{ fieldErrors.email }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="email-edit-actions">
|
<div class="email-edit-actions">
|
||||||
<button
|
<button
|
||||||
|
|
@ -317,6 +320,19 @@ const showEmailEdit = ref(false);
|
||||||
const newEmail = ref("");
|
const newEmail = ref("");
|
||||||
const isUpdatingEmail = ref(false);
|
const isUpdatingEmail = ref(false);
|
||||||
|
|
||||||
|
// Inline blur validation (UI feedback only — does not block submission)
|
||||||
|
const fieldErrors = reactive({ email: "" });
|
||||||
|
|
||||||
|
const validateNewEmail = () => {
|
||||||
|
const value = newEmail.value.trim();
|
||||||
|
if (!value) {
|
||||||
|
fieldErrors.email = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const ok = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
|
||||||
|
fieldErrors.email = ok ? "" : "Please enter a valid email address.";
|
||||||
|
};
|
||||||
|
|
||||||
// Payment history state
|
// Payment history state
|
||||||
const paymentHistory = ref([]);
|
const paymentHistory = ref([]);
|
||||||
const paymentHistoryLoading = ref(false);
|
const paymentHistoryLoading = ref(false);
|
||||||
|
|
@ -516,6 +532,7 @@ const handleUpdateCircle = async () => {
|
||||||
const cancelEmailEdit = () => {
|
const cancelEmailEdit = () => {
|
||||||
showEmailEdit.value = false;
|
showEmailEdit.value = false;
|
||||||
newEmail.value = "";
|
newEmail.value = "";
|
||||||
|
fieldErrors.email = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUpdateEmail = async () => {
|
const handleUpdateEmail = async () => {
|
||||||
|
|
@ -823,6 +840,11 @@ const confirmCancelMembership = async () => {
|
||||||
.email-edit .field input:focus {
|
.email-edit .field input:focus {
|
||||||
border-color: var(--candle);
|
border-color: var(--candle);
|
||||||
}
|
}
|
||||||
|
.email-edit .field .field-error {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--ember);
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
.email-edit-actions {
|
.email-edit-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue