base setup
This commit is contained in:
75
at_django_boilerplate/accounts/templates/change_password.html
Executable file
75
at_django_boilerplate/accounts/templates/change_password.html
Executable file
@@ -0,0 +1,75 @@
|
||||
{% extends "public_base.html" %}
|
||||
{% block content %}
|
||||
<div class="form-content my-3 p-3">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-5">
|
||||
<div class="card shadow-lg border-0 rounded-lg mt-0 mb-3">
|
||||
<div class="card-header justify-content-center">
|
||||
<h3 class="font-weight-light my-4 text-center">Change Your Password</h3>
|
||||
</div>
|
||||
{% if form.errors %}
|
||||
<div class="alert alert-danger alert-dismissible" role="alert">
|
||||
<div id="form_errors">
|
||||
{% for key, value in form.errors.items %}
|
||||
<strong>{{ value }}</strong>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="card-body">
|
||||
<form method="POST">
|
||||
{% csrf_token %}
|
||||
<div class="form-row">
|
||||
<div class="col-md-10 offset-md-1">
|
||||
<div class="form-group">
|
||||
<label class="small mb-1" for="id_old_password">Old Password</label>
|
||||
<input type="password" name="old_password" autocomplete="new-password"
|
||||
class="form-control" required id="id_old_password"
|
||||
placeholder="Enter Old Password"/>
|
||||
<span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="col-md-10 offset-md-1">
|
||||
<div class="form-group">
|
||||
<label class="small mb-1" for="id_new_password1">New Password</label>
|
||||
<input type="password" name="new_password1" autocomplete="new-password"
|
||||
class="form-control" required id="id_new_password1"
|
||||
placeholder="Enter New Password"/>
|
||||
<span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="col-md-10 offset-md-1">
|
||||
<div class="form-group">
|
||||
<label class="small mb-1" for="id_new_password2">New Password Confirmation</label>
|
||||
<input type="password" name="new_password2" autocomplete="new-password"
|
||||
required id="id_new_password2" class="form-control"
|
||||
placeholder="Confirm New Password"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="col-md-10 offset-md-1">
|
||||
<div class="form-group mt-0 mb-1">
|
||||
<button type="submit" class="col-md-12 btn btn-dark" id="reset">Update Password</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
596
at_django_boilerplate/accounts/templates/login.html
Executable file
596
at_django_boilerplate/accounts/templates/login.html
Executable file
@@ -0,0 +1,596 @@
|
||||
{% extends 'public_base.html' %}
|
||||
{% block title %}
|
||||
Login
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% load static %}
|
||||
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--primary: #6366f1;
|
||||
--accent: #4f46e5;
|
||||
--gradient-start: #6366f1;
|
||||
--gradient-end: #8b5cf6;
|
||||
--bg: #ffffff;
|
||||
--muted: #6b7280;
|
||||
--danger: #ef4444;
|
||||
--success: #10b981;
|
||||
--radius: 16px;
|
||||
--shadow: 0 20px 40px rgba(99, 102, 241, 0.15);
|
||||
--shadow-hover: 0 25px 50px rgba(99, 102, 241, 0.25);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: linear-gradient(135deg, #f0f4ff 0%, #fdf2ff 50%, #f0fdf4 100%);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.login-page {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 2rem;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.login-page::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
left: -50%;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
background: radial-gradient(circle, rgba(99, 102, 241, 0.05) 0%, transparent 70%);
|
||||
animation: float 20s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%, 100% { transform: translate(0, 0) rotate(0deg); }
|
||||
25% { transform: translate(10px, 10px) rotate(1deg); }
|
||||
50% { transform: translate(-5px, 15px) rotate(-1deg); }
|
||||
75% { transform: translate(15px, -5px) rotate(1deg); }
|
||||
}
|
||||
|
||||
.login-container {
|
||||
display: flex;
|
||||
max-width: 1200px;
|
||||
width: 100%;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(20px);
|
||||
border-radius: var(--radius);
|
||||
box-shadow: var(--shadow);
|
||||
overflow: hidden;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.login-left {
|
||||
flex: 1;
|
||||
padding: 4rem 3rem;
|
||||
background: white;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.login-left::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 4px;
|
||||
height: 100%;
|
||||
background: linear-gradient(to bottom, var(--gradient-start), var(--gradient-end));
|
||||
}
|
||||
|
||||
.form-container {
|
||||
max-width: 400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.logo-section {
|
||||
text-align: center;
|
||||
margin-bottom: 2.5rem;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
background: linear-gradient(135deg, var(--gradient-start), var(--gradient-end));
|
||||
border-radius: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 1rem;
|
||||
box-shadow: 0 10px 25px rgba(99, 102, 241, 0.3);
|
||||
}
|
||||
|
||||
.logo i {
|
||||
font-size: 1.8rem;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.form-container h3 {
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: 2rem;
|
||||
font-weight: 800;
|
||||
background: linear-gradient(135deg, var(--gradient-start), var(--gradient-end));
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form-container p.subtext {
|
||||
margin-bottom: 2.5rem;
|
||||
font-size: 1rem;
|
||||
color: var(--muted);
|
||||
text-align: center;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 1.5rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 1rem 1rem 1rem 3rem;
|
||||
border: 2px solid #e5e7eb;
|
||||
border-radius: 12px;
|
||||
font-size: 1rem;
|
||||
transition: all 0.3s ease;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
border-color: var(--accent);
|
||||
background: white;
|
||||
box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.1);
|
||||
outline: none;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.input-icon {
|
||||
position: absolute;
|
||||
left: 1rem;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: var(--muted);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.form-control:focus + .input-icon {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.form-options {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.remember-me {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.9rem;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.forgot-password {
|
||||
color: var(--accent);
|
||||
text-decoration: none;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.forgot-password:hover {
|
||||
color: var(--gradient-end);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.btn-submit {
|
||||
background: linear-gradient(135deg, var(--gradient-start), var(--gradient-end));
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 1rem 2rem;
|
||||
width: 100%;
|
||||
border-radius: 12px;
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 10px 25px rgba(99, 102, 241, 0.3);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.btn-submit:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: var(--shadow-hover);
|
||||
}
|
||||
|
||||
/* OTP Button */
|
||||
.btn-otp {
|
||||
background: linear-gradient(135deg, #10b981, #059669);
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.btn-otp:hover {
|
||||
background: linear-gradient(135deg, #059669, #047857);
|
||||
transform: translateY(-3px);
|
||||
box-shadow: var(--shadow-hover);
|
||||
}
|
||||
|
||||
.signup-link {
|
||||
text-align: center;
|
||||
font-size: 0.9rem;
|
||||
color: var(--muted);
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.signup-link a {
|
||||
color: var(--accent);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.login-right {
|
||||
flex: 1;
|
||||
background: linear-gradient(135deg, var(--gradient-start), var(--gradient-end));
|
||||
padding: 4rem 3rem;
|
||||
color: white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.login-right::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grid" width="10" height="10" patternUnits="userSpaceOnUse"><path d="M 10 0 L 0 0 0 10" fill="none" stroke="rgba(255,255,255,0.1)" stroke-width="0.5"/></pattern></defs><rect width="100" height="100" fill="url(%23grid)"/></svg>');
|
||||
}
|
||||
|
||||
.features-title {
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.feature-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.feature-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background: rgba(255,255,255,0.2);
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 1rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.alert {
|
||||
padding: 1rem 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
border-radius: 12px;
|
||||
font-size: 0.9rem;
|
||||
border-left: 4px solid;
|
||||
}
|
||||
|
||||
.alert-success { background: rgba(16,185,129,0.1); border-color: var(--success); color: #065f46; }
|
||||
.alert-danger { background: rgba(239,68,68,0.1); border-color: var(--danger); color: #991b1b; }
|
||||
|
||||
/* Modal Styles (No Bootstrap) */
|
||||
.otp-modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0; left: 0; width: 100%; height: 100%;
|
||||
background: rgba(0,0,0,0.5);
|
||||
z-index: 1000;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.otp-modal.active {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.otp-modal-content {
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
box-shadow: var(--shadow);
|
||||
width: 90%;
|
||||
max-width: 480px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.otp-modal-header {
|
||||
padding: 1.5rem 2rem;
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.otp-modal-body {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.otp-input-group {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
justify-content: center;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.otp-input {
|
||||
width: 56px;
|
||||
height: 64px;
|
||||
text-align: center;
|
||||
font-size: 1.8rem;
|
||||
font-weight: bold;
|
||||
border: 2px solid #e5e7eb;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.otp-input:focus {
|
||||
border-color: var(--accent);
|
||||
box-shadow: 0 0 0 4px rgba(99,102,241,0.1);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.login-container { flex-direction: column; max-width: 500px; }
|
||||
.login-left::before { display: none; }
|
||||
.login-right { display: none; }
|
||||
.login-left { padding: 3rem 2rem; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="login-page">
|
||||
<div class="login-container">
|
||||
<div class="login-left">
|
||||
<div class="form-container">
|
||||
<div class="logo-section">
|
||||
<div class="logo">
|
||||
<i class="fas fa-shield-alt"></i>
|
||||
</div>
|
||||
<h3>Welcome Back</h3>
|
||||
<p class="subtext">Sign in to your account to continue</p>
|
||||
</div>
|
||||
|
||||
{% if messages %}
|
||||
{% for message in messages %}
|
||||
<div class="alert {% if 'success' in message.tags %}alert-success{% else %}alert-danger{% endif %}">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<form method="POST" action="{% url 'login' %}">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="next" value="{{ request.GET.next }}">
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="username">Username</label>
|
||||
<div class="input-group">
|
||||
<input id="username" name="username" class="form-control" placeholder="Enter your username" required autofocus type="text">
|
||||
<div class="input-icon"><i class="fas fa-user"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="password">Password</label>
|
||||
<div class="input-group">
|
||||
<input type="password" id="password" name="password" class="form-control" placeholder="Enter your password" required>
|
||||
<div class="input-icon"><i class="fas fa-lock"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-options">
|
||||
<label class="remember-me">
|
||||
<input type="checkbox" name="remember"> Remember me
|
||||
</label>
|
||||
<a href="{% url 'password_reset' %}" class="forgot-password">Forgot password?</a>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-submit">
|
||||
<i class="fas fa-sign-in-alt"></i> Sign In
|
||||
</button>
|
||||
|
||||
<button type="button" class="btn-submit btn-otp" id="openOtpModal">
|
||||
<i class="fas fa-mobile-alt"></i> Login with OTP
|
||||
</button>
|
||||
|
||||
<div class="signup-link">
|
||||
Don't have an account? <a href="{% url 'signup' %}">Sign up here</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="login-right">
|
||||
<h2 class="features-title">Why Choose Us?</h2>
|
||||
<ul class="feature-list">
|
||||
<li class="feature-item">
|
||||
<div class="feature-icon"><i class="fas fa-rocket"></i></div>
|
||||
<div class="feature-text"><strong>Lightning Fast</strong><br>Quick and seamless login experience</div>
|
||||
</li>
|
||||
<li class="feature-item">
|
||||
<div class="feature-icon"><i class="fas fa-shield-alt"></i></div>
|
||||
<div class="feature-text"><strong>Bank-Level Security</strong><br>Your data is protected with enterprise-grade security</div>
|
||||
</li>
|
||||
<li class="feature-item">
|
||||
<div class="feature-icon"><i class="fas fa-sync-alt"></i></div>
|
||||
<div class="feature-text"><strong>Sync Across Devices</strong><br>Access your account from anywhere, anytime</div>
|
||||
</li>
|
||||
<li class="feature-item">
|
||||
<div class="feature-icon"><i class="fas fa-headset"></i></div>
|
||||
<div class="feature-text"><strong>24/7 Support</strong><br>Our team is always here to help you</div>
|
||||
</li>
|
||||
<li class="feature-item">
|
||||
<div class="feature-icon"><i class="fas fa-chart-line"></i></div>
|
||||
<div class="feature-text"><strong>Advanced Analytics</strong><br>Get insights into your business performance</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Custom OTP Modal (No Bootstrap) -->
|
||||
<div class="otp-modal" id="otpModal">
|
||||
<div class="otp-modal-content">
|
||||
<div class="otp-modal-header">
|
||||
<h5 class="fw-bold">Login with OTP</h5>
|
||||
<button type="button" id="closeOtpModal" style="background:none;border:none;font-size:1.5rem;cursor:pointer;">×</button>
|
||||
</div>
|
||||
<div class="otp-modal-body">
|
||||
<div id="step1">
|
||||
<p class="text-center text-muted mb-4">Enter your registered email or mobile number</p>
|
||||
<input type="text" id="otpIdentifier" class="form-control" placeholder="Email or Phone (+91...)" required autofocus>
|
||||
<button id="sendOtpBtn" class="btn-submit w-100 mt-4">
|
||||
<i class="fas fa-paper-plane"></i> Send OTP
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="step2" style="display:none;">
|
||||
<p class="text-center text-muted mb-4">Enter the 6-digit OTP sent to<br><strong id="sentTo"></strong></p>
|
||||
<div class="otp-input-group">
|
||||
<input type="text" class="otp-input" maxlength="1" inputmode="numeric">
|
||||
<input type="text" class="otp-input" maxlength="1" inputmode="numeric">
|
||||
<input type="text" class="otp-input" maxlength="1" inputmode="numeric">
|
||||
<input type="text" class="otp-input" maxlength="1" inputmode="numeric">
|
||||
<input type="text" class="otp-input" maxlength="1" inputmode="numeric">
|
||||
<input type="text" class="otp-input" maxlength="1" inputmode="numeric">
|
||||
</div>
|
||||
<button id="verifyOtpBtn" class="btn-submit w-100">
|
||||
<i class="fas fa-check-circle"></i> Verify & Login
|
||||
</button>
|
||||
<p class="text-center mt-3">
|
||||
<a href="#" id="resendOtp" class="text-muted small">Resend OTP</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Modal Controls
|
||||
const modal = document.getElementById('otpModal');
|
||||
const openBtn = document.getElementById('openOtpModal');
|
||||
const closeBtn = document.getElementById('closeOtpModal');
|
||||
const step1 = document.getElementById('step1');
|
||||
const step2 = document.getElementById('step2');
|
||||
const identifierInput = document.getElementById('otpIdentifier');
|
||||
const sentTo = document.getElementById('sentTo');
|
||||
const otpInputs = document.querySelectorAll('.otp-input');
|
||||
|
||||
openBtn.onclick = () => modal.classList.add('active');
|
||||
closeBtn.onclick = () => modal.classList.remove('active');
|
||||
modal.onclick = (e) => { if (e.target === modal) modal.classList.remove('active'); };
|
||||
|
||||
// Auto-focus OTP inputs
|
||||
otpInputs.forEach((input, i) => {
|
||||
input.addEventListener('input', () => {
|
||||
if (input.value && i < 5) otpInputs[i + 1].focus();
|
||||
});
|
||||
input.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Backspace' && !input.value && i > 0) otpInputs[i - 1].focus();
|
||||
});
|
||||
});
|
||||
|
||||
// Send OTP
|
||||
document.getElementById('sendOtpBtn').onclick = async () => {
|
||||
const identifier = identifierInput.value.trim();
|
||||
if (!identifier) return alert('Please enter email or phone');
|
||||
|
||||
const btn = document.getElementById('sendOtpBtn');
|
||||
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Sending...';
|
||||
btn.disabled = true;
|
||||
|
||||
const res = await fetch("{% url 'request_otp' %}", {
|
||||
method: 'POST',
|
||||
headers: { 'X-CSRFToken': '{{ csrf_token }}' },
|
||||
body: new URLSearchParams({ 'identifier': identifier })
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
step1.style.display = 'none';
|
||||
step2.style.display = 'block';
|
||||
sentTo.textContent = identifier;
|
||||
otpInputs[0].focus();
|
||||
} else {
|
||||
alert('Failed to send OTP');
|
||||
}
|
||||
btn.innerHTML = '<i class="fas fa-paper-plane"></i> Send OTP';
|
||||
btn.disabled = false;
|
||||
};
|
||||
|
||||
// Verify OTP
|
||||
document.getElementById('verifyOtpBtn').onclick = async () => {
|
||||
const otp = Array.from(otpInputs).map(i => i.value).join('');
|
||||
if (otp.length !== 6) return alert('Enter full 6-digit OTP');
|
||||
|
||||
const btn = document.getElementById('verifyOtpBtn');
|
||||
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Verifying...';
|
||||
btn.disabled = true;
|
||||
|
||||
const res = await fetch("{% url 'verify_otp' %}", {
|
||||
method: 'POST',
|
||||
headers: { 'X-CSRFToken': '{{ csrf_token }}' },
|
||||
body: new URLSearchParams({ 'otp': otp })
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
window.location.href = "{% url 'home' %}";
|
||||
} else {
|
||||
alert('Invalid or expired OTP');
|
||||
}
|
||||
btn.innerHTML = '<i class="fas fa-check-circle"></i> Verify & Login';
|
||||
btn.disabled = false;
|
||||
};
|
||||
|
||||
// Resend
|
||||
document.getElementById('resendOtp').onclick = (e) => {
|
||||
e.preventDefault();
|
||||
step2.style.display = 'none';
|
||||
step1.style.display = 'block';
|
||||
identifierInput.value = sentTo.textContent;
|
||||
identifierInput.focus();
|
||||
};
|
||||
|
||||
// Loading spinner on normal login
|
||||
document.querySelector('form').addEventListener('submit', function() {
|
||||
this.querySelector('.btn-submit').innerHTML = '<i class="fas fa-spinner fa-spin"></i> Signing In...';
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
31
at_django_boilerplate/accounts/templates/otp/otp_email.html
Executable file
31
at_django_boilerplate/accounts/templates/otp/otp_email.html
Executable file
@@ -0,0 +1,31 @@
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Your OTP for {{ purpose|capfirst }}</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; background-color: #f9f9f9; color: #333; }
|
||||
.container { max-width: 600px; margin: auto; padding: 30px; background-color: #fff; border-radius: 8px; }
|
||||
.otp-box { font-size: 24px; font-weight: bold; color: #2c3e50; background: #ecf0f1; padding: 15px; text-align: center; border-radius: 6px; margin: 20px 0; }
|
||||
.footer { font-size: 12px; color: #999; margin-top: 30px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h2>Hello,</h2>
|
||||
<p>You requested an OTP for <strong>{{ purpose|capfirst }}</strong>.</p>
|
||||
<p>Your One-Time Password (OTP) is:</p>
|
||||
<div class="otp-box">{{ otp }}</div>
|
||||
<p>This OTP is valid for <strong>{{ validity_minutes }} minutes</strong>.</p>
|
||||
|
||||
<p><strong>⚠️ Do not share this OTP with anyone.</strong></p>
|
||||
|
||||
<p>If you did not request this OTP, please ignore this email or <a href="mailto:support@example.com">contact support</a> immediately.</p>
|
||||
|
||||
<div class="footer">
|
||||
This is an automated message. Please do not reply.
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
66
at_django_boilerplate/accounts/templates/profile/profile.html
Executable file
66
at_django_boilerplate/accounts/templates/profile/profile.html
Executable file
@@ -0,0 +1,66 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}User Profile{% endblock %}
|
||||
{% load crispy_forms_tags %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<!-- Profile Picture Update Modal -->
|
||||
<div class="fixed inset-0 z-50 hidden overflow-y-auto bg-black bg-opacity-60" id="profilePicModal">
|
||||
<div class="flex items-center justify-center min-h-screen px-4">
|
||||
<div class="bg-white rounded-xl shadow-2xl w-full max-w-md">
|
||||
<div class="flex justify-between items-center p-6 border-b border-gray-200">
|
||||
<h3 class="text-xl font-semibold text-[#1E40AF]">Update Profile Picture</h3>
|
||||
<button onclick="document.getElementById('profilePicModal').classList.add('hidden')" class="text-gray-500 hover:text-[#1E40AF] text-2xl transition-colors">×</button>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<form method="POST" action="{% url 'update_profile_picture' %}" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
{{ update_profile_photo_form|crispy }}
|
||||
<button type="submit" class="mt-4 w-full bg-[#1E40AF] hover:bg-[#1E3A8A] text-white font-semibold py-2.5 px-4 rounded-lg transition-colors duration-200">Save Changes</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Profile Content -->
|
||||
<div class="max-w-6xl mx-auto px-4 py-12">
|
||||
<div class="bg-white rounded-xl shadow-xl overflow-hidden">
|
||||
<div class="flex flex-col md:flex-row">
|
||||
|
||||
<!-- Left Sidebar -->
|
||||
<div class="bg-gray-50 md:w-1/3 flex flex-col items-center justify-center p-8 text-center">
|
||||
<img src="{{ user.profile_photo.url }}" alt="Profile Image" class="w-36 h-36 rounded-full object-cover mb-6 shadow-lg border-4 border-white">
|
||||
<h2 class="text-2xl font-bold text-[#1E40AF]">{{ user }}</h2>
|
||||
<a href="{% url 'update_profile' user.id %}" class="mt-6 w-full bg-[#1E40AF] text-white py-2.5 rounded-lg hover:bg-[#1E3A8A] font-semibold transition-colors duration-200">Update Profile</a>
|
||||
<button onclick="document.getElementById('profilePicModal').classList.remove('hidden')" class="mt-3 w-full bg-[#1E40AF] text-white py-2.5 rounded-lg hover:bg-[#1E3A8A] font-semibold transition-colors duration-200">Update Profile Picture</button>
|
||||
</div>
|
||||
|
||||
<!-- Right Content -->
|
||||
<div class="md:w-2/3 p-8">
|
||||
<h3 class="text-xl font-semibold text-[#1E40AF] mb-4 border-b border-gray-200 pb-3">Account Information</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
|
||||
<div>
|
||||
<p class="text-sm font-semibold text-gray-500">Email</p>
|
||||
<p class="text-[#1E3A8A]">{{ user.email }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm font-semibold text-gray-500">Phone</p>
|
||||
<p class="text-[#1E3A8A]">{{ user.get_decrypted_contact_number }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 class="text-xl font-semibold text-[#1E40AF] mb-4 border-b border-gray-200 pb-3">Social Media</h3>
|
||||
<div class="flex space-x-6 mt-4">
|
||||
<a href="#" class="text-[#1E40AF] hover:text-[#1E3A8A] text-2xl transition-colors"><i class="mdi mdi-facebook"></i></a>
|
||||
<a href="#" class="text-[#1E40AF] hover:text-[#1E3A8A] text-2xl transition-colors"><i class="mdi mdi-twitter"></i></a>
|
||||
<a href="#" class="text-[#1E40AF] hover:text-[#1E3A8A] text-2xl transition-colors"><i class="mdi mdi-instagram"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
21
at_django_boilerplate/accounts/templates/profile/update_profile.html
Executable file
21
at_django_boilerplate/accounts/templates/profile/update_profile.html
Executable file
@@ -0,0 +1,21 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}
|
||||
Update Profile
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto max-w-lg p-6 bg-white rounded-lg shadow-lg mt-10">
|
||||
<h2 class="text-3xl font-bold text-center text-gray-800 mb-6">Update Profile</h2>
|
||||
|
||||
<form method="POST" class="space-y-6">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
|
||||
<button type="submit" class="w-full bg-gradient-to-r from-indigo-500 to-purple-500 hover:from-indigo-600 hover:to-purple-600 text-white font-bold py-2 px-4 rounded-lg shadow-lg transition duration-300">
|
||||
Save changes
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
14
at_django_boilerplate/accounts/templates/profile/update_profile_picture.html
Executable file
14
at_django_boilerplate/accounts/templates/profile/update_profile_picture.html
Executable file
@@ -0,0 +1,14 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}
|
||||
Update Profile Picture
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<h2>Update Profile Picture</h2>
|
||||
<form method="POST" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<button type="submit" class="btn btn-primary">Save changes</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
21
at_django_boilerplate/accounts/templates/registration/create_user.html
Executable file
21
at_django_boilerplate/accounts/templates/registration/create_user.html
Executable file
@@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Create User</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Create User</h2>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<button type="submit">Create</button>
|
||||
</form>
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="{% url 'index' %}">Index</a></li>
|
||||
<li><a href="{% url 'home' %}">Home</a></li>
|
||||
<li><a href="{% url 'user_list' %}">User List</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</body>
|
||||
</html>
|
||||
316
at_django_boilerplate/accounts/templates/registration/signup.html
Executable file
316
at_django_boilerplate/accounts/templates/registration/signup.html
Executable file
@@ -0,0 +1,316 @@
|
||||
{% extends 'public_base.html' %}
|
||||
{% load static %}
|
||||
{% load custom_tags %}
|
||||
|
||||
{% block title %}Sign Up | Register your startup{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="min-h-screen flex items-center justify-center bg-gradient-to-br from-indigo-50 to-purple-50 py-8 px-4 sm:px-6 lg:px-8">
|
||||
<div class="w-full max-w-6xl">
|
||||
<div class="bg-white shadow-xl rounded-2xl overflow-hidden border border-gray-100">
|
||||
<div class="flex flex-col lg:flex-row">
|
||||
<!-- Left Side - Image Card (Hidden on mobile) -->
|
||||
<div class="hidden lg:block lg:w-2/5 bg-gradient-to-br from-indigo-600 to-purple-700 p-8 lg:p-12">
|
||||
<div class="flex flex-col justify-center items-center text-center text-white h-full">
|
||||
<div class="max-w-sm">
|
||||
<img src="{% static 'img/images or rys/Startup India Registration/hero1.png' %}" alt="Startup Registration" class="w-full h-48 lg:h-56 mb-6 lg:mb-8 rounded-lg">
|
||||
<h2 class="text-2xl font-bold mb-4">Start Your Entrepreneurial Journey</h2>
|
||||
<div class="space-y-4 text-left">
|
||||
<div class="flex items-start">
|
||||
<i class="fas fa-check-circle text-green-300 mr-3 mt-1 flex-shrink-0"></i>
|
||||
<span class="text-sm">Quick and hassle-free registration process</span>
|
||||
</div>
|
||||
<div class="flex items-start">
|
||||
<i class="fas fa-check-circle text-green-300 mr-3 mt-1 flex-shrink-0"></i>
|
||||
<span class="text-sm">Expert guidance for startup compliance</span>
|
||||
</div>
|
||||
<div class="flex items-start">
|
||||
<i class="fas fa-check-circle text-green-300 mr-3 mt-1 flex-shrink-0"></i>
|
||||
<span class="text-sm">Secure and confidential data handling</span>
|
||||
</div>
|
||||
<div class="flex items-start">
|
||||
<i class="fas fa-check-circle text-green-300 mr-3 mt-1 flex-shrink-0"></i>
|
||||
<span class="text-sm">Dedicated support for your business growth</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right Side - Form Card -->
|
||||
<div class="w-full lg:w-3/5 py-8 lg:py-10 px-6 lg:px-8">
|
||||
<!-- Mobile Header with Logo -->
|
||||
<div class="lg:hidden mb-6 text-center">
|
||||
<div class="flex justify-center mb-4">
|
||||
<div class="w-16 h-16 bg-indigo-600 rounded-full flex items-center justify-center">
|
||||
<i class="fas fa-rocket text-white text-2xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
<h2 class="text-2xl font-bold text-gray-900 mb-2">Create Your Account</h2>
|
||||
<p class="text-sm text-gray-600">Join thousands of successful startups</p>
|
||||
</div>
|
||||
|
||||
<!-- Desktop Header -->
|
||||
<div class="hidden lg:block text-center mb-8">
|
||||
<h2 class="text-3xl font-bold text-gray-900 mb-2">Create Your Account</h2>
|
||||
<p class="text-gray-600">Start your entrepreneurial journey with us today</p>
|
||||
</div>
|
||||
|
||||
<!-- Error Display -->
|
||||
{% if form.errors %}
|
||||
<div class="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-lg mb-6 animate-pulse">
|
||||
<div class="flex items-center">
|
||||
<i class="fa-solid fa-exclamation-circle mr-2"></i>
|
||||
<strong class="font-medium">Please correct the following errors:</strong>
|
||||
</div>
|
||||
<ul class="list-disc list-inside mt-2 ml-2 text-sm">
|
||||
{% for field, errors in form.errors.items %}
|
||||
{% for error in errors %}
|
||||
<li>{{ error }}</li>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Email Validation Status -->
|
||||
<div id="email-status" class="flex items-center space-x-2 text-sm mb-4 hidden">
|
||||
<span id="email-icon" class="text-lg"></span>
|
||||
<span id="email-text"></span>
|
||||
</div>
|
||||
|
||||
<form method="post" action="{% url 'signup' %}" class="space-y-6" novalidate>
|
||||
{% csrf_token %}
|
||||
|
||||
<!-- Personal Information Section -->
|
||||
<div class="space-y-5">
|
||||
<div class="flex items-center mb-2">
|
||||
<div class="w-8 h-8 bg-indigo-100 rounded-full flex items-center justify-center mr-3">
|
||||
<i class="fas fa-user text-indigo-600 text-sm"></i>
|
||||
</div>
|
||||
<h3 class="text-lg font-semibold text-gray-900">Personal Information</h3>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Name Fields -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<!-- First Name -->
|
||||
<div>
|
||||
<label for="{{ form.first_name.id_for_label }}" class="block text-sm font-medium text-gray-700 mb-2">
|
||||
First Name <span class="text-red-500">*</span>
|
||||
</label>
|
||||
<div class="relative">
|
||||
<span class="absolute inset-y-0 left-0 pl-3 flex items-center text-gray-400">
|
||||
<i class="fa-solid fa-user"></i>
|
||||
</span>
|
||||
{{ form.first_name|add_class:"appearance-none block w-full pl-10 px-3 py-3 border border-gray-300 rounded-lg shadow-sm placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition duration-200" }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Last Name -->
|
||||
<div>
|
||||
<label for="{{ form.last_name.id_for_label }}" class="block text-sm font-medium text-gray-700 mb-2">
|
||||
Last Name <span class="text-red-500">*</span>
|
||||
</label>
|
||||
<div class="relative">
|
||||
<span class="absolute inset-y-0 left-0 pl-3 flex items-center text-gray-400">
|
||||
<i class="fa-solid fa-user"></i>
|
||||
</span>
|
||||
{{ form.last_name|add_class:"appearance-none block w-full pl-10 px-3 py-3 border border-gray-300 rounded-lg shadow-sm placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition duration-200" }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Contact & DOB Fields -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<!-- Contact Number -->
|
||||
<div>
|
||||
<label for="{{ form.contact_number.id_for_label }}" class="block text-sm font-medium text-gray-700 mb-2">
|
||||
Contact Number
|
||||
</label>
|
||||
<div class="relative">
|
||||
<span class="absolute inset-y-0 left-0 pl-3 flex items-center text-gray-400">
|
||||
<i class="fa-solid fa-phone"></i>
|
||||
</span>
|
||||
{{ form.contact_number|add_class:"appearance-none block w-full pl-10 px-3 py-3 border border-gray-300 rounded-lg shadow-sm placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition duration-200" }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Email Field -->
|
||||
<div>
|
||||
<label for="{{ form.email.id_for_label }}" class="block text-sm font-medium text-gray-700 mb-2">
|
||||
Email Address <span class="text-red-500">*</span>
|
||||
</label>
|
||||
<div class="relative">
|
||||
<span class="absolute inset-y-0 left-0 pl-3 flex items-center text-gray-400">
|
||||
<i class="fa-solid fa-envelope"></i>
|
||||
</span>
|
||||
{{ form.email|add_class:"appearance-none block w-full pl-10 px-3 py-3 border border-gray-300 rounded-lg shadow-sm placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition duration-200" }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Password Section -->
|
||||
<div class="space-y-5">
|
||||
<div class="flex items-center mb-2">
|
||||
<div class="w-8 h-8 bg-indigo-100 rounded-full flex items-center justify-center mr-3">
|
||||
<i class="fas fa-lock text-indigo-600 text-sm"></i>
|
||||
</div>
|
||||
<h3 class="text-lg font-semibold text-gray-900">Security</h3>
|
||||
</div>
|
||||
|
||||
<!-- Password Field -->
|
||||
<div>
|
||||
<label for="{{ form.password.id_for_label }}" class="block text-sm font-medium text-gray-700 mb-2">
|
||||
Password <span class="text-red-500">*</span>
|
||||
</label>
|
||||
<div class="relative">
|
||||
<span class="absolute inset-y-0 left-0 pl-3 flex items-center text-gray-400">
|
||||
<i class="fa-solid fa-lock"></i>
|
||||
</span>
|
||||
{{ form.password|add_class:"appearance-none block w-full pl-10 px-3 py-3 border border-gray-300 rounded-lg shadow-sm placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition duration-200" }}
|
||||
</div>
|
||||
|
||||
<!-- Compact Password Requirements -->
|
||||
<div class="mt-2">
|
||||
<button type="button" id="password-toggle" class="flex items-center text-xs text-indigo-600 hover:text-indigo-500 transition duration-200">
|
||||
<i class="fas fa-info-circle mr-1"></i>
|
||||
View password requirements
|
||||
</button>
|
||||
|
||||
<div id="password-requirements" class="hidden mt-2 p-3 bg-blue-50 border border-blue-200 rounded-lg">
|
||||
<p class="text-xs font-medium text-blue-700 mb-2">Password Requirements:</p>
|
||||
<ul class="text-xs text-blue-600 space-y-1">
|
||||
<li class="flex items-start">
|
||||
<span class="text-blue-500 mr-1">•</span>
|
||||
<span>Cannot be too similar to your personal information</span>
|
||||
</li>
|
||||
<li class="flex items-start">
|
||||
<span class="text-blue-500 mr-1">•</span>
|
||||
<span>Must be at least 8 characters long</span>
|
||||
</li>
|
||||
<li class="flex items-start">
|
||||
<span class="text-blue-500 mr-1">•</span>
|
||||
<span>Cannot be a commonly used password</span>
|
||||
</li>
|
||||
<li class="flex items-start">
|
||||
<span class="text-blue-500 mr-1">•</span>
|
||||
<span>Cannot be entirely numeric</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Confirm Password -->
|
||||
<div>
|
||||
<label for="{{ form.confirm_password.id_for_label }}" class="block text-sm font-medium text-gray-700 mb-2">
|
||||
Confirm Password <span class="text-red-500">*</span>
|
||||
</label>
|
||||
<div class="relative">
|
||||
<span class="absolute inset-y-0 left-0 pl-3 flex items-center text-gray-400">
|
||||
<i class="fa-solid fa-lock"></i>
|
||||
</span>
|
||||
{{ form.confirm_password|add_class:"appearance-none block w-full pl-10 px-3 py-3 border border-gray-300 rounded-lg shadow-sm placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition duration-200" }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Terms and Conditions -->
|
||||
<div class="bg-indigo-50 p-4 rounded-lg border border-indigo-100">
|
||||
<div class="flex items-start">
|
||||
{{ form.terms_accepted|add_class:"h-5 w-5 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded mt-0.5" }}
|
||||
<label for="{{ form.terms_accepted.id_for_label }}" class="ml-3 block text-sm text-gray-700">
|
||||
<span class="font-medium text-gray-900">I accept the terms and conditions</span> <span class="text-red-500">*</span>
|
||||
<p class="text-xs text-gray-500 mt-1">By creating an account, you agree to our Terms of Service and Privacy Policy.</p>
|
||||
</label>
|
||||
</div>
|
||||
{% if form.terms_accepted.errors %}
|
||||
<div class="text-red-600 text-sm mt-2 ml-8">
|
||||
{{ form.terms_accepted.errors }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Submit Button -->
|
||||
<div>
|
||||
<button type="submit"
|
||||
class="w-full flex justify-center py-3 px-4 border border-transparent text-sm font-medium rounded-lg text-white bg-gradient-to-r from-indigo-600 to-purple-600 hover:from-indigo-700 hover:to-purple-700 transition duration-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 shadow-lg transform hover:scale-[1.02] active:scale-[0.98]">
|
||||
<i class="fa-solid fa-rocket mr-2"></i>
|
||||
Launch Your Startup Journey
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Login Redirect -->
|
||||
<div class="text-center pt-4">
|
||||
<p class="text-sm text-gray-600">
|
||||
Already have an account?
|
||||
<a href="{% url 'login' %}" class="font-medium text-indigo-600 hover:text-indigo-500 transition duration-200 ml-1">
|
||||
Sign in here
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Email AJAX Validation -->
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
// Email validation
|
||||
$('#id_email').on('blur', function () {
|
||||
const username = $(this).val();
|
||||
if (username) {
|
||||
$.ajax({
|
||||
url: '{% url "check_username" %}',
|
||||
data: { 'username': username },
|
||||
dataType: 'json',
|
||||
success: function (data) {
|
||||
$('#email-status').removeClass('hidden');
|
||||
if (data.is_taken) {
|
||||
$('#email-icon').html('<i class="fa-solid fa-times-circle"></i>').removeClass('text-green-600').addClass('text-red-600');
|
||||
$('#email-text').text('This email is already registered.').removeClass('text-green-600').addClass('text-red-600');
|
||||
$('#id_email').addClass('border-red-500 focus:border-red-500 focus:ring-red-500');
|
||||
} else {
|
||||
$('#email-icon').html('<i class="fa-solid fa-check-circle"></i>').removeClass('text-red-600').addClass('text-green-600');
|
||||
$('#email-text').text('Email is available.').removeClass('text-red-600').addClass('text-green-600');
|
||||
$('#id_email').removeClass('border-red-500 focus:border-red-500 focus:ring-red-500');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
$('#email-status').addClass('hidden');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$('#email-status').addClass('hidden');
|
||||
}
|
||||
});
|
||||
|
||||
// Password requirements toggle
|
||||
$('#password-toggle').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
$('#password-requirements').toggleClass('hidden');
|
||||
if ($('#password-requirements').hasClass('hidden')) {
|
||||
$(this).html('<i class="fas fa-info-circle mr-1"></i>View password requirements');
|
||||
} else {
|
||||
$(this).html('<i class="fas fa-times mr-1"></i>Hide password requirements');
|
||||
}
|
||||
});
|
||||
|
||||
// Hide password requirements when clicking outside
|
||||
$(document).on('click', function(e) {
|
||||
if (!$(e.target).closest('#password-toggle, #password-requirements').length) {
|
||||
$('#password-requirements').addClass('hidden');
|
||||
$('#password-toggle').html('<i class="fas fa-info-circle mr-1"></i>View password requirements');
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
15
at_django_boilerplate/accounts/templates/registration/verify_email.html
Executable file
15
at_django_boilerplate/accounts/templates/registration/verify_email.html
Executable file
@@ -0,0 +1,15 @@
|
||||
{% extends 'public_base.html' %}
|
||||
|
||||
{% block title %}
|
||||
Verify
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h1>You need to verify your email</h1>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<input class="btn btn-primary max-btn" type="submit" value="Verify">
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,14 @@
|
||||
|
||||
{% extends 'public_base.html' %}
|
||||
|
||||
{% block title %}
|
||||
Verify
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
|
||||
<div class="alert alert-success">
|
||||
You have successfully verified your e-mail
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,18 @@
|
||||
{% extends 'public_base.html' %}
|
||||
|
||||
{% block title %}
|
||||
Verify
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
{% if messages %}
|
||||
{% for message in messages %}
|
||||
<div class="alert">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
12
at_django_boilerplate/accounts/templates/registration/verify_email_done.html
Executable file
12
at_django_boilerplate/accounts/templates/registration/verify_email_done.html
Executable file
@@ -0,0 +1,12 @@
|
||||
{% extends 'public_base.html' %}
|
||||
|
||||
{% block title %}
|
||||
Verify
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h5>An email has been sent with instructions to verify your email</h5>
|
||||
<h5>If you have not received the email. Please check the spam folder</h5>
|
||||
{% endblock %}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<div style="background-color: #c2c2c2; padding: 15px;">
|
||||
<h2 style="margin: 0; padding: 10px; border-top-left-radius: 10px; border-top-right-radius: 10px; background-color: #fdc038; color: #ffffff;">Verify Email</h2>
|
||||
<div style="margin: 0; padding: 15px; background-color: #ffffff;">
|
||||
<p>Hi {{ user.name }},</p>
|
||||
<p>You created an account on we-kwick, you need to verify your email. Please click on the button below to verify your email.</p>
|
||||
|
||||
<a href="{{ request.scheme }}://{{ domain }}{% url 'verify-email-confirm' uidb64=uid token=token %}"
|
||||
style="border: 0; color: #ffffff; background-color: #fdc038; padding: 15px; font-weight: bold; text-decoration: none; border-radius: 5px; display: inline-block; margin-top: 20px;">
|
||||
Verify Email
|
||||
</a>
|
||||
|
||||
<p style="margin-top: 40px;">Or you can copy the link below to your browser</p>
|
||||
<p>{{ request.scheme }}://{{ domain }}{% url 'verify-email-confirm' uidb64=uid token=token %}</p>
|
||||
<p>The We-Kwick Team</p>
|
||||
</div>
|
||||
<div style="text-align: center; margin-top: 20px 0;">
|
||||
<p>© {% now 'Y' %} <a href="">Blog</a></p>
|
||||
<p>Follow us on <a href="">Twitter</a></p>
|
||||
</div>
|
||||
</div>
|
||||
49
at_django_boilerplate/accounts/templates/registration/verify_otp.html
Executable file
49
at_django_boilerplate/accounts/templates/registration/verify_otp.html
Executable file
@@ -0,0 +1,49 @@
|
||||
{% extends 'public_base.html' %}
|
||||
{% load static %}
|
||||
{% block title %}Verify OTP{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="flex items-center justify-center min-h-screen bg-gray-100 px-4">
|
||||
<div class="w-full max-w-md bg-white rounded-xl shadow-md p-6">
|
||||
<h4 class="text-2xl font-semibold text-center text-gray-800 mb-6">Verify OTP</h4>
|
||||
|
||||
{% if messages %}
|
||||
{% for message in messages %}
|
||||
<div class="mb-4 px-4 py-3 rounded text-sm
|
||||
{% if message.tags == 'error' %}
|
||||
bg-red-100 text-red-700
|
||||
{% elif message.tags == 'success' %}
|
||||
bg-green-100 text-green-700
|
||||
{% else %}
|
||||
bg-blue-100 text-blue-700
|
||||
{% endif %}
|
||||
">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<form method="post" novalidate>
|
||||
{% csrf_token %}
|
||||
<div class="mb-4">
|
||||
<label for="otpInput" class="block text-sm font-medium text-gray-700 mb-1">Enter OTP sent to your email</label>
|
||||
<input type="text" name="otp" id="otpInput" maxlength="6" pattern="\d{6}"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
placeholder="Enter 6-digit OTP" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<button type="submit"
|
||||
class="w-full bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded-md text-lg font-medium transition">
|
||||
<i class="fas fa-check-circle mr-2"></i>Verify OTP
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="text-center text-sm text-gray-600">
|
||||
Didn’t receive the OTP?
|
||||
<a href="{% url 'request_otp' %}" class="text-blue-600 hover:underline">Resend</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
62
at_django_boilerplate/accounts/templates/reset/password_reset.html
Executable file
62
at_django_boilerplate/accounts/templates/reset/password_reset.html
Executable file
@@ -0,0 +1,62 @@
|
||||
{% extends "public_base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="flex items-center justify-center min-h-screen bg-gray-100 px-4">
|
||||
<div class="w-full max-w-md">
|
||||
<div class="bg-white shadow-xl rounded-2xl overflow-hidden">
|
||||
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 text-center">
|
||||
<h3 class="text-xl font-semibold text-gray-800">Forgot Password?</h3>
|
||||
</div>
|
||||
|
||||
<!-- Error Messages -->
|
||||
{% if form.errors %}
|
||||
<div class="m-4 p-3 rounded-md bg-red-50 border border-red-200 text-red-700">
|
||||
<ul class="list-disc list-inside text-sm">
|
||||
{% for key, value in form.errors.items %}
|
||||
<li>{{ value }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Body -->
|
||||
<div class="px-6 py-4">
|
||||
<form method="POST" class="space-y-4">
|
||||
{% csrf_token %}
|
||||
|
||||
<!-- Email Field -->
|
||||
<div>
|
||||
<label for="id_email" class="block text-sm font-medium text-gray-700">Email</label>
|
||||
<input type="email"
|
||||
name="email"
|
||||
id="id_email"
|
||||
class="mt-1 block w-full rounded-lg border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
|
||||
placeholder="Enter email"
|
||||
autocomplete="email"
|
||||
maxlength="254"
|
||||
required>
|
||||
</div>
|
||||
|
||||
<!-- Submit Button -->
|
||||
<div>
|
||||
<button type="submit"
|
||||
class="w-full py-2 px-4 bg-gray-900 text-white font-medium rounded-lg shadow hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-900">
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="px-6 py-4 border-t border-gray-200 text-center">
|
||||
<a href="{% url 'login' %}" class="text-sm text-indigo-600 hover:text-indigo-800">
|
||||
Back To Login
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
@@ -0,0 +1,9 @@
|
||||
<!-- templates/registration/password_reset_complete.html -->
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}Password reset complete{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Password reset complete</h1>
|
||||
<p>Your new password has been set. You can log in now on the <a href="{% url 'login' %}">log in page</a>.</p>
|
||||
{% endblock %}
|
||||
42
at_django_boilerplate/accounts/templates/reset/password_reset_confirm.html
Executable file
42
at_django_boilerplate/accounts/templates/reset/password_reset_confirm.html
Executable file
@@ -0,0 +1,42 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Password Reset{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
|
||||
<style>
|
||||
.centered-form {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: rgba(255, 255, 255, 1.0); /* Adjust the opacity as needed */
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
|
||||
}
|
||||
</style>
|
||||
|
||||
{% if validlink %}
|
||||
<div class="container-fluid h-100">
|
||||
<div class="row h-100">
|
||||
<div class="col-sm-8">
|
||||
<div class="centered-form">
|
||||
<h3>Set a New Password</h3>
|
||||
<form method="POST">
|
||||
{% csrf_token %}
|
||||
{{form|crispy}}
|
||||
|
||||
<button type="submit" class="btn btn-primary">Change My Password</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
<p>The password reset link was invalid, possibly because it has already been used. Please request a new password reset.</p>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
26
at_django_boilerplate/accounts/templates/reset/password_reset_done.html
Executable file
26
at_django_boilerplate/accounts/templates/reset/password_reset_done.html
Executable file
@@ -0,0 +1,26 @@
|
||||
<!-- templates/registration/password_reset_done.html -->
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Email Sent{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div>
|
||||
{% if messages %}
|
||||
{% for message in messages %}
|
||||
<div class="alert mt-2
|
||||
{% if 'success' in message.tags %} alert-success
|
||||
{% elif 'warning' in message.tags %} alert-warning
|
||||
{% elif 'info' in message.tags %} alert-info
|
||||
{% else %} alert-danger
|
||||
{% endif %}">
|
||||
<span class="text-danger fst-italic">{{ message }}</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<h1>Check your inbox.</h1>
|
||||
<p>We've emailed you instructions for setting your password. You should receive the email shortly!</p>
|
||||
|
||||
{% endblock %}
|
||||
13
at_django_boilerplate/accounts/templates/reset/password_reset_email.html
Executable file
13
at_django_boilerplate/accounts/templates/reset/password_reset_email.html
Executable file
@@ -0,0 +1,13 @@
|
||||
{% autoescape off %}
|
||||
To initiate the password reset process for your we-kwick account,
|
||||
click the link below:
|
||||
|
||||
{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}
|
||||
|
||||
If clicking the link above doesn't work, please copy and paste the URL below in a new browser
|
||||
window instead.
|
||||
|
||||
Sincerely,
|
||||
we-kwick Team
|
||||
{% endautoescape %}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<div style="background-color: #c2c2c2; padding: 15px;">
|
||||
<h2 style="margin: 0; padding: 10px; border-top-left-radius: 10px; border-top-right-radius: 10px; background-color: #fdc038; color: #ffffff;">
|
||||
Password Reset
|
||||
</h2>
|
||||
<div style="margin: 0; padding: 15px; background-color: #ffffff;">
|
||||
<p>Hi {{ user.name }},</p>
|
||||
<p>
|
||||
To initiate the password reset process for your we-kwick account,
|
||||
click the link below:
|
||||
|
||||
<a href="{{ request.scheme }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}"
|
||||
style="border: 0; color: #ffffff; background-color: #fdc038; padding: 15px; font-weight: bold; text-decoration: none; border-radius: 5px; display: inline-block; margin-top: 20px;">
|
||||
Reset Password
|
||||
</a>
|
||||
|
||||
<p style="margin-top: 40px;">Or you can copy the link below to your browser</p>
|
||||
<p>{{ request.scheme }}://{{ domain }}{% url 'verify-email-confirm' uidb64=uid token=token %}</p>
|
||||
<p>The We-Kwick Team</p>
|
||||
</div>
|
||||
<div style="text-align: center; margin-top: 20px 0;">
|
||||
<p>© {% now 'Y' %} <a href="">Blog</a></p>
|
||||
<p>Follow us on <a href="">Twitter</a></p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1 @@
|
||||
We-Kwick Password Reset
|
||||
25
at_django_boilerplate/accounts/templates/user_list.html
Executable file
25
at_django_boilerplate/accounts/templates/user_list.html
Executable file
@@ -0,0 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>User List</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>User List</h2>
|
||||
<ul>
|
||||
{% for user in users %}
|
||||
<li>
|
||||
Name: {{ user.name }}, Birthday: {{ user.birthday }},
|
||||
Address: {{ user.address }}, Email: {{ user.email_address }},
|
||||
Contact: {{ user.contact_number }}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="{% url 'index' %}">Index</a></li>
|
||||
<li><a href="{% url 'home' %}">Home</a></li>
|
||||
<li><a href="{% url 'signup' %}">Create user</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user