feat: admin panel enrichment
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
{% load socialaccount %}
|
||||
{% if social_auth_enabled %}
|
||||
<div class="social-auth">
|
||||
<p class="social-auth-label">Continue with</p>
|
||||
<div class="social-auth-buttons">
|
||||
{% for provider in social_providers %}
|
||||
<a href="{% provider_login_url provider.id %}" class="social-auth-btn social-auth-btn--{{ provider.id }}">
|
||||
{% if provider.id == "google" %}
|
||||
<span class="social-auth-icon" aria-hidden="true">G</span>
|
||||
<span>Google</span>
|
||||
{% else %}
|
||||
<span class="social-auth-icon" aria-hidden="true">GH</span>
|
||||
<span>GitHub</span>
|
||||
{% endif %}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="portal-divider" role="separator"><span>or use email</span></div>
|
||||
{% endif %}
|
||||
@@ -0,0 +1,5 @@
|
||||
Password reset for {{ user.get_username }}
|
||||
|
||||
{{ protocol }}://{{ domain }}{% url 'accounts:password_reset_confirm' uidb64=uid token=token %}
|
||||
|
||||
If you did not request this, ignore this email.
|
||||
@@ -0,0 +1 @@
|
||||
Tecvico password reset
|
||||
@@ -0,0 +1,55 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Login{% endblock %}
|
||||
{% block meta_description %}Log in to your Tecvico client portal.{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="page-hero" aria-labelledby="login-heading">
|
||||
<div class="page-hero-blobs" aria-hidden="true">
|
||||
<div class="blob blob--1"></div>
|
||||
<div class="blob blob--2"></div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="page-hero-content">
|
||||
<div class="section-badge">Client Portal</div>
|
||||
<h1 class="page-hero-title" id="login-heading">Log In</h1>
|
||||
<p class="page-hero-subtitle">Access your project briefs and track progress with the Tecvico team.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container container--narrow">
|
||||
<div class="glass-card portal-card fade-in">
|
||||
{% include "accounts/_social_auth.html" %}
|
||||
|
||||
<form method="post" novalidate>
|
||||
{% csrf_token %}
|
||||
{% if form.non_field_errors %}
|
||||
<div class="form-errors">{{ form.non_field_errors }}</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="form-group">
|
||||
<label for="id_username">Username <span class="required-star">*</span></label>
|
||||
{{ form.username }}
|
||||
{{ form.username.errors }}
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="id_password">Password <span class="required-star">*</span></label>
|
||||
{{ form.password }}
|
||||
{{ form.password.errors }}
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-primary btn-submit">Log in</button>
|
||||
</form>
|
||||
|
||||
<p class="portal-footer-text">
|
||||
<a href="{% url 'accounts:password_reset' %}">Forgot password?</a>
|
||||
·
|
||||
<a href="{% url 'accounts:signup' %}">Create account</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,24 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Reset Password{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="section">
|
||||
<div class="container container--narrow">
|
||||
<div class="glass-card portal-card fade-in">
|
||||
<h1 class="contact-form-title">Reset password</h1>
|
||||
<p class="portal-intro">Enter your email and we'll send reset instructions.</p>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="form-group">
|
||||
<label for="id_email">Email</label>
|
||||
{{ form.email }}
|
||||
{{ form.email.errors }}
|
||||
</div>
|
||||
<button type="submit" class="btn-primary btn-submit">Send reset link</button>
|
||||
</form>
|
||||
<p class="portal-footer-text"><a href="{% url 'accounts:login' %}">Back to login</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,15 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Password Updated{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="section">
|
||||
<div class="container container--narrow">
|
||||
<div class="glass-card portal-card fade-in">
|
||||
<h1 class="contact-form-title">Password updated</h1>
|
||||
<p class="portal-intro">You can now log in with your new password.</p>
|
||||
<p class="portal-footer-text"><a href="{% url 'accounts:login' %}">Log in</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,28 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Set New Password{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="section">
|
||||
<div class="container container--narrow">
|
||||
<div class="glass-card portal-card fade-in">
|
||||
<h1 class="contact-form-title">Set a new password</h1>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form.non_field_errors }}
|
||||
<div class="form-group">
|
||||
<label for="id_new_password1">New password</label>
|
||||
{{ form.new_password1 }}
|
||||
{{ form.new_password1.errors }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="id_new_password2">Confirm password</label>
|
||||
{{ form.new_password2 }}
|
||||
{{ form.new_password2.errors }}
|
||||
</div>
|
||||
<button type="submit" class="btn-primary btn-submit">Update password</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,15 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Reset Email Sent{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="section">
|
||||
<div class="container container--narrow">
|
||||
<div class="glass-card portal-card fade-in">
|
||||
<h1 class="contact-form-title">Check your email</h1>
|
||||
<p class="portal-intro">If an account exists for that address, you'll receive password reset instructions shortly.</p>
|
||||
<p class="portal-footer-text"><a href="{% url 'accounts:login' %}">Back to login</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,64 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Profile{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="page-hero" aria-labelledby="profile-heading">
|
||||
<div class="container">
|
||||
<div class="page-hero-content">
|
||||
<h1 class="page-hero-title" id="profile-heading">Your Profile</h1>
|
||||
<p class="page-hero-subtitle">Keep your contact details up to date for project communication.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container container--narrow">
|
||||
<div class="glass-card portal-card fade-in">
|
||||
<form method="post" novalidate>
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="form-grid form-grid--2">
|
||||
<div class="form-group">
|
||||
<label for="id_first_name">First name <span class="required-star">*</span></label>
|
||||
{{ form.first_name }}
|
||||
{{ form.first_name.errors }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="id_last_name">Last name <span class="required-star">*</span></label>
|
||||
{{ form.last_name }}
|
||||
{{ form.last_name.errors }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="id_email">Email <span class="required-star">*</span></label>
|
||||
{{ form.email }}
|
||||
{{ form.email.errors }}
|
||||
</div>
|
||||
|
||||
<div class="form-grid form-grid--2">
|
||||
<div class="form-group">
|
||||
<label for="id_company">Company</label>
|
||||
{{ form.company }}
|
||||
{{ form.company.errors }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="id_phone">Phone</label>
|
||||
{{ form.phone }}
|
||||
{{ form.phone.errors }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="id_timezone">Timezone <span class="required-star">*</span></label>
|
||||
{{ form.timezone }}
|
||||
{{ form.timezone.errors }}
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-primary btn-submit">Save profile</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,96 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Sign Up{% endblock %}
|
||||
{% block meta_description %}Create a Tecvico client account and submit your project brief.{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="page-hero" aria-labelledby="signup-heading">
|
||||
<div class="page-hero-blobs" aria-hidden="true">
|
||||
<div class="blob blob--1"></div>
|
||||
<div class="blob blob--2"></div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="page-hero-content">
|
||||
<div class="section-badge">Client Portal</div>
|
||||
<h1 class="page-hero-title" id="signup-heading">Create Account</h1>
|
||||
<p class="page-hero-subtitle">Tell us about your project and our team will take it from there.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container container--narrow">
|
||||
<div class="glass-card portal-card fade-in">
|
||||
{% include "accounts/_social_auth.html" %}
|
||||
|
||||
<form method="post" novalidate>
|
||||
{% csrf_token %}
|
||||
{% if form.non_field_errors %}
|
||||
<div class="form-errors">{{ form.non_field_errors }}</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="form-grid form-grid--2">
|
||||
<div class="form-group">
|
||||
<label for="id_first_name">First name <span class="required-star">*</span></label>
|
||||
{{ form.first_name }}
|
||||
{{ form.first_name.errors }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="id_last_name">Last name <span class="required-star">*</span></label>
|
||||
{{ form.last_name }}
|
||||
{{ form.last_name.errors }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="id_email">Email <span class="required-star">*</span></label>
|
||||
{{ form.email }}
|
||||
{{ form.email.errors }}
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="id_username">Username <span class="required-star">*</span></label>
|
||||
{{ form.username }}
|
||||
{{ form.username.errors }}
|
||||
</div>
|
||||
|
||||
<div class="form-grid form-grid--2">
|
||||
<div class="form-group">
|
||||
<label for="id_company">Company</label>
|
||||
{{ form.company }}
|
||||
{{ form.company.errors }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="id_phone">Phone</label>
|
||||
{{ form.phone }}
|
||||
{{ form.phone.errors }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="id_timezone">Timezone <span class="required-star">*</span></label>
|
||||
{{ form.timezone }}
|
||||
{{ form.timezone.errors }}
|
||||
</div>
|
||||
|
||||
<div class="form-grid form-grid--2">
|
||||
<div class="form-group">
|
||||
<label for="id_password1">Password <span class="required-star">*</span></label>
|
||||
{{ form.password1 }}
|
||||
{{ form.password1.errors }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="id_password2">Confirm password <span class="required-star">*</span></label>
|
||||
{{ form.password2 }}
|
||||
{{ form.password2.errors }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-primary btn-submit">Create account</button>
|
||||
</form>
|
||||
|
||||
<p class="portal-footer-text">Already have an account? <a href="{% url 'accounts:login' %}">Log in</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
@@ -17,6 +17,8 @@
|
||||
|
||||
{% include "partials/_navbar.html" %}
|
||||
|
||||
{% include "partials/_messages.html" %}
|
||||
|
||||
<main id="main-content">
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
|
||||
@@ -37,6 +37,6 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{% include "partials/_page_sections.html" with sections=homepage_sections %}
|
||||
{% include "partials/_page_sections.html" with sections=homepage_sections website_project_briefs=website_project_briefs %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{% if messages %}
|
||||
<div class="flash-messages" aria-live="polite">
|
||||
{% for message in messages %}
|
||||
<div class="flash-message flash-message--{{ message.tags|default:'info' }}">{{ message }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
@@ -90,6 +90,36 @@
|
||||
</a>
|
||||
</li>
|
||||
|
||||
{% if user.is_authenticated %}
|
||||
<li class="nav-item">
|
||||
<a href="{% url 'projects:dashboard' %}" class="nav-link {% if request.resolver_match.namespace == 'projects' %}active{% endif %}">
|
||||
My Projects
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{% url 'accounts:profile' %}" class="nav-link {% if request.resolver_match.url_name == 'profile' %}active{% endif %}">
|
||||
Profile
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<form method="post" action="{% url 'accounts:logout' %}" class="nav-logout-form">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="nav-link nav-link-button">Logout</button>
|
||||
</form>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="nav-item">
|
||||
<a href="{% url 'accounts:login' %}" class="nav-link {% if request.resolver_match.url_name == 'login' %}active{% endif %}">
|
||||
Login
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{% url 'accounts:signup' %}" class="nav-link nav-link--cta {% if request.resolver_match.url_name == 'signup' %}active{% endif %}">
|
||||
Start a Project
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -245,6 +245,58 @@
|
||||
</article>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if website_project_briefs %}
|
||||
{% for brief in website_project_briefs %}
|
||||
{% if brief.public_url %}
|
||||
<a href="{{ brief.public_url }}" class="project-card glass-card fade-in" data-project-status="{{ brief.public_project_status|default:'all' }}"{% if brief.public_url|slice:":4" == "http" %} target="_blank" rel="noopener noreferrer"{% endif %}>
|
||||
{% else %}
|
||||
<article class="project-card glass-card fade-in" data-project-status="{{ brief.public_project_status|default:'all' }}">
|
||||
{% endif %}
|
||||
{% if brief.public_image %}
|
||||
<div class="project-card-image">
|
||||
<img src="{{ brief.public_image.url }}" alt="{{ brief.public_display_title|default:brief.title }}" loading="lazy" />
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="project-card-body">
|
||||
{% if brief.public_display_title %}<h3 class="project-card-title">{{ brief.public_display_title }}</h3>{% endif %}
|
||||
{% if brief.public_display_summary %}
|
||||
<p class="project-card-desc" data-readmore="120">{{ brief.public_display_summary }}</p>
|
||||
{% endif %}
|
||||
{% if brief.public_budget_display or brief.public_deadline_display %}
|
||||
<p class="project-card-meta">
|
||||
{% if brief.public_budget_display %}<span>Budget: {{ brief.public_budget_display }}</span>{% endif %}
|
||||
{% if brief.public_deadline_display %}<span>Deadline: {{ brief.public_deadline_display }}</span>{% endif %}
|
||||
</p>
|
||||
{% endif %}
|
||||
{% if brief.public_tag_list %}
|
||||
<ul class="project-card-tags" role="list">
|
||||
{% for tag in brief.public_tag_list %}
|
||||
<li>{{ tag }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% if brief.public_reference_link_list %}
|
||||
<ul class="project-card-links" role="list">
|
||||
{% for link in brief.public_reference_link_list %}
|
||||
<li><a href="{{ link }}" target="_blank" rel="noopener noreferrer">{{ link }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% if brief.public_attachment_filename_list %}
|
||||
<ul class="project-card-attachments" role="list">
|
||||
{% for filename in brief.public_attachment_filename_list %}
|
||||
<li>{{ filename }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if brief.public_url %}
|
||||
</a>
|
||||
{% else %}
|
||||
</article>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ brief.title }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="page-hero" aria-labelledby="brief-heading">
|
||||
<div class="container">
|
||||
<div class="page-hero-content page-hero-content--row">
|
||||
<div>
|
||||
{% if brief.category %}<div class="section-badge">{{ brief.get_category_display }}</div>{% endif %}
|
||||
<h1 class="page-hero-title" id="brief-heading">{{ brief.title }}</h1>
|
||||
<p class="page-hero-subtitle">
|
||||
<span class="status-pill status-pill--{{ brief.status }}">{{ brief.get_status_display }}</span>
|
||||
· Submitted {{ brief.submitted_at|date:"M j, Y" }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="portal-actions">
|
||||
<a href="{% url 'projects:dashboard' %}" class="btn-ghost">Back to list</a>
|
||||
{% if brief.is_editable_by_client %}
|
||||
<a href="{% url 'projects:edit' pk=brief.pk %}" class="btn-primary">Edit brief</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<div class="portal-detail-grid">
|
||||
<div class="glass-card portal-card fade-in">
|
||||
<h2 class="portal-section-title">Overview</h2>
|
||||
<dl class="portal-meta-list">
|
||||
{% if brief.budget_range %}<div><dt>Budget</dt><dd>{{ brief.get_budget_range_display }}</dd></div>{% endif %}
|
||||
<div><dt>Deadline</dt><dd>{% if brief.desired_deadline %}{{ brief.desired_deadline|date:"M j, Y" }}{% else %}Not specified{% endif %}</dd></div>
|
||||
<div><dt>Last updated</dt><dd>{{ brief.updated_at|date:"M j, Y, P" }}</dd></div>
|
||||
</dl>
|
||||
|
||||
{% if brief.description %}
|
||||
<h3 class="portal-subtitle">Description & goals</h3>
|
||||
<p class="portal-body-text">{{ brief.description|linebreaksbr }}</p>
|
||||
{% endif %}
|
||||
|
||||
{% if brief.reference_links %}
|
||||
<h3 class="portal-subtitle">Reference links</h3>
|
||||
<p class="portal-body-text">{{ brief.reference_links|linebreaksbr }}</p>
|
||||
{% endif %}
|
||||
|
||||
{% if brief.attachments.exists %}
|
||||
<h3 class="portal-subtitle">Attachments</h3>
|
||||
<ul class="portal-attachment-list">
|
||||
{% for attachment in brief.attachments.all %}
|
||||
<li>{{ attachment.original_filename }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if brief.shows_quote_to_client %}
|
||||
<div class="glass-card portal-card fade-in">
|
||||
<h2 class="portal-section-title">Quote from Tecvico</h2>
|
||||
<p class="portal-body-text">{{ brief.quote_text|linebreaksbr }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if brief.has_website_notes %}
|
||||
<div class="glass-card portal-card fade-in">
|
||||
<h2 class="portal-section-title">Notes from Tecvico</h2>
|
||||
<p class="portal-body-text">{{ brief.website_notes|linebreaksbr }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,150 @@
|
||||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}{{ form_title }}{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
<link rel="stylesheet" href="{% static 'css/brief-wizard.css' %}" />
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="page-hero" aria-labelledby="brief-form-heading">
|
||||
<div class="container">
|
||||
<div class="page-hero-content">
|
||||
<h1 class="page-hero-title" id="brief-form-heading">{{ form_title }}</h1>
|
||||
<p class="page-hero-subtitle">Complete each step to submit your project brief.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container container--narrow">
|
||||
<div class="glass-card portal-card fade-in">
|
||||
<form method="post" enctype="multipart/form-data" novalidate class="brief-wizard" data-brief-wizard data-initial-step="{{ initial_step }}">
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="wizard-progress" aria-hidden="true">
|
||||
<div class="wizard-progress-track">
|
||||
<div class="wizard-progress-fill" data-wizard-progress-fill></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ol class="wizard-stepper" role="list">
|
||||
{% for step in wizard_steps %}
|
||||
<li class="wizard-stepper-item{% if forloop.first %} is-active{% endif %}" data-wizard-step-indicator="{{ forloop.counter }}">
|
||||
<span class="wizard-stepper-number">{{ forloop.counter }}</span>
|
||||
<span class="wizard-stepper-label">{{ step.title }}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
|
||||
{% for step in wizard_steps %}
|
||||
<section
|
||||
class="wizard-panel{% if forloop.first %} is-active{% endif %}"
|
||||
data-wizard-panel="{{ forloop.counter }}"
|
||||
data-step-fields="{{ step.fields|join:',' }}"
|
||||
aria-labelledby="wizard-step-title-{{ forloop.counter }}"
|
||||
{% if not forloop.first %}hidden{% endif %}
|
||||
>
|
||||
<header class="wizard-panel-header">
|
||||
<p class="wizard-panel-eyebrow">Step {{ forloop.counter }} of {{ wizard_steps|length }}</p>
|
||||
<h2 class="wizard-panel-title" id="wizard-step-title-{{ forloop.counter }}">{{ step.title }}</h2>
|
||||
<p class="wizard-panel-subtitle">{{ step.subtitle }}</p>
|
||||
</header>
|
||||
|
||||
{% if step.id == "basics" %}
|
||||
<div class="form-group">
|
||||
<label for="id_title">Project title <span class="required-star">*</span></label>
|
||||
{{ form.title }}
|
||||
{{ form.title.errors }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="id_category">Category</label>
|
||||
{{ form.category }}
|
||||
{{ form.category.errors }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if step.id == "scope" %}
|
||||
<div class="form-group">
|
||||
<label for="id_description">Description & goals</label>
|
||||
{{ form.description }}
|
||||
<p class="form-hint">Describe the project scope, context, and the outcomes you expect.</p>
|
||||
{{ form.description.errors }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if step.id == "timeline" %}
|
||||
<div class="form-grid form-grid--2">
|
||||
<div class="form-group">
|
||||
<label for="id_budget_range">Budget range</label>
|
||||
{{ form.budget_range }}
|
||||
{{ form.budget_range.errors }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="id_desired_deadline">Desired deadline</label>
|
||||
{{ form.desired_deadline }}
|
||||
{{ form.desired_deadline.errors }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if step.id == "references" %}
|
||||
<div class="form-group">
|
||||
<label for="id_reference_links">Reference links</label>
|
||||
{{ form.reference_links }}
|
||||
<p class="form-hint">One URL per line — docs, mockups, repos, etc.</p>
|
||||
{{ form.reference_links.errors }}
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="id_attachments">Attachments</label>
|
||||
<input type="file" name="attachments" id="id_attachments" multiple
|
||||
accept=".png,.jpg,.jpeg,.gif,.webp,.bmp,.zip,.log,.txt"
|
||||
class="file-input"
|
||||
{% if brief and brief.attachments_locked %}disabled{% endif %} />
|
||||
<p class="form-hint">Images, ZIP, or log/text files — up to 3 files, 10 MB each.</p>
|
||||
{{ form.attachments.errors }}
|
||||
</div>
|
||||
|
||||
{% if brief and brief.attachments.exists %}
|
||||
<div class="portal-attachment-list">
|
||||
<p class="form-hint">Existing attachments:</p>
|
||||
<ul>
|
||||
{% for attachment in brief.attachments.all %}
|
||||
<li>{{ attachment.original_filename }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="wizard-review" data-wizard-review hidden>
|
||||
<h3 class="wizard-review-title">Review your brief</h3>
|
||||
<dl class="wizard-review-list">
|
||||
<div><dt>Title</dt><dd data-review-field="title">—</dd></div>
|
||||
<div><dt>Category</dt><dd data-review-field="category">—</dd></div>
|
||||
<div><dt>Description & goals</dt><dd data-review-field="description">—</dd></div>
|
||||
<div><dt>Budget</dt><dd data-review-field="budget_range">—</dd></div>
|
||||
<div><dt>Deadline</dt><dd data-review-field="desired_deadline">—</dd></div>
|
||||
<div><dt>Reference links</dt><dd data-review-field="reference_links">—</dd></div>
|
||||
<div><dt>New attachments</dt><dd data-review-field="attachments">None</dd></div>
|
||||
</dl>
|
||||
</div>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% endfor %}
|
||||
|
||||
<div class="wizard-actions">
|
||||
<button type="button" class="btn-ghost" data-wizard-prev hidden>Back</button>
|
||||
<button type="button" class="btn-primary" data-wizard-next>Continue</button>
|
||||
<button type="submit" class="btn-primary btn-submit" data-wizard-submit hidden>{{ submit_label }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script src="{% static 'js/brief-wizard.js' %}"></script>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,70 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}My Projects{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="page-hero" aria-labelledby="dashboard-heading">
|
||||
<div class="container">
|
||||
<div class="page-hero-content page-hero-content--row">
|
||||
<div>
|
||||
<div class="section-badge">Client Portal</div>
|
||||
<h1 class="page-hero-title" id="dashboard-heading">My Projects</h1>
|
||||
<p class="page-hero-subtitle">Track briefs you've submitted to the Tecvico team.</p>
|
||||
</div>
|
||||
<a href="{% url 'projects:create' %}" class="btn-primary">New project brief</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
{% if briefs %}
|
||||
<div class="portal-table-wrap glass-card fade-in">
|
||||
<table class="portal-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Category</th>
|
||||
<th>Status</th>
|
||||
<th>Budget</th>
|
||||
<th>Submitted</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for brief in briefs %}
|
||||
<tr>
|
||||
<td>{{ brief.title }}</td>
|
||||
<td>{% if brief.category %}{{ brief.get_category_display }}{% else %}—{% endif %}</td>
|
||||
<td><span class="status-pill status-pill--{{ brief.status }}">{{ brief.get_status_display }}</span></td>
|
||||
<td>{% if brief.budget_range %}{{ brief.get_budget_range_display }}{% else %}—{% endif %}</td>
|
||||
<td>{{ brief.submitted_at|date:"M j, Y" }}</td>
|
||||
<td><a href="{% url 'projects:detail' pk=brief.pk %}" class="portal-link">View</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% if page_obj.has_other_pages %}
|
||||
<div class="portal-pagination">
|
||||
{% if page_obj.has_previous %}
|
||||
<a href="?page={{ page_obj.previous_page_number }}" class="portal-link">Previous</a>
|
||||
{% endif %}
|
||||
<span>Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}</span>
|
||||
{% if page_obj.has_next %}
|
||||
<a href="?page={{ page_obj.next_page_number }}" class="portal-link">Next</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% else %}
|
||||
<div class="glass-card portal-card portal-empty fade-in">
|
||||
<h2>No project briefs yet</h2>
|
||||
<p class="portal-intro">Describe what you need and our team will review your request.</p>
|
||||
<a href="{% url 'projects:create' %}" class="btn-primary">Submit your first brief</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,13 @@
|
||||
New project brief submitted
|
||||
|
||||
Title: {{ brief.title }}
|
||||
Client: {{ brief.client.get_full_name|default:brief.client.username }} ({{ brief.client.email }})
|
||||
Category: {{ brief.get_category_display }}
|
||||
Budget: {{ brief.get_budget_range_display }}
|
||||
Status: {{ brief.get_status_display }}
|
||||
Submitted: {{ brief.submitted_at }}
|
||||
|
||||
Description & goals:
|
||||
{{ brief.description }}
|
||||
|
||||
Review in admin: /admin/projects/projectbrief/{{ brief.pk }}/change/
|
||||
@@ -0,0 +1,13 @@
|
||||
Hello {{ brief.client.first_name|default:brief.client.username }},
|
||||
|
||||
Your project "{{ brief.title }}" status changed from {{ previous_status }} to {{ brief.status }}.
|
||||
|
||||
{% if brief.shows_quote_to_client %}
|
||||
Quote from Tecvico:
|
||||
{{ brief.quote_text }}
|
||||
{% endif %}
|
||||
|
||||
View your project: {{ brief.get_absolute_url }}
|
||||
|
||||
Thank you,
|
||||
Tecvico Team
|
||||
@@ -0,0 +1,32 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}Sign-in failed{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="page-hero" aria-labelledby="auth-error-heading">
|
||||
<div class="container">
|
||||
<div class="page-hero-content">
|
||||
<h1 class="page-hero-title" id="auth-error-heading">Sign-in failed</h1>
|
||||
<p class="page-hero-subtitle">We could not complete third-party authentication.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container container--narrow">
|
||||
<div class="glass-card portal-card fade-in">
|
||||
<div class="form-errors">
|
||||
{% trans "An error occurred while attempting to sign in with your third-party account." %}
|
||||
</div>
|
||||
<p class="portal-intro">
|
||||
{% trans "Please try again, or sign in with your email and password." %}
|
||||
</p>
|
||||
<div class="portal-actions">
|
||||
<a href="{% url 'accounts:login' %}" class="btn-primary">Back to login</a>
|
||||
<a href="{% url 'pages:contact' %}" class="btn-ghost">Contact support</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,80 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}
|
||||
{% if process == "connect" %}Connect {{ provider.name }}{% else %}Continue with {{ provider.name }}{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block meta_description %}Secure sign-in with {{ provider.name }} for your Tecvico client portal.{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="page-hero" aria-labelledby="social-login-heading">
|
||||
<div class="page-hero-blobs" aria-hidden="true">
|
||||
<div class="blob blob--1"></div>
|
||||
<div class="blob blob--2"></div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="page-hero-content">
|
||||
<div class="section-badge">Client Portal</div>
|
||||
<h1 class="page-hero-title" id="social-login-heading">
|
||||
{% if process == "connect" %}
|
||||
Connect {{ provider.name }}
|
||||
{% else %}
|
||||
Sign in with {{ provider.name }}
|
||||
{% endif %}
|
||||
</h1>
|
||||
<p class="page-hero-subtitle">
|
||||
{% if process == "connect" %}
|
||||
Link your {{ provider.name }} account to your Tecvico profile.
|
||||
{% else %}
|
||||
You will be redirected to {{ provider.name }} to verify your identity.
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container container--narrow">
|
||||
<div class="glass-card portal-card fade-in social-oauth-card">
|
||||
<div class="social-oauth-provider social-oauth-provider--{{ provider.id }}">
|
||||
<span class="social-oauth-provider-icon" aria-hidden="true">
|
||||
{% if provider.id == "google" %}G{% else %}GH{% endif %}
|
||||
</span>
|
||||
<div>
|
||||
<h2 class="social-oauth-provider-title">{{ provider.name }}</h2>
|
||||
<p class="social-oauth-provider-copy">
|
||||
{% if process == "connect" %}
|
||||
{% blocktrans with provider_name=provider.name %}You are about to connect a {{ provider_name }} account to Tecvico.{% endblocktrans %}
|
||||
{% else %}
|
||||
{% blocktrans with provider_name=provider.name %}Continue to authorize access with your {{ provider_name }} account.{% endblocktrans %}
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="social-oauth-steps">
|
||||
<li>Redirect to {{ provider.name }}</li>
|
||||
<li>Approve access</li>
|
||||
<li>Return to your project dashboard</li>
|
||||
</ul>
|
||||
|
||||
<form method="post" class="social-oauth-form">
|
||||
{% csrf_token %}
|
||||
{% if redirect_field_value %}
|
||||
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
|
||||
{% endif %}
|
||||
<button type="submit" class="btn-primary btn-submit social-oauth-continue">
|
||||
Continue to {{ provider.name }}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<p class="portal-footer-text">
|
||||
<a href="{% url 'accounts:login' %}">Back to login</a>
|
||||
·
|
||||
<a href="{% url 'pages:home' %}">Home</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,29 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}Login cancelled{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="page-hero" aria-labelledby="cancelled-heading">
|
||||
<div class="container">
|
||||
<div class="page-hero-content">
|
||||
<h1 class="page-hero-title" id="cancelled-heading">Login cancelled</h1>
|
||||
<p class="page-hero-subtitle">No changes were made to your account.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container container--narrow">
|
||||
<div class="glass-card portal-card fade-in">
|
||||
<p class="portal-intro">
|
||||
{% trans "You cancelled third-party sign-in. You can try again or use email login instead." %}
|
||||
</p>
|
||||
<div class="portal-actions">
|
||||
<a href="{% url 'accounts:login' %}" class="btn-primary">Back to login</a>
|
||||
<a href="{% url 'pages:home' %}" class="btn-ghost">Home</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,23 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}Redirecting to {{ provider }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="section">
|
||||
<div class="container container--narrow">
|
||||
<div class="glass-card portal-card fade-in social-oauth-redirect">
|
||||
<div class="social-oauth-spinner" aria-hidden="true"></div>
|
||||
<h1 class="contact-form-title">Redirecting to {{ provider }}</h1>
|
||||
<p class="portal-intro">{% trans "Please wait while we connect you securely." %}</p>
|
||||
<p class="portal-footer-text">
|
||||
<a href="{{ redirect_to }}" class="portal-link">{% trans "Continue manually" %}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script>window.location.replace("{{ redirect_to|escapejs }}");</script>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,57 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}Complete sign up{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="page-hero" aria-labelledby="social-signup-heading">
|
||||
<div class="page-hero-blobs" aria-hidden="true">
|
||||
<div class="blob blob--1"></div>
|
||||
<div class="blob blob--2"></div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="page-hero-content">
|
||||
<div class="section-badge">Client Portal</div>
|
||||
<h1 class="page-hero-title" id="social-signup-heading">Complete your account</h1>
|
||||
<p class="page-hero-subtitle">
|
||||
{% blocktrans with provider_name=account.get_provider.name %}Finish setup after signing in with {{ provider_name }}.{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container container--narrow">
|
||||
<div class="glass-card portal-card fade-in">
|
||||
<form method="post" action="{% url 'socialaccount_signup' %}" novalidate>
|
||||
{% csrf_token %}
|
||||
{{ redirect_field }}
|
||||
|
||||
{% if form.non_field_errors %}
|
||||
<div class="form-errors">{{ form.non_field_errors }}</div>
|
||||
{% endif %}
|
||||
|
||||
{% for field in form %}
|
||||
<div class="form-group{% if field.errors %} has-error{% endif %}">
|
||||
<label for="{{ field.id_for_label }}">{{ field.label }}{% if field.field.required %} <span class="required-star">*</span>{% endif %}</label>
|
||||
{{ field }}
|
||||
{{ field.errors }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<button type="submit" class="btn-primary btn-submit">Create account</button>
|
||||
</form>
|
||||
|
||||
<p class="portal-footer-text"><a href="{% url 'accounts:login' %}">Back to login</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script>
|
||||
document.querySelectorAll(".portal-card input, .portal-card select, .portal-card textarea").forEach(function (el) {
|
||||
el.classList.add("form-control");
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user