initial
This commit is contained in:
commit
74ae93a96e
1 changed files with 94691 additions and 0 deletions
557
_resources/editForm2.html
Normal file
557
_resources/editForm2.html
Normal file
|
|
@ -0,0 +1,557 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Custom Audio Player with SVGs</title>
|
||||
<script
|
||||
src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"
|
||||
defer
|
||||
></script>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<style>
|
||||
.error-input {
|
||||
border-color: #ef4444 !important; /* Tailwind's red-500 */
|
||||
box-shadow: 0 0 0 1px #ef4444; /* optional subtle red glow */
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-white dark:bg-gray-900 min-h-screen p-4">
|
||||
<section
|
||||
class="relative overflow-hidden min-h-screen bg-white dark:bg-gray-900 flex items-center justify-center py-12"
|
||||
>
|
||||
<!-- Background elements -->
|
||||
<div class="absolute inset-0 overflow-hidden">
|
||||
<div
|
||||
class="absolute -top-1/2 -right-1/2 bg-blue-400/20 dark:bg-blue-600/20 blur-3xl w-96 h-96 rounded-full"
|
||||
></div>
|
||||
<div
|
||||
class="absolute -bottom-1/2 -left-1/2 bg-purple-400/20 dark:bg-purple-600/20 blur-3xl w-96 h-96 rounded-full"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div class="relative z-10 mx-auto px-4 w-full max-w-2xl">
|
||||
<!-- Header -->
|
||||
<div class="text-center mb-8">
|
||||
<h1
|
||||
class="text-3xl font-bold text-gray-900 leading-tight sm:text-4xl dark:text-white"
|
||||
>
|
||||
Complete Your Profile
|
||||
</h1>
|
||||
<p class="mt-2 text-gray-600 dark:text-gray-400">
|
||||
Tell us more about yourself to personalize your experience
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Profile Setup Form Container -->
|
||||
<div
|
||||
class="bg-white/80 dark:bg-gray-800/80 backdrop-blur-sm rounded-2xl shadow-xl p-8 border border-gray-200/50 dark:border-gray-700/50"
|
||||
x-data="{
|
||||
submitting: false,
|
||||
showSuccess: false,
|
||||
formData: {
|
||||
avatar: '',
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
bio: '',
|
||||
location: '',
|
||||
website: '',
|
||||
jobTitle: '',
|
||||
company: '',
|
||||
facebook: '',
|
||||
linkedin: ''
|
||||
},
|
||||
errors: {},
|
||||
|
||||
validateField(fieldName, value) {
|
||||
this.errors[fieldName] = '';
|
||||
|
||||
if (fieldName === 'firstName') {
|
||||
if (!value.trim()) return 'First name is required';
|
||||
}
|
||||
|
||||
if (fieldName === 'lastName') {
|
||||
if (!value.trim()) return 'Last name is required';
|
||||
}
|
||||
|
||||
if (fieldName === 'bio') {
|
||||
if (value.length > 200) return 'Bio must be less than 200 characters';
|
||||
}
|
||||
|
||||
if (fieldName === 'website') {
|
||||
if (value && !/^(https?:\/\/)?([\da-z.-]+)\.([a-z.]{2,6})([/\w .-]*)*\/?$/.test(value)) {
|
||||
return 'Please enter a valid website URL';
|
||||
}
|
||||
}
|
||||
|
||||
if (fieldName === 'facebook') {
|
||||
if (value && !/^(https?:\/\/)?(www\.)?facebook\.com\/[a-zA-Z0-9(\.\?)?]/.test(value)) {
|
||||
return 'Please enter a valid Facebook URL';
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
},
|
||||
|
||||
validateForm() {
|
||||
this.errors = {};
|
||||
let isValid = true;
|
||||
|
||||
// Validate required fields
|
||||
Object.keys(this.formData).forEach(field => {
|
||||
if (field === 'firstName' || field === 'lastName') {
|
||||
const error = this.validateField(field, this.formData[field]);
|
||||
if (error) {
|
||||
this.errors[field] = error;
|
||||
isValid = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Validate optional fields if they have values
|
||||
['bio', 'website', 'facebook'].forEach(field => {
|
||||
if (this.formData[field]) {
|
||||
const error = this.validateField(field, this.formData[field]);
|
||||
if (error) {
|
||||
this.errors[field] = error;
|
||||
isValid = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return isValid;
|
||||
},
|
||||
|
||||
async submitForm() {
|
||||
if (!this.validateForm()) {
|
||||
// Scroll to first error
|
||||
const firstError = document.querySelector('.error-input');
|
||||
if (firstError) {
|
||||
firstError.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
firstError.focus();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this.submitting = true;
|
||||
|
||||
// Simulate API call
|
||||
await new Promise(resolve => setTimeout(resolve, 1500));
|
||||
|
||||
// Show success message
|
||||
this.showSuccess = true;
|
||||
this.submitting = false;
|
||||
},
|
||||
|
||||
handleAvatarUpload(event) {
|
||||
const file = event.target.files[0];
|
||||
if (file) {
|
||||
if (file.size > 2 * 1024 * 1024) { // 2MB limit
|
||||
alert('File size must be less than 2MB');
|
||||
return;
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
this.formData.avatar = e.target.result;
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
}
|
||||
}"
|
||||
>
|
||||
<!-- Profile Setup Form -->
|
||||
<form @submit.prevent="submitForm" x-show="!showSuccess" x-transition>
|
||||
<div class="space-y-8">
|
||||
<!-- Avatar Section -->
|
||||
<div class="text-center">
|
||||
<div class="avatar-upload inline-block mb-4 relative">
|
||||
<div
|
||||
class="relative w-32 h-32 mx-auto rounded-full border-2 border-dashed border-gray-300 dark:border-gray-600 flex items-center justify-center overflow-hidden"
|
||||
>
|
||||
<!-- Profile Image -->
|
||||
<template x-if="formData.avatar">
|
||||
<img
|
||||
:src="formData.avatar"
|
||||
alt="Profile preview"
|
||||
class="w-full h-full object-cover rounded-full"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- Placeholder Icon -->
|
||||
<template x-if="!formData.avatar">
|
||||
<div
|
||||
class="flex items-center justify-center w-full h-full text-gray-400"
|
||||
>
|
||||
<svg
|
||||
class="w-12 h-12"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Hover Overlay -->
|
||||
<div
|
||||
class="absolute inset-0 bg-black bg-opacity-30 rounded-full flex items-center justify-center opacity-0 hover:opacity-100 transition-opacity duration-200 cursor-pointer"
|
||||
>
|
||||
<svg
|
||||
class="w-8 h-8 text-white"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z"
|
||||
></path>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M15 13a3 3 0 11-6 0 3 3 0 016 0z"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<!-- Hidden File Input -->
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
@change="handleAvatarUpload"
|
||||
class="absolute inset-0 w-full h-full opacity-0 cursor-pointer rounded-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">
|
||||
Click on the avatar to upload a profile picture
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Personal Information -->
|
||||
<div>
|
||||
<h3
|
||||
class="text-lg font-semibold text-gray-900 dark:text-white mb-4"
|
||||
>
|
||||
Personal Information
|
||||
</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label
|
||||
for="firstName"
|
||||
class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
|
||||
>First Name</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
id="firstName"
|
||||
x-model="formData.firstName"
|
||||
@blur="errors.firstName = validateField('firstName', formData.firstName)"
|
||||
:class="errors.firstName ? 'error-input' : ''"
|
||||
class="w-full px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:outline-none transition-all duration-200"
|
||||
placeholder="John"
|
||||
/>
|
||||
<div
|
||||
x-show="errors.firstName"
|
||||
x-text="errors.firstName"
|
||||
class="mt-1 text-sm text-red-600 dark:text-red-400"
|
||||
></div>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
for="lastName"
|
||||
class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
|
||||
>Last Name</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
id="lastName"
|
||||
x-model="formData.lastName"
|
||||
@blur="errors.lastName = validateField('lastName', formData.lastName)"
|
||||
:class="errors.lastName ? 'error-input' : ''"
|
||||
class="w-full px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:outline-none transition-all duration-200"
|
||||
placeholder="Doe"
|
||||
/>
|
||||
<div
|
||||
x-show="errors.lastName"
|
||||
x-text="errors.lastName"
|
||||
class="mt-1 text-sm text-red-600 dark:text-red-400"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bio -->
|
||||
<div>
|
||||
<label
|
||||
for="bio"
|
||||
class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
|
||||
>Bio</label
|
||||
>
|
||||
<textarea
|
||||
id="bio"
|
||||
x-model="formData.bio"
|
||||
rows="3"
|
||||
@blur="errors.bio = validateField('bio', formData.bio)"
|
||||
:class="errors.bio ? 'error-input' : ''"
|
||||
class="w-full px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:outline-none transition-all duration-200"
|
||||
placeholder="Tell us a bit about yourself..."
|
||||
></textarea>
|
||||
<div class="flex justify-between mt-1">
|
||||
<div
|
||||
x-show="errors.bio"
|
||||
x-text="errors.bio"
|
||||
class="text-sm text-red-600 dark:text-red-400"
|
||||
></div>
|
||||
<div
|
||||
class="text-sm text-gray-500 dark:text-gray-400"
|
||||
x-text="`${200 - formData.bio.length} characters remaining`"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Professional Information -->
|
||||
<div>
|
||||
<h3
|
||||
class="text-lg font-semibold text-gray-900 dark:text-white mb-4"
|
||||
>
|
||||
Professional Information
|
||||
</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label
|
||||
for="jobTitle"
|
||||
class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
|
||||
>Job Title</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
id="jobTitle"
|
||||
x-model="formData.jobTitle"
|
||||
class="w-full px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:outline-none transition-all duration-200"
|
||||
placeholder="Software Engineer"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
for="company"
|
||||
class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
|
||||
>Company</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
id="company"
|
||||
x-model="formData.company"
|
||||
class="w-full px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:outline-none transition-all duration-200"
|
||||
placeholder="Acme Inc."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Location & Website -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label
|
||||
for="location"
|
||||
class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
|
||||
>Location</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
id="location"
|
||||
x-model="formData.location"
|
||||
class="w-full px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:outline-none transition-all duration-200"
|
||||
placeholder="New York, NY"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
for="website"
|
||||
class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
|
||||
>Website</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
id="website"
|
||||
x-model="formData.website"
|
||||
@blur="errors.website = validateField('website', formData.website)"
|
||||
:class="errors.website ? 'error-input' : ''"
|
||||
class="w-full px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:outline-none transition-all duration-200"
|
||||
placeholder="https://example.com"
|
||||
/>
|
||||
<div
|
||||
x-show="errors.website"
|
||||
x-text="errors.website"
|
||||
class="mt-1 text-sm text-red-600 dark:text-red-400"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Social Links -->
|
||||
<div>
|
||||
<h3
|
||||
class="text-lg font-semibold text-gray-900 dark:text-white mb-4"
|
||||
>
|
||||
Social Links
|
||||
</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label
|
||||
for="facebook"
|
||||
class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1 flex items-center"
|
||||
>
|
||||
<svg
|
||||
class="w-4 h-4 mr-2 text-blue-600"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"
|
||||
/>
|
||||
</svg>
|
||||
Facebook
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="facebook"
|
||||
x-model="formData.facebook"
|
||||
@blur="errors.facebook = validateField('facebook', formData.facebook)"
|
||||
:class="errors.facebook ? 'error-input' : ''"
|
||||
class="w-full px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:outline-none transition-all duration-200"
|
||||
placeholder="https://facebook.com/username"
|
||||
/>
|
||||
<div
|
||||
x-show="errors.facebook"
|
||||
x-text="errors.facebook"
|
||||
class="mt-1 text-sm text-red-600 dark:text-red-400"
|
||||
></div>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
for="linkedin"
|
||||
class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1 flex items-center"
|
||||
>
|
||||
<svg
|
||||
class="w-4 h-4 mr-2 text-blue-600"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.238-5-5-5zm-11 19h-3v-11h3v11zm-1.5-12.268c-.966 0-1.75-.79-1.75-1.764s.784-1.764 1.75-1.764 1.75.79 1.75 1.764-.783 1.764-1.75 1.764zm13.5 12.268h-3v-5.604c0-3.368-4-3.113-4 0v5.604h-3v-11h3v1.765c1.396-2.586 7-2.777 7 2.476v6.759z"
|
||||
/>
|
||||
</svg>
|
||||
LinkedIn
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="linkedin"
|
||||
x-model="formData.linkedin"
|
||||
class="w-full px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:outline-none transition-all duration-200"
|
||||
placeholder="linkedin.com/in/username"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<div class="pt-4">
|
||||
<button
|
||||
type="submit"
|
||||
:disabled="submitting"
|
||||
class="w-full px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-70 disabled:cursor-not-allowed transition-colors duration-200 font-medium flex items-center justify-center"
|
||||
>
|
||||
<span x-show="!submitting">Complete Profile</span>
|
||||
<span x-show="submitting" class="flex items-center">
|
||||
<svg
|
||||
class="animate-spin -ml-1 mr-2 h-4 w-4 text-white"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle
|
||||
class="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
stroke-width="4"
|
||||
></circle>
|
||||
<path
|
||||
class="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
></path>
|
||||
</svg>
|
||||
Saving Profile...
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- Success Message -->
|
||||
<div
|
||||
x-show="showSuccess"
|
||||
x-transition:enter="transition ease-out duration-300"
|
||||
x-transition:enter-start="opacity-0 transform scale-95"
|
||||
x-transition:enter-end="opacity-100 transform scale-100"
|
||||
class="text-center py-8"
|
||||
>
|
||||
<div class="mb-4 flex justify-center">
|
||||
<div
|
||||
class="w-16 h-16 rounded-full bg-green-100 dark:bg-green-900 flex items-center justify-center success-checkmark"
|
||||
>
|
||||
<svg
|
||||
class="w-8 h-8 text-green-600 dark:text-green-400"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M5 13l4 4L19 7"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<h3 class="text-2xl font-bold text-gray-900 dark:text-white mb-2">
|
||||
Profile Updated Successfully!
|
||||
</h3>
|
||||
<p class="text-gray-600 dark:text-gray-400 mb-6">
|
||||
Your profile has been updated with the new information.
|
||||
</p>
|
||||
<div class="flex space-x-4">
|
||||
<a
|
||||
href="#"
|
||||
class="flex-1 px-4 py-2 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors duration-200 font-medium text-center"
|
||||
>
|
||||
View Profile
|
||||
</a>
|
||||
<a
|
||||
href="#"
|
||||
class="flex-1 px-4 py-2 bg-blue-600 text-white text-center rounded-lg hover:bg-blue-700 transition-colors duration-200 font-medium"
|
||||
>Go to Dashboard</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<p class="text-center">
|
||||
Get More Components | Templates in multiple Frameowkr by visiting
|
||||
<a href="https://wavykits.com" class="text-blue-600">wavykits.com</a>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in a new issue