V2
This commit is contained in:
parent
69bb9544a4
commit
fc8bd63b34
BIN
core/__pycache__/forms.cpython-311.pyc
Normal file
BIN
core/__pycache__/forms.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
22
core/forms.py
Normal file
22
core/forms.py
Normal file
@ -0,0 +1,22 @@
|
||||
from django import forms
|
||||
from .models import Startup, Message, Profile
|
||||
|
||||
class StartupForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Startup
|
||||
fields = ['name', 'headline', 'description', 'target_amount', 'status']
|
||||
widgets = {
|
||||
'name': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Startup Name'}),
|
||||
'headline': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'A catchy one-liner'}),
|
||||
'description': forms.Textarea(attrs={'class': 'form-control', 'rows': 4, 'placeholder': 'What are you building?'}),
|
||||
'target_amount': forms.NumberInput(attrs={'class': 'form-control', 'placeholder': 'Target funding amount (£)'}),
|
||||
'status': forms.Select(attrs={'class': 'form-select'}),
|
||||
}
|
||||
|
||||
class MessageForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Message
|
||||
fields = ['content']
|
||||
widgets = {
|
||||
'content': forms.Textarea(attrs={'class': 'form-control', 'rows': 3, 'placeholder': 'Write your message...'}),
|
||||
}
|
||||
BIN
core/management/commands/__pycache__/seed_data.cpython-311.pyc
Normal file
BIN
core/management/commands/__pycache__/seed_data.cpython-311.pyc
Normal file
Binary file not shown.
58
core/management/commands/seed_data.py
Normal file
58
core/management/commands/seed_data.py
Normal file
@ -0,0 +1,58 @@
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.contrib.auth.models import User
|
||||
from core.models import Profile, Tag, Startup, Investment
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Seed the database with initial mock data'
|
||||
|
||||
def handle(self, *args, **kwargs):
|
||||
self.stdout.write('Seeding initial data...')
|
||||
|
||||
# Create Tags
|
||||
tags = ['Fintech', 'Healthtech', 'Edtech', 'Sustainability', 'Consumer', 'Social Impact', 'AI', 'SaaS']
|
||||
tag_objs = []
|
||||
for tag in tags:
|
||||
obj, created = Tag.objects.get_or_create(name=tag)
|
||||
tag_objs.append(obj)
|
||||
|
||||
# Create Founders
|
||||
founders = [
|
||||
{'username': 'alex_founder', 'uni': 'Oxford', 'grad': 2025, 'bio': 'Building the future of finance for Gen Z.'},
|
||||
{'username': 'sarah_tech', 'uni': 'Imperial', 'grad': 2024, 'bio': 'Tech enthusiast and full-stack developer.'},
|
||||
{'username': 'michael_green', 'uni': 'UCL', 'grad': 2023, 'bio': 'Sustainability expert looking to change the world.'}
|
||||
]
|
||||
|
||||
founder_profiles = []
|
||||
for f in founders:
|
||||
user, created = User.objects.get_or_create(username=f['username'], email=f'{f["username"]}@uni.ac.uk')
|
||||
if created:
|
||||
user.set_password('password123')
|
||||
user.save()
|
||||
|
||||
profile = user.profile
|
||||
profile.role = 'FOUNDER'
|
||||
profile.university = f['uni']
|
||||
profile.graduation_year = f['grad']
|
||||
profile.bio = f['bio']
|
||||
profile.is_verified = True
|
||||
profile.save()
|
||||
founder_profiles.append(profile)
|
||||
|
||||
# Create Startups
|
||||
startups = [
|
||||
{'name': 'Stashify', 'headline': 'Micro-investing for students', 'amount': 5000, 'founder': founder_profiles[0]},
|
||||
{'name': 'HealthPulse', 'headline': 'AI-driven wellness tracker', 'amount': 10000, 'founder': founder_profiles[1]},
|
||||
{'name': 'EcoCart', 'headline': 'Track your carbon footprint while shopping', 'amount': 7500, 'founder': founder_profiles[2]}
|
||||
]
|
||||
|
||||
for s in startups:
|
||||
Startup.objects.get_or_create(
|
||||
founder=s['founder'],
|
||||
name=s['name'],
|
||||
headline=s['headline'],
|
||||
description="This is a detailed description of the startup " + s['name'] + ". We are building an MVP and looking for early-stage investors from the university community.",
|
||||
target_amount=s['amount'],
|
||||
status='FUNDING'
|
||||
)
|
||||
|
||||
self.stdout.write(self.style.SUCCESS('Successfully seeded data!'))
|
||||
85
core/migrations/0001_initial.py
Normal file
85
core/migrations/0001_initial.py
Normal file
@ -0,0 +1,85 @@
|
||||
# Generated by Django 5.2.7 on 2026-02-28 12:06
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Tag',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=50, unique=True)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Profile',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('role', models.CharField(choices=[('FOUNDER', 'Founder'), ('INVESTOR', 'Investor'), ('PENDING', 'Pending')], default='PENDING', max_length=10)),
|
||||
('university', models.CharField(blank=True, max_length=255)),
|
||||
('graduation_year', models.IntegerField(blank=True, null=True)),
|
||||
('bio', models.CharField(blank=True, max_length=150)),
|
||||
('investment_appetite', models.DecimalField(blank=True, decimal_places=2, max_digits=12, null=True)),
|
||||
('investment_frequency', models.CharField(blank=True, max_length=100)),
|
||||
('matching_answers', models.JSONField(blank=True, null=True)),
|
||||
('is_verified', models.BooleanField(default=False)),
|
||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile', to=settings.AUTH_USER_MODEL)),
|
||||
('interests', models.ManyToManyField(blank=True, to='core.tag')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Notification',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('content', models.TextField()),
|
||||
('timestamp', models.DateTimeField(auto_now_add=True)),
|
||||
('is_read', models.BooleanField(default=False)),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notifications', to='core.profile')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Message',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('content', models.TextField()),
|
||||
('timestamp', models.DateTimeField(auto_now_add=True)),
|
||||
('is_read', models.BooleanField(default=False)),
|
||||
('receiver', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='received_messages', to='core.profile')),
|
||||
('sender', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sent_messages', to='core.profile')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Startup',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('headline', models.CharField(max_length=255)),
|
||||
('description', models.TextField()),
|
||||
('target_amount', models.DecimalField(decimal_places=2, max_digits=12)),
|
||||
('raised_amount', models.DecimalField(decimal_places=2, default=0, max_digits=12)),
|
||||
('status', models.CharField(choices=[('DRAFT', 'Draft'), ('FUNDING', 'Funding'), ('FUNDED', 'Funded')], default='DRAFT', max_length=10)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('founder', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='startups', to='core.profile')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Investment',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('amount', models.DecimalField(decimal_places=2, max_digits=12)),
|
||||
('timestamp', models.DateTimeField(auto_now_add=True)),
|
||||
('investor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='investments', to='core.profile')),
|
||||
('startup', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='investments', to='core.startup')),
|
||||
],
|
||||
),
|
||||
]
|
||||
BIN
core/migrations/__pycache__/0001_initial.cpython-311.pyc
Normal file
BIN
core/migrations/__pycache__/0001_initial.cpython-311.pyc
Normal file
Binary file not shown.
@ -1,3 +1,92 @@
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
from django.db.models.signals import post_save
|
||||
from django.dispatch import receiver
|
||||
|
||||
# Create your models here.
|
||||
class Tag(models.Model):
|
||||
name = models.CharField(max_length=50, unique=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Profile(models.Model):
|
||||
ROLE_CHOICES = [
|
||||
('FOUNDER', 'Founder'),
|
||||
('INVESTOR', 'Investor'),
|
||||
('PENDING', 'Pending'),
|
||||
]
|
||||
|
||||
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
|
||||
role = models.CharField(max_length=10, choices=ROLE_CHOICES, default='PENDING')
|
||||
university = models.CharField(max_length=255, blank=True)
|
||||
graduation_year = models.IntegerField(null=True, blank=True)
|
||||
bio = models.CharField(max_length=150, blank=True)
|
||||
interests = models.ManyToManyField(Tag, blank=True)
|
||||
|
||||
# Investor specific
|
||||
investment_appetite = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True)
|
||||
investment_frequency = models.CharField(max_length=100, blank=True)
|
||||
|
||||
# Founder specific (for partner matching)
|
||||
matching_answers = models.JSONField(null=True, blank=True) # To store questionnaire responses
|
||||
|
||||
is_verified = models.BooleanField(default=False)
|
||||
|
||||
def __str__(self):
|
||||
return self.user.username
|
||||
|
||||
@receiver(post_save, sender=User)
|
||||
def create_user_profile(sender, instance, created, **kwargs):
|
||||
if created:
|
||||
Profile.objects.create(user=instance)
|
||||
|
||||
@receiver(post_save, sender=User)
|
||||
def save_user_profile(sender, instance, **kwargs):
|
||||
instance.profile.save()
|
||||
|
||||
class Startup(models.Model):
|
||||
STATUS_CHOICES = [
|
||||
('DRAFT', 'Draft'),
|
||||
('FUNDING', 'Funding'),
|
||||
('FUNDED', 'Funded'),
|
||||
]
|
||||
|
||||
founder = models.ForeignKey(Profile, on_delete=models.CASCADE, related_name='startups')
|
||||
name = models.CharField(max_length=255)
|
||||
headline = models.CharField(max_length=255)
|
||||
description = models.TextField()
|
||||
target_amount = models.DecimalField(max_digits=12, decimal_places=2)
|
||||
raised_amount = models.DecimalField(max_digits=12, decimal_places=2, default=0)
|
||||
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='DRAFT')
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Investment(models.Model):
|
||||
investor = models.ForeignKey(Profile, on_delete=models.CASCADE, related_name='investments')
|
||||
startup = models.ForeignKey(Startup, on_delete=models.CASCADE, related_name='investments')
|
||||
amount = models.DecimalField(max_digits=12, decimal_places=2)
|
||||
timestamp = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.investor.user.username} invested in {self.startup.name}"
|
||||
|
||||
class Message(models.Model):
|
||||
sender = models.ForeignKey(Profile, on_delete=models.CASCADE, related_name='sent_messages')
|
||||
receiver = models.ForeignKey(Profile, on_delete=models.CASCADE, related_name='received_messages')
|
||||
content = models.TextField()
|
||||
timestamp = models.DateTimeField(auto_now_add=True)
|
||||
is_read = models.BooleanField(default=False)
|
||||
|
||||
def __str__(self):
|
||||
return f"From {self.sender.user.username} to {self.receiver.user.username}"
|
||||
|
||||
class Notification(models.Model):
|
||||
user = models.ForeignKey(Profile, on_delete=models.CASCADE, related_name='notifications')
|
||||
content = models.TextField()
|
||||
timestamp = models.DateTimeField(auto_now_add=True)
|
||||
is_read = models.BooleanField(default=False)
|
||||
|
||||
def __str__(self):
|
||||
return f"Notification for {self.user.user.username}"
|
||||
@ -1,9 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{% block title %}Knowledge Base{% endblock %}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}Gatsby{% endblock %}</title>
|
||||
{% if project_description %}
|
||||
<meta name="description" content="{{ project_description }}">
|
||||
<meta property="og:description" content="{{ project_description }}">
|
||||
@ -14,12 +14,129 @@
|
||||
<meta property="twitter:image" content="{{ project_image_url }}">
|
||||
{% endif %}
|
||||
{% load static %}
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<link rel="stylesheet" href="{% static 'css/custom.css' %}?v={{ deployment_timestamp }}">
|
||||
{% block head %}{% endblock %}
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
||||
{% block extra_css %}{% endblock %}
|
||||
<style>
|
||||
body { font-family: 'Inter', sans-serif; }
|
||||
.navbar-brand { font-weight: 800; font-size: 1.5rem; letter-spacing: -1px; }
|
||||
.nav-link { color: var(--text-secondary); font-weight: 500; font-size: 0.95rem; }
|
||||
.nav-link:hover { color: var(--text-primary); }
|
||||
.navbar { padding: 16px 0; }
|
||||
.nav-icon { margin-right: 5px; }
|
||||
.nav-active { color: var(--text-primary) !important; font-weight: 700 !important; }
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{% block content %}{% endblock %}
|
||||
</body>
|
||||
<nav class="navbar navbar-expand-lg sticky-top">
|
||||
<div class="container">
|
||||
<a class="navbar-brand text-white" href="{% url 'home' %}">GATSBY</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav ms-auto align-items-center">
|
||||
{% if user.is_authenticated %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link px-3 {% if request.resolver_match.url_name == 'discover' %}nav-active{% endif %}" href="{% url 'discover' %}">
|
||||
<i class="bi bi-compass nav-icon"></i>Discover
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link px-3 {% if request.resolver_match.url_name == 'partners' %}nav-active{% endif %}" href="{% url 'partners' %}">
|
||||
<i class="bi bi-people nav-icon"></i>Partners
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link px-3 {% if request.resolver_match.url_name == 'messages' %}nav-active{% endif %}" href="{% url 'messages' %}">
|
||||
<i class="bi bi-chat-left-dots nav-icon"></i>Messages
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item me-2">
|
||||
<a class="nav-link px-3 {% if request.resolver_match.url_name == 'portfolio' %}nav-active{% endif %}" href="{% url 'portfolio' %}">
|
||||
<i class="bi bi-grid-fill nav-icon"></i>Portfolio
|
||||
</a>
|
||||
</li>
|
||||
|
||||
{% if user.profile.role == 'FOUNDER' %}
|
||||
<li class="nav-item">
|
||||
<a class="btn btn-outline-primary rounded-pill btn-sm px-3 me-3" href="{% url 'post_startup' %}">
|
||||
<i class="bi bi-plus-lg me-1"></i>Post Startup
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle text-white d-flex align-items-center" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown">
|
||||
<div class="rounded-circle bg-dark-subtle d-inline-block text-center me-2" style="width: 32px; height: 32px; line-height: 32px;">
|
||||
{{ user.username|first|upper }}
|
||||
</div>
|
||||
<span>{{ user.username }}</span>
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-end glass-card border-0 shadow-lg mt-3 p-2">
|
||||
<li>
|
||||
<form action="{% url 'logout' %}" method="post" class="d-inline">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="dropdown-item text-danger rounded-3 p-2">
|
||||
<i class="bi bi-box-arrow-right me-2"></i>Sign Out
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="nav-item"><a class="nav-link px-3" href="{% url 'login' %}">Sign In</a></li>
|
||||
<li class="nav-item ms-lg-3"><a class="btn btn-primary rounded-pill px-4" href="{% url 'signup' %}">Get Started</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container mt-4">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
|
||||
<footer class="py-5 mt-5">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<h5 class="text-white mb-4">Gatsby</h5>
|
||||
<p class="text-secondary small">Empowering the next generation of student-led startups. Built by peers, for peers.</p>
|
||||
</div>
|
||||
<div class="col-md-2 offset-md-2">
|
||||
<h6 class="text-white mb-3 text-uppercase small ls-wide">Platform</h6>
|
||||
<ul class="list-unstyled">
|
||||
<li><a href="{% url 'discover' %}" class="text-secondary text-decoration-none small">Discover</a></li>
|
||||
<li><a href="{% url 'partners' %}" class="text-secondary text-decoration-none small">Partners</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<h6 class="text-white mb-3 text-uppercase small ls-wide">Community</h6>
|
||||
<ul class="list-unstyled">
|
||||
<li><a href="#" class="text-secondary text-decoration-none small">University Leaders</a></li>
|
||||
<li><a href="#" class="text-secondary text-decoration-none small">Success Stories</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<h6 class="text-white mb-3 text-uppercase small ls-wide">Support</h6>
|
||||
<ul class="list-unstyled">
|
||||
<li><a href="#" class="text-secondary text-decoration-none small">Help Center</a></li>
|
||||
<li><a href="#" class="text-secondary text-decoration-none small">Terms</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border-top border-secondary mt-5 pt-4 text-center">
|
||||
<p class="text-secondary small">© 2026 Gatsby Platform. For university students and graduates only.</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
141
core/templates/core/chat_detail.html
Normal file
141
core/templates/core/chat_detail.html
Normal file
@ -0,0 +1,141 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Chat with {{ chat_partner.user.username }} - Gatsby{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
<style>
|
||||
.chat-container {
|
||||
height: calc(100vh - 400px);
|
||||
min-height: 400px;
|
||||
overflow-y: auto;
|
||||
padding-right: 15px;
|
||||
}
|
||||
.message-bubble {
|
||||
max-width: 75%;
|
||||
border-radius: 20px;
|
||||
padding: 12px 18px;
|
||||
margin-bottom: 12px;
|
||||
font-size: 0.95rem;
|
||||
position: relative;
|
||||
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
||||
}
|
||||
.message-sent {
|
||||
background: linear-gradient(135deg, var(--primary-accent), #3d5afe);
|
||||
color: white;
|
||||
margin-left: auto;
|
||||
border-bottom-right-radius: 5px;
|
||||
}
|
||||
.message-received {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
color: #e0e0e0;
|
||||
margin-right: auto;
|
||||
border-bottom-left-radius: 5px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.message-time {
|
||||
font-size: 0.75rem;
|
||||
color: rgba(255,255,255,0.5);
|
||||
margin-top: 5px;
|
||||
display: block;
|
||||
}
|
||||
.chat-container::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
.chat-container::-webkit-scrollbar-thumb {
|
||||
background: rgba(255,255,255,0.1);
|
||||
border-radius: 10px;
|
||||
}
|
||||
.glass-input {
|
||||
background: rgba(0, 0, 0, 0.3) !important;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1) !important;
|
||||
color: white !important;
|
||||
border-radius: 30px !important;
|
||||
padding-left: 20px !important;
|
||||
padding-right: 20px !important;
|
||||
}
|
||||
.glass-input:focus {
|
||||
background: rgba(0, 0, 0, 0.5) !important;
|
||||
border-color: var(--primary-accent) !important;
|
||||
box-shadow: 0 0 15px rgba(0, 163, 255, 0.2) !important;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-10 offset-md-1">
|
||||
<div class="card glass-card p-0 overflow-hidden shadow-lg border-0">
|
||||
<!-- Chat Header -->
|
||||
<div class="card-header p-4 border-bottom border-secondary-subtle d-flex align-items-center">
|
||||
<a href="{% url 'messages' %}" class="btn btn-link text-white p-0 me-3">
|
||||
<i class="bi bi-arrow-left" style="font-size: 1.25rem;"></i>
|
||||
</a>
|
||||
<div class="rounded-circle bg-dark p-2 text-white fw-bold me-3 shadow-sm border border-secondary" style="width: 45px; height: 45px; display: flex; align-items: center; justify-content: center;">
|
||||
{{ chat_partner.user.username|first|upper }}
|
||||
</div>
|
||||
<div>
|
||||
<h5 class="mb-0 text-white fw-bold">{{ chat_partner.user.username }}</h5>
|
||||
<p class="mb-0 text-secondary small">{{ chat_partner.university }} · {{ chat_partner.role|title }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Chat Body -->
|
||||
<div class="card-body p-4 chat-container" id="chat-box">
|
||||
{% for message in messages %}
|
||||
<div class="message-bubble {% if message.sender == user.profile %}message-sent{% else %}message-received{% endif %}">
|
||||
{{ message.content }}
|
||||
<span class="message-time">
|
||||
{% if message.sender == user.profile %}You{% else %}{{ message.sender.user.username }}{% endif %} · {{ message.timestamp|date:"H:i" }}
|
||||
</span>
|
||||
</div>
|
||||
{% empty %}
|
||||
<div class="text-center py-5">
|
||||
<p class="text-secondary small">This is the beginning of your conversation with {{ chat_partner.user.username }}.</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<!-- Chat Footer -->
|
||||
<div class="card-footer p-4 border-top border-secondary-subtle">
|
||||
<form method="post" id="message-form">
|
||||
{% csrf_token %}
|
||||
<div class="input-group">
|
||||
<textarea name="content" class="form-control glass-input" placeholder="Type a message..." rows="1" required id="id_content" style="resize: none;"></textarea>
|
||||
<button class="btn btn-primary rounded-pill px-4 py-2 ms-2 fw-bold" type="submit">
|
||||
<i class="bi bi-send-fill"></i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Scroll to bottom of chat
|
||||
const chatBox = document.getElementById('chat-box');
|
||||
chatBox.scrollTop = chatBox.scrollHeight;
|
||||
|
||||
// Auto-expand textarea
|
||||
const textarea = document.getElementById('id_content');
|
||||
textarea.addEventListener('input', function() {
|
||||
this.style.height = 'auto';
|
||||
this.style.height = (this.scrollHeight) + 'px';
|
||||
if (this.scrollHeight > 150) {
|
||||
this.style.overflowY = 'scroll';
|
||||
this.style.height = '150px';
|
||||
} else {
|
||||
this.style.overflowY = 'hidden';
|
||||
}
|
||||
});
|
||||
|
||||
// Handle enter key
|
||||
textarea.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
document.getElementById('message-form').submit();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
33
core/templates/core/create_startup.html
Normal file
33
core/templates/core/create_startup.html
Normal file
@ -0,0 +1,33 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Post Startup - Gatsby{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card glass-card p-4">
|
||||
<h2 class="fw-bold mb-4 text-gradient">Post New Startup</h2>
|
||||
<p class="text-secondary">Ready to build the future? Share your vision with the university ecosystem.</p>
|
||||
|
||||
<form method="post" class="mt-4">
|
||||
{% csrf_token %}
|
||||
{% for field in form %}
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-secondary small">{{ field.label }}</label>
|
||||
{{ field }}
|
||||
{% if field.errors %}
|
||||
<div class="text-danger small">{{ field.errors }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<div class="mt-5">
|
||||
<button type="submit" class="btn btn-primary w-100 py-3 rounded-pill fw-bold">List Startup for Funding</button>
|
||||
<a href="{% url 'portfolio' %}" class="btn btn-link w-100 mt-2 text-secondary">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
57
core/templates/core/discover.html
Normal file
57
core/templates/core/discover.html
Normal file
@ -0,0 +1,57 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<section class="container py-5">
|
||||
<div class="row mb-5">
|
||||
<div class="col-md-8">
|
||||
<h2 class="text-white mb-3">Discover Peer-Led <span class="text-gradient">Startups</span></h2>
|
||||
<p class="text-secondary mb-0">Browse through the latest student and graduate startups looking for investment and partners.</p>
|
||||
</div>
|
||||
<div class="col-md-4 text-md-end align-self-end">
|
||||
<a href="#" class="btn btn-outline-light rounded-pill px-4 py-2 mt-4 mt-md-0"><i class="fa-solid fa-plus me-2"></i>Post Startup</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
{% for startup in startups %}
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<div class="glass-card h-100 p-0 overflow-hidden">
|
||||
<div class="p-4">
|
||||
<div class="d-flex justify-content-between align-items-start mb-3">
|
||||
<span class="badge bg-dark border border-secondary text-secondary rounded-pill px-3 py-2 fw-medium">Fintech</span>
|
||||
<span class="text-white small fw-bold"><i class="fa-solid fa-hourglass-start me-1 text-primary"></i> 12 days left</span>
|
||||
</div>
|
||||
<h4 class="text-white mb-2">{{ startup.name }}</h4>
|
||||
<p class="text-secondary small mb-4">{{ startup.headline }}</p>
|
||||
|
||||
<div class="mb-4">
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
<span class="text-white small fw-bold">£{{ startup.raised_amount|floatformat:0 }} raised</span>
|
||||
<span class="text-secondary small">{{ startup.raised_amount|divisibleby:startup.target_amount|default:0 }}% of £{{ startup.target_amount|floatformat:0 }}</span>
|
||||
</div>
|
||||
<div class="progress bg-dark" style="height: 6px;">
|
||||
<div class="progress-bar bg-primary" role="progressbar" style="width: 25%;" aria-valuenow="25" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-grid">
|
||||
<a href="{% url 'startup_detail' startup.pk %}" class="btn btn-primary rounded-pill">View Details</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% empty %}
|
||||
<div class="col-12 text-center py-5">
|
||||
<div class="glass-card py-5">
|
||||
<div class="mb-4">
|
||||
<i class="fa-solid fa-magnifying-glass fa-3x text-secondary"></i>
|
||||
</div>
|
||||
<h4 class="text-white mb-2">No startups found yet</h4>
|
||||
<p class="text-secondary small">Be the first to post your vision and start raising capital from your peers.</p>
|
||||
<a href="#" class="btn btn-primary mt-3">Post Your Startup</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
@ -1,145 +1,93 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ project_name }}{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--bg-color-start: #6a11cb;
|
||||
--bg-color-end: #2575fc;
|
||||
--text-color: #ffffff;
|
||||
--card-bg-color: rgba(255, 255, 255, 0.01);
|
||||
--card-border-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Inter', sans-serif;
|
||||
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
|
||||
color: var(--text-color);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
body::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'><path d='M-10 10L110 10M10 -10L10 110' stroke-width='1' stroke='rgba(255,255,255,0.05)'/></svg>");
|
||||
animation: bg-pan 20s linear infinite;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
@keyframes bg-pan {
|
||||
0% {
|
||||
background-position: 0% 0%;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: 100% 100%;
|
||||
}
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: var(--card-bg-color);
|
||||
border: 1px solid var(--card-border-color);
|
||||
border-radius: 16px;
|
||||
padding: 2.5rem 2rem;
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
box-shadow: 0 12px 36px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: clamp(2.2rem, 3vw + 1.2rem, 3.2rem);
|
||||
font-weight: 700;
|
||||
margin: 0 0 1.2rem;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0.5rem 0;
|
||||
font-size: 1.1rem;
|
||||
opacity: 0.92;
|
||||
}
|
||||
|
||||
.loader {
|
||||
margin: 1.5rem auto;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border: 4px solid rgba(255, 255, 255, 0.25);
|
||||
border-top-color: #fff;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.runtime code {
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
padding: 0.15rem 0.45rem;
|
||||
border-radius: 4px;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
}
|
||||
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
border: 0;
|
||||
}
|
||||
|
||||
footer {
|
||||
position: absolute;
|
||||
bottom: 1rem;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 0.85rem;
|
||||
opacity: 0.75;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
<main>
|
||||
<div class="card">
|
||||
<h1>Analyzing your requirements and generating your app…</h1>
|
||||
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
|
||||
<span class="sr-only">Loading…</span>
|
||||
<section class="hero-section container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-10">
|
||||
<div class="feature-pill">
|
||||
<span class="feature-dot"></span>
|
||||
<span class="text-white fw-medium">Exclusive for Students & Grads</span>
|
||||
</div>
|
||||
<h1 class="hero-title text-white">Build and Fund the <span class="text-gradient">Future Together.</span></h1>
|
||||
<p class="hero-subtitle text-secondary">The only university networking and micro-investment platform where verified students and recent graduates support each other's startups.</p>
|
||||
<div class="d-flex flex-column flex-sm-row justify-content-center gap-3">
|
||||
<a href="{% url 'signup' %}" class="btn btn-primary btn-lg">Get Started</a>
|
||||
<a href="#how-it-works" class="btn btn-outline-light btn-lg rounded-pill px-4">See How It Works</a>
|
||||
</div>
|
||||
</div>
|
||||
<p class="hint">AppWizzy AI is collecting your requirements and applying the first changes.</p>
|
||||
<p class="hint">This page will refresh automatically as the plan is implemented.</p>
|
||||
<p class="runtime">
|
||||
Runtime: Django <code>{{ django_version }}</code> · Python <code>{{ python_version }}</code>
|
||||
— UTC <code>{{ current_time|date:"Y-m-d H:i:s" }}</code>
|
||||
</p>
|
||||
</div>
|
||||
</main>
|
||||
<footer>
|
||||
Page updated: {{ current_time|date:"Y-m-d H:i:s" }} (UTC)
|
||||
</footer>
|
||||
{% endblock %}
|
||||
</section>
|
||||
|
||||
<section class="container mb-5 pb-5">
|
||||
<div class="row g-4">
|
||||
<div class="col-md-4">
|
||||
<div class="glass-card h-100">
|
||||
<div class="mb-4">
|
||||
<i class="fa-solid fa-user-shield fa-2x text-gradient"></i>
|
||||
</div>
|
||||
<h3 class="text-white mb-3">Verify</h3>
|
||||
<p class="text-secondary small">Exclusively for students and graduates within the last 5 years. Use your university email to join the network.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="glass-card h-100">
|
||||
<div class="mb-4">
|
||||
<i class="fa-solid fa-people-group fa-2x text-gradient"></i>
|
||||
</div>
|
||||
<h3 class="text-white mb-3">Connect</h3>
|
||||
<p class="text-secondary small">Find the perfect co-founder or team member using our AI-driven matching questionnaire based on skills and vision.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="glass-card h-100">
|
||||
<div class="mb-4">
|
||||
<i class="fa-solid fa-rocket fa-2x text-gradient"></i>
|
||||
</div>
|
||||
<h3 class="text-white mb-3">Fund</h3>
|
||||
<p class="text-secondary small">Start raising capital from your peers or discover student-led startups to invest in, starting from as little as £50.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="how-it-works" class="container py-5">
|
||||
<div class="text-center mb-5">
|
||||
<h2 class="text-white mb-4">How it Works</h2>
|
||||
<p class="text-secondary">Three simple steps to launch your vision or grow your portfolio.</p>
|
||||
</div>
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-8">
|
||||
<div class="d-flex flex-column gap-5">
|
||||
<div class="d-flex align-items-start gap-4">
|
||||
<div class="bg-primary rounded-circle d-flex align-items-center justify-content-center flex-shrink-0" style="width: 40px; height: 40px;">
|
||||
<span class="text-white fw-bold">1</span>
|
||||
</div>
|
||||
<div>
|
||||
<h5 class="text-white mb-2">Create Your Verified Profile</h5>
|
||||
<p class="text-secondary">Sign up with your .edu or university email. We verify your status as a current student or recent graduate instantly.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-start gap-4">
|
||||
<div class="bg-primary rounded-circle d-flex align-items-center justify-content-center flex-shrink-0" style="width: 40px; height: 40px;">
|
||||
<span class="text-white fw-bold">2</span>
|
||||
</div>
|
||||
<div>
|
||||
<h5 class="text-white mb-2">Define Your Role</h5>
|
||||
<p class="text-secondary">Choose to be a Founder looking for investment and partners, or an Investor looking to support peer-led innovation.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-start gap-4">
|
||||
<div class="bg-primary rounded-circle d-flex align-items-center justify-content-center flex-shrink-0" style="width: 40px; height: 40px;">
|
||||
<span class="text-white fw-bold">3</span>
|
||||
</div>
|
||||
<div>
|
||||
<h5 class="text-white mb-2">Start Networking & Investing</h5>
|
||||
<p class="text-secondary">Browse listings, connect with founders, or set up your startup profile and start your first funding round.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
31
core/templates/core/invest.html
Normal file
31
core/templates/core/invest.html
Normal file
@ -0,0 +1,31 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<section class="container py-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6 text-center">
|
||||
<div class="glass-card px-4 py-5">
|
||||
<h2 class="text-white mb-4">Invest in <span class="text-gradient">{{ startup.name }}</span></h2>
|
||||
<p class="text-secondary small mb-5">Be part of the next big university startup. Minimum investment is £50.</p>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="mb-4 text-start">
|
||||
<label class="form-label text-white small fw-bold px-3">Amount (£)</label>
|
||||
<input type="number" name="amount" min="50" class="form-control bg-dark text-white border-secondary rounded-pill px-4 py-3 h1 mb-0 fw-bold text-center" placeholder="50" required>
|
||||
<div class="form-text text-secondary small text-center mt-3">Enter the amount you wish to invest in this project.</div>
|
||||
</div>
|
||||
|
||||
<div class="d-grid mt-5">
|
||||
<button type="submit" class="btn btn-primary btn-lg rounded-pill py-3">Confirm Investment</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="mt-4">
|
||||
<p class="text-secondary small">By clicking "Confirm Investment", you agree to our <a href="#" class="text-white fw-bold">Terms of Use</a> and acknowledge the risks involved.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
38
core/templates/core/login.html
Normal file
38
core/templates/core/login.html
Normal file
@ -0,0 +1,38 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<section class="container py-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6 text-center">
|
||||
<div class="glass-card">
|
||||
<h2 class="text-white mb-4">Welcome Back to <span class="text-gradient">GATSBY</span></h2>
|
||||
<p class="text-secondary small mb-5">Sign in to your account and explore the peer-to-peer startup ecosystem.</p>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{% if form.non_field_errors %}
|
||||
<div class="alert alert-danger mb-4 rounded-pill small py-2">{{ form.non_field_errors.0 }}</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="mb-3 text-start">
|
||||
<label class="form-label text-white small fw-bold px-3">Username</label>
|
||||
<input type="text" name="username" class="form-control bg-dark text-white border-secondary rounded-pill px-4 py-2" required>
|
||||
</div>
|
||||
<div class="mb-3 text-start">
|
||||
<label class="form-label text-white small fw-bold px-3">Password</label>
|
||||
<input type="password" name="password" class="form-control bg-dark text-white border-secondary rounded-pill px-4 py-2" required>
|
||||
</div>
|
||||
|
||||
<div class="d-grid mt-4">
|
||||
<button type="submit" class="btn btn-primary btn-lg">Sign In</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="text-center mt-4">
|
||||
<p class="text-secondary small">Don't have an account? <a href="{% url 'signup' %}" class="text-white fw-bold">Sign Up</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
41
core/templates/core/messages.html
Normal file
41
core/templates/core/messages.html
Normal file
@ -0,0 +1,41 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Messages - Gatsby{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h2 class="fw-bold mb-4 text-gradient">Your Chats</h2>
|
||||
|
||||
<div class="card glass-card">
|
||||
<div class="list-group list-group-flush bg-transparent">
|
||||
{% for partner in chat_partners %}
|
||||
<a href="{% url 'chat_detail' partner.pk %}" class="list-group-item list-group-item-action bg-transparent p-4 border-bottom border-secondary-subtle">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="rounded-circle bg-dark p-3 text-white fw-bold me-3 shadow-sm border border-secondary" style="width: 50px; height: 50px; display: flex; align-items: center; justify-content: center;">
|
||||
{{ partner.user.username|first|upper }}
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<h5 class="mb-1 text-white fw-bold">{{ partner.user.username }}</h5>
|
||||
<p class="mb-0 text-secondary small">{{ partner.university }} · {{ partner.role|title }}</p>
|
||||
</div>
|
||||
<div class="text-secondary small">
|
||||
<i class="bi bi-chevron-right"></i>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
{% empty %}
|
||||
<div class="p-5 text-center">
|
||||
<div class="p-3 mb-4 rounded-circle bg-dark-subtle d-inline-block">
|
||||
<i class="bi bi-chat-dots-fill text-secondary" style="font-size: 2rem;"></i>
|
||||
</div>
|
||||
<h5 class="text-white">No messages yet</h5>
|
||||
<p class="text-secondary">Start a conversation with a founder or partner from the <a href="{% url 'partners' %}" class="text-primary">Discover</a> page.</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
32
core/templates/core/onboarding/investor.html
Normal file
32
core/templates/core/onboarding/investor.html
Normal file
@ -0,0 +1,32 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<section class="container py-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="glass-card">
|
||||
<h2 class="text-white mb-4">Complete Your Investor Profile</h2>
|
||||
<p class="text-secondary small mb-4">Sharing your background and goals helps us match you with the right startups.</p>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-white small fw-bold px-3">Short Bio (Max 150 characters)</label>
|
||||
<textarea name="bio" class="form-control bg-dark text-white border-secondary rounded-4 px-4 py-3" rows="3" maxlength="150" required></textarea>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-white small fw-bold px-3">Investment Appetite (£)</label>
|
||||
<input type="number" name="appetite" min="50" class="form-control bg-dark text-white border-secondary rounded-pill px-4 py-2" placeholder="e.g. 500" required>
|
||||
<div class="form-text text-secondary small">What's the total amount you're comfortable investing across the platform?</div>
|
||||
</div>
|
||||
|
||||
<div class="d-grid mt-5">
|
||||
<button type="submit" class="btn btn-primary btn-lg">Finish Onboarding</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
99
core/templates/core/onboarding/partner_matching.html
Normal file
99
core/templates/core/onboarding/partner_matching.html
Normal file
@ -0,0 +1,99 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<section class="container py-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-8">
|
||||
<div class="glass-card p-5">
|
||||
<h2 class="text-white mb-4">Partner Matching Questionnaire</h2>
|
||||
<p class="text-secondary mb-5">Tell us more about your vision to find the perfect co-founder or team members.</p>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="mb-5">
|
||||
<h5 class="text-white mb-3 small fw-bold">1. Which area best describes your startup or project?</h5>
|
||||
<select name="industry" class="form-select bg-dark text-white border-secondary rounded-pill px-4 py-3" required>
|
||||
<option value="" disabled selected>Select an industry...</option>
|
||||
<option value="fintech">Fintech</option>
|
||||
<option value="healthtech">Healthtech</option>
|
||||
<option value="edtech">Edtech</option>
|
||||
<option value="sustainability">Sustainability</option>
|
||||
<option value="consumer">Consumer Tech</option>
|
||||
<option value="social-impact">Social Impact</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-5">
|
||||
<h5 class="text-white mb-3 small fw-bold">2. What role are you currently fulfilling?</h5>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-4 text-center">
|
||||
<label class="d-block">
|
||||
<input type="radio" name="current_role" value="TECH" class="d-none peer-choice" required>
|
||||
<div class="glass-card choice-card py-3">Technical</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-md-4 text-center">
|
||||
<label class="d-block">
|
||||
<input type="radio" name="current_role" value="BUSINESS" class="d-none peer-choice" required>
|
||||
<div class="glass-card choice-card py-3">Business</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-md-4 text-center">
|
||||
<label class="d-block">
|
||||
<input type="radio" name="current_role" value="DESIGN" class="d-none peer-choice" required>
|
||||
<div class="glass-card choice-card py-3">Design</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-5">
|
||||
<h5 class="text-white mb-3 small fw-bold">3. What kind of partner are you looking for?</h5>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-4 text-center">
|
||||
<label class="d-block">
|
||||
<input type="radio" name="target_role" value="TECH" class="d-none peer-choice" required>
|
||||
<div class="glass-card choice-card py-3">Technical</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-md-4 text-center">
|
||||
<label class="d-block">
|
||||
<input type="radio" name="target_role" value="BUSINESS" class="d-none peer-choice" required>
|
||||
<div class="glass-card choice-card py-3">Business</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-md-4 text-center">
|
||||
<label class="d-block">
|
||||
<input type="radio" name="target_role" value="DESIGN" class="d-none peer-choice" required>
|
||||
<div class="glass-card choice-card py-3">Design</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-5">
|
||||
<h5 class="text-white mb-3 small fw-bold">4. What's your primary goal for the next 3 months?</h5>
|
||||
<textarea name="goal" class="form-control bg-dark text-white border-secondary rounded-4 px-4 py-3" rows="4" placeholder="e.g. Building an MVP, finding our first 100 users, raising seed funding..." required></textarea>
|
||||
</div>
|
||||
|
||||
<div class="d-grid mt-5">
|
||||
<button type="submit" class="btn btn-primary btn-lg">Find Partners</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<style>
|
||||
.peer-choice:checked + .choice-card {
|
||||
border-color: var(--accent-blue) !important;
|
||||
background: rgba(33, 212, 253, 0.1);
|
||||
}
|
||||
.choice-card {
|
||||
cursor: pointer;
|
||||
border: 1px solid var(--border-glass) !important;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
59
core/templates/core/onboarding/role.html
Normal file
59
core/templates/core/onboarding/role.html
Normal file
@ -0,0 +1,59 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<section class="container py-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-8 text-center">
|
||||
<div class="glass-card px-4 py-5">
|
||||
<h2 class="text-white mb-4">Choose Your Role</h2>
|
||||
<p class="text-secondary mb-5">How do you plan to use Gatsby? You can change this later.</p>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="row g-4 mb-5">
|
||||
<div class="col-md-6">
|
||||
<label class="d-block h-100">
|
||||
<input type="radio" name="role" value="FOUNDER" class="d-none peer-role" required>
|
||||
<div class="glass-card role-card h-100 p-4 border-2">
|
||||
<div class="mb-4">
|
||||
<i class="fa-solid fa-lightbulb fa-3x text-gradient"></i>
|
||||
</div>
|
||||
<h4 class="text-white mb-3">Founder</h4>
|
||||
<p class="text-secondary small">I have a startup idea or a project and I'm looking for funding and partners.</p>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="d-block h-100">
|
||||
<input type="radio" name="role" value="INVESTOR" class="d-none peer-role" required>
|
||||
<div class="glass-card role-card h-100 p-4 border-2">
|
||||
<div class="mb-4">
|
||||
<i class="fa-solid fa-piggy-bank fa-3x text-gradient"></i>
|
||||
</div>
|
||||
<h4 class="text-white mb-3">Investor</h4>
|
||||
<p class="text-secondary small">I'm looking to support peer-led innovation and grow my investment portfolio.</p>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-grid mt-4">
|
||||
<button type="submit" class="btn btn-primary btn-lg">Next</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<style>
|
||||
.peer-role:checked + .role-card {
|
||||
border-color: var(--accent-blue) !important;
|
||||
background: rgba(33, 212, 253, 0.1);
|
||||
}
|
||||
.role-card {
|
||||
cursor: pointer;
|
||||
border: 2px solid transparent !important;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
62
core/templates/core/partners.html
Normal file
62
core/templates/core/partners.html
Normal file
@ -0,0 +1,62 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<section class="container py-5">
|
||||
<div class="row mb-5">
|
||||
<div class="col-md-8">
|
||||
<h2 class="text-white mb-3">Find Your Next <span class="text-gradient">Co-Founder</span></h2>
|
||||
<p class="text-secondary mb-0">Discover and connect with talented student and graduate founders looking for partners.</p>
|
||||
</div>
|
||||
<div class="col-md-4 text-md-end align-self-end">
|
||||
<a href="{% url 'partner_matching' %}" class="btn btn-outline-light rounded-pill px-4 py-2 mt-4 mt-md-0"><i class="bi bi-arrow-repeat me-2"></i>Retake Quiz</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
{% for partner in partners %}
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<div class="glass-card h-100 p-0 overflow-hidden d-flex flex-column">
|
||||
<div class="p-4 flex-grow-1">
|
||||
<div class="d-flex align-items-center mb-4">
|
||||
<div class="bg-dark rounded-circle d-flex align-items-center justify-content-center border border-secondary shadow-sm" style="width: 60px; height: 60px;">
|
||||
<i class="bi bi-person-fill fs-2 text-gradient"></i>
|
||||
</div>
|
||||
<div class="ms-3">
|
||||
<h5 class="text-white mb-0 fw-bold">{{ partner.user.username }}</h5>
|
||||
<span class="text-secondary small fw-bold">{{ partner.university }} · '{{ partner.graduation_year }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="text-secondary small mb-4 lh-lg">{{ partner.bio|default:"No bio available yet. I'm excited to build something new!" }}</p>
|
||||
|
||||
<div class="mb-4">
|
||||
<h6 class="text-white small fw-bold mb-3">Primary Focus</h6>
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<span class="badge bg-dark border border-secondary text-secondary rounded-pill px-3 py-2 fw-medium">Fintech</span>
|
||||
<span class="badge bg-dark border border-secondary text-secondary rounded-pill px-3 py-2 fw-medium">Tech</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-grid mt-4">
|
||||
<a href="{% url 'chat_detail' partner.pk %}" class="btn btn-primary rounded-pill py-2 fw-bold shadow-sm">
|
||||
<i class="bi bi-chat-dots-fill me-2"></i>Message & Connect
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% empty %}
|
||||
<div class="col-12 text-center py-5">
|
||||
<div class="glass-card py-5 shadow-lg border-0">
|
||||
<div class="mb-4">
|
||||
<i class="bi bi-person-plus-fill fs-1 text-secondary opacity-50"></i>
|
||||
</div>
|
||||
<h4 class="text-white mb-2">No partners found yet</h4>
|
||||
<p class="text-secondary small max-w-400 mx-auto">Be the first to create your founder profile and find your perfect co-founder.</p>
|
||||
<a href="{% url 'partner_matching' %}" class="btn btn-primary mt-3 rounded-pill px-5">Create Profile</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
102
core/templates/core/portfolio.html
Normal file
102
core/templates/core/portfolio.html
Normal file
@ -0,0 +1,102 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<section class="container py-5">
|
||||
<div class="row mb-5 align-items-end">
|
||||
<div class="col-md-8">
|
||||
<h2 class="text-white mb-3">Your <span class="text-gradient">Portfolio</span></h2>
|
||||
<p class="text-secondary mb-0">Manage your investments and track your startup's progress.</p>
|
||||
</div>
|
||||
<div class="col-md-4 text-md-end">
|
||||
<div class="glass-card py-3 px-4 d-inline-block border border-secondary shadow-lg">
|
||||
<span class="text-white small fw-bold me-2 opacity-75">Total Invested:</span>
|
||||
<span class="text-gradient h4 mb-0 fw-bold">£{{ total_invested|floatformat:0 }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-5">
|
||||
<!-- Investments Section -->
|
||||
<div class="col-lg-12">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h4 class="text-white fw-bold mb-0">My Investments</h4>
|
||||
<a href="{% url 'discover' %}" class="btn btn-link text-primary text-decoration-none small fw-bold">
|
||||
<i class="bi bi-plus-lg me-1"></i>New Investment
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{% for investment in investments %}
|
||||
<div class="glass-card mb-3 p-3 hover-lift border-0 shadow-sm">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-4">
|
||||
<h5 class="text-white mb-1 fw-bold">{{ investment.startup.name }}</h5>
|
||||
<span class="text-secondary small fw-medium opacity-75">{{ investment.timestamp|date:"d M Y" }}</span>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<span class="text-white fw-bold">£{{ investment.amount|floatformat:0 }}</span>
|
||||
<div class="text-secondary small opacity-75">Invested</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<span class="text-white fw-bold">£{{ investment.startup.raised_amount|floatformat:0 }}</span>
|
||||
<div class="text-secondary small opacity-75">Total Raised</div>
|
||||
</div>
|
||||
<div class="col-md-2 text-md-end mt-3 mt-md-0">
|
||||
<a href="{% url 'startup_detail' investment.startup.pk %}" class="btn btn-outline-light btn-sm rounded-pill px-4 py-2 border-secondary">View Detail</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% empty %}
|
||||
<div class="glass-card py-5 text-center border-0 shadow-sm">
|
||||
<div class="mb-3">
|
||||
<i class="bi bi-wallet2 fs-2 text-secondary opacity-50"></i>
|
||||
</div>
|
||||
<p class="text-secondary small">No investments found yet. Browse startups and start supporting your peers.</p>
|
||||
<a href="{% url 'discover' %}" class="btn btn-primary rounded-pill mt-3 px-4 fw-bold">Explore Startups</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<!-- My Startups Section -->
|
||||
<div class="col-lg-12">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h4 class="text-white fw-bold mb-0">My Startups</h4>
|
||||
{% if user.profile.role == 'FOUNDER' %}
|
||||
<a href="{% url 'post_startup' %}" class="btn btn-link text-primary text-decoration-none small fw-bold">
|
||||
<i class="bi bi-plus-lg me-1"></i>Post New Startup
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% for startup in my_startups %}
|
||||
<div class="glass-card mb-3 p-4 hover-lift border-0 shadow-sm">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-4">
|
||||
<h5 class="text-white mb-1 fw-bold">{{ startup.name }}</h5>
|
||||
<span class="badge bg-dark-subtle border border-secondary text-secondary rounded-pill px-3 py-1 fw-medium">{{ startup.status }}</span>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<span class="text-white fw-bold">£{{ startup.raised_amount|floatformat:0 }}</span>
|
||||
<div class="text-secondary small opacity-75">Raised</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<span class="text-white fw-bold">£{{ startup.target_amount|floatformat:0 }}</span>
|
||||
<div class="text-secondary small opacity-75">Target</div>
|
||||
</div>
|
||||
<div class="col-md-2 text-md-end mt-3 mt-md-0">
|
||||
<a href="{% url 'startup_detail' startup.pk %}" class="btn btn-outline-light btn-sm rounded-pill px-4 py-2 border-secondary">Manage</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% empty %}
|
||||
<div class="glass-card py-5 text-center border-0 shadow-sm">
|
||||
<div class="mb-3">
|
||||
<i class="bi bi-rocket-takeoff fs-2 text-secondary opacity-50"></i>
|
||||
</div>
|
||||
<p class="text-secondary small">You haven't posted any startups yet. Ready to build the future?</p>
|
||||
<a href="{% url 'post_startup' %}" class="btn btn-primary rounded-pill mt-3 px-4 fw-bold">Post Your Startup</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
38
core/templates/core/signup.html
Normal file
38
core/templates/core/signup.html
Normal file
@ -0,0 +1,38 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<section class="container py-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="glass-card">
|
||||
<h2 class="text-white mb-4">Create Your Profile</h2>
|
||||
<p class="text-secondary small mb-4">Join Gatsby and connect with the next generation of student-led startups. Only university or graduate emails from the last 5 years are accepted.</p>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{% for field in form %}
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-white small fw-bold">{{ field.label }}</label>
|
||||
<input type="{{ field.widget_type }}" name="{{ field.html_name }}" class="form-control bg-dark text-white border-secondary rounded-pill px-4 py-2" required>
|
||||
{% if field.help_text %}
|
||||
<div class="form-text text-secondary small">{{ field.help_text }}</div>
|
||||
{% endif %}
|
||||
{% if field.errors %}
|
||||
<div class="text-danger small mt-1">{{ field.errors.0 }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<div class="d-grid mt-4">
|
||||
<button type="submit" class="btn btn-primary btn-lg">Sign Up</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="text-center mt-4">
|
||||
<p class="text-secondary small">Already have an account? <a href="{% url 'login' %}" class="text-white fw-bold">Sign In</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
84
core/templates/core/startup_detail.html
Normal file
84
core/templates/core/startup_detail.html
Normal file
@ -0,0 +1,84 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<section class="container py-5">
|
||||
<div class="row g-5">
|
||||
<div class="col-lg-8">
|
||||
<div class="glass-card mb-5 p-5 border-0 shadow-lg">
|
||||
<div class="d-flex justify-content-between align-items-start mb-4">
|
||||
<span class="badge bg-dark border border-secondary text-secondary rounded-pill px-3 py-2 fw-medium">Fintech</span>
|
||||
<span class="text-white small fw-bold opacity-75"><i class="bi bi-clock-history me-1 text-primary"></i> 12 days left</span>
|
||||
</div>
|
||||
<h1 class="text-white mb-3 fw-bold">{{ startup.name }}</h1>
|
||||
<p class="text-secondary lead mb-5 opacity-75">{{ startup.headline }}</p>
|
||||
|
||||
<div class="mb-5">
|
||||
<h4 class="text-white mb-3 fw-bold">About the Project</h4>
|
||||
<p class="text-secondary opacity-75">{{ startup.description }}</p>
|
||||
</div>
|
||||
|
||||
<div class="mb-5">
|
||||
<h4 class="text-white mb-3 fw-bold">The Vision</h4>
|
||||
<p class="text-secondary opacity-75">Gatsby is built by the community. This project aims to revolutionize its sector by leveraging the unique insights of university talent. Our goal is to scale beyond campus while maintaining our core community values.</p>
|
||||
</div>
|
||||
|
||||
<div class="p-4 bg-dark-subtle rounded-4 border border-secondary border-opacity-25">
|
||||
<h5 class="text-white mb-3 fw-bold"><i class="bi bi-person-circle me-2 text-primary"></i>Founder</h5>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="rounded-circle bg-dark p-2 text-white fw-bold me-3 shadow-sm border border-secondary" style="width: 40px; height: 40px; display: flex; align-items: center; justify-content: center;">
|
||||
{{ startup.founder.user.username|first|upper }}
|
||||
</div>
|
||||
<div>
|
||||
<p class="mb-0 text-white fw-bold">{{ startup.founder.user.username }}</p>
|
||||
<p class="mb-0 text-secondary small opacity-75">{{ startup.founder.university }} · Class of {{ startup.founder.graduation_year }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4">
|
||||
<div class="glass-card sticky-top shadow-lg border-0" style="top: 100px; z-index: 10;">
|
||||
<h4 class="text-white mb-4 fw-bold">Investment</h4>
|
||||
|
||||
<div class="mb-5">
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
<span class="text-white small fw-bold">£{{ startup.raised_amount|floatformat:0 }} raised</span>
|
||||
<span class="text-secondary small">Target: £{{ startup.target_amount|floatformat:0 }}</span>
|
||||
</div>
|
||||
<div class="progress bg-dark" style="height: 10px; border-radius: 5px;">
|
||||
{% widthratio startup.raised_amount startup.target_amount 100 as percentage %}
|
||||
<div class="progress-bar bg-primary" role="progressbar" style="width: {{ percentage }}%;" aria-valuenow="{{ percentage }}" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-column gap-3 mb-5 p-3 bg-white bg-opacity-5 rounded-4 border border-secondary border-opacity-10">
|
||||
<div class="d-flex justify-content-between">
|
||||
<span class="text-secondary small opacity-75">Minimum</span>
|
||||
<span class="text-white small fw-bold">£50</span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between">
|
||||
<span class="text-secondary small opacity-75">Backers</span>
|
||||
<span class="text-white small fw-bold">{{ startup.investments.count }}</span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between">
|
||||
<span class="text-secondary small opacity-75">Valuation</span>
|
||||
<span class="text-white small fw-bold">£25k - £100k</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-grid gap-3">
|
||||
<a href="{% url 'invest' startup.pk %}" class="btn btn-primary btn-lg rounded-pill fw-bold py-3 shadow-sm">Invest Now</a>
|
||||
<a href="{% url 'chat_detail' startup.founder.pk %}" class="btn btn-outline-light rounded-pill fw-bold py-3 border-secondary">
|
||||
<i class="bi bi-chat-left-dots-fill me-2"></i>Contact Founder
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 text-center">
|
||||
<p class="text-secondary small mb-0 opacity-75">Join the network of alumni and students supporting this vision.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
28
core/templates/core/verify.html
Normal file
28
core/templates/core/verify.html
Normal file
@ -0,0 +1,28 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<section class="container py-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6 text-center">
|
||||
<div class="glass-card">
|
||||
<div class="mb-4">
|
||||
<i class="fa-solid fa-envelope-circle-check fa-4x text-gradient"></i>
|
||||
</div>
|
||||
<h2 class="text-white mb-4">Verify Your Email</h2>
|
||||
<p class="text-secondary mb-5">We've sent a verification link to your university or graduate email address. Please click the link to confirm your status and access Gatsby.</p>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="d-grid mt-4">
|
||||
<button type="submit" class="btn btn-primary btn-lg">Continue (Demo Verified)</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="mt-4">
|
||||
<p class="text-secondary small">Didn't receive the email? <a href="#" class="text-white fw-bold">Resend Link</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
24
core/urls.py
24
core/urls.py
@ -1,7 +1,23 @@
|
||||
from django.urls import path
|
||||
|
||||
from .views import home
|
||||
from django.contrib.auth import views as auth_views
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path("", home, name="home"),
|
||||
]
|
||||
path('', views.home, name='home'),
|
||||
path('signup/', views.signup_view, name='signup'),
|
||||
path('login/', auth_views.LoginView.as_view(template_name='core/login.html'), name='login'),
|
||||
path('logout/', auth_views.LogoutView.as_view(next_page='home'), name='logout'),
|
||||
path('verify/', views.verify_view, name='verify'),
|
||||
path('onboarding/role/', views.onboarding_role, name='onboarding_role'),
|
||||
path('onboarding/investor/', views.onboarding_investor, name='onboarding_investor'),
|
||||
path('onboarding/founder/', views.onboarding_founder, name='onboarding_founder'),
|
||||
path('onboarding/matching/', views.onboarding_partner_matching, name='partner_matching'),
|
||||
path('discover/', views.discover_startups, name='discover'),
|
||||
path('partners/', views.find_partners, name='partners'),
|
||||
path('portfolio/', views.my_portfolio, name='portfolio'),
|
||||
path('startup/<int:pk>/', views.startup_detail, name='startup_detail'),
|
||||
path('startup/<int:pk>/invest/', views.invest, name='invest'),
|
||||
path('startup/post/', views.post_startup, name='post_startup'),
|
||||
path('messages/', views.messages_view, name='messages'),
|
||||
path('chat/<int:pk>/', views.chat_detail, name='chat_detail'),
|
||||
]
|
||||
214
core/views.py
214
core/views.py
@ -1,25 +1,195 @@
|
||||
import os
|
||||
import platform
|
||||
|
||||
from django import get_version as django_version
|
||||
from django.shortcuts import render
|
||||
from django.utils import timezone
|
||||
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.contrib.auth import login, authenticate
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from .models import Profile, Tag, Startup, Investment, Message, Notification
|
||||
from .forms import StartupForm, MessageForm
|
||||
from django.contrib.auth.forms import UserCreationForm
|
||||
from django import forms
|
||||
from django.db.models import Sum, Q
|
||||
|
||||
def home(request):
|
||||
"""Render the landing screen with loader and environment details."""
|
||||
host_name = request.get_host().lower()
|
||||
agent_brand = "AppWizzy" if host_name == "appwizzy.com" else "Flatlogic"
|
||||
now = timezone.now()
|
||||
return render(request, "core/index.html")
|
||||
|
||||
context = {
|
||||
"project_name": "New Style",
|
||||
"agent_brand": agent_brand,
|
||||
"django_version": django_version(),
|
||||
"python_version": platform.python_version(),
|
||||
"current_time": now,
|
||||
"host_name": host_name,
|
||||
"project_description": os.getenv("PROJECT_DESCRIPTION", ""),
|
||||
"project_image_url": os.getenv("PROJECT_IMAGE_URL", ""),
|
||||
}
|
||||
return render(request, "core/index.html", context)
|
||||
class SignupForm(UserCreationForm):
|
||||
university_email = forms.EmailField(required=True, label="University or Graduate Email")
|
||||
full_name = forms.CharField(required=True)
|
||||
university_name = forms.CharField(required=True)
|
||||
graduation_year = forms.IntegerField(required=True)
|
||||
|
||||
class Meta(UserCreationForm.Meta):
|
||||
fields = UserCreationForm.Meta.fields + ('university_email',)
|
||||
|
||||
def signup_view(request):
|
||||
if request.method == "POST":
|
||||
form = SignupForm(request.POST)
|
||||
if form.is_valid():
|
||||
user = form.save()
|
||||
profile = user.profile
|
||||
profile.university = form.cleaned_data.get('university_name')
|
||||
profile.graduation_year = form.cleaned_data.get('graduation_year')
|
||||
profile.save()
|
||||
# login(request, user) # Don't login yet, show verification screen
|
||||
request.session['pending_verification_user_id'] = user.id
|
||||
return redirect('verify')
|
||||
else:
|
||||
form = SignupForm()
|
||||
return render(request, "core/signup.html", {"form": form})
|
||||
|
||||
def verify_view(request):
|
||||
user_id = request.session.get('pending_verification_user_id')
|
||||
if not user_id:
|
||||
return redirect('signup')
|
||||
|
||||
if request.method == "POST":
|
||||
user = get_object_or_404(User, id=user_id)
|
||||
user.profile.is_verified = True
|
||||
user.profile.save()
|
||||
login(request, user)
|
||||
return redirect('onboarding_role')
|
||||
|
||||
return render(request, "core/verify.html")
|
||||
|
||||
@login_required
|
||||
def onboarding_role(request):
|
||||
if request.method == "POST":
|
||||
role = request.POST.get('role')
|
||||
request.user.profile.role = role
|
||||
request.user.profile.save()
|
||||
if role == 'INVESTOR':
|
||||
return redirect('onboarding_investor')
|
||||
else:
|
||||
return redirect('onboarding_founder')
|
||||
return render(request, "core/onboarding/role.html")
|
||||
|
||||
@login_required
|
||||
def onboarding_investor(request):
|
||||
if request.method == "POST":
|
||||
bio = request.POST.get('bio')
|
||||
appetite = request.POST.get('appetite')
|
||||
request.user.profile.bio = bio
|
||||
request.user.profile.investment_appetite = appetite
|
||||
request.user.profile.save()
|
||||
return redirect('discover')
|
||||
return render(request, "core/onboarding/investor.html")
|
||||
|
||||
@login_required
|
||||
def onboarding_founder(request):
|
||||
# This leads to partner matching questionnaire
|
||||
return redirect('partner_matching')
|
||||
|
||||
@login_required
|
||||
def onboarding_partner_matching(request):
|
||||
if request.method == "POST":
|
||||
# Save questionnaire answers in JSON format
|
||||
request.user.profile.matching_answers = request.POST.dict()
|
||||
request.user.profile.save()
|
||||
return redirect('partners')
|
||||
return render(request, "core/onboarding/partner_matching.html")
|
||||
|
||||
@login_required
|
||||
def discover_startups(request):
|
||||
startups = Startup.objects.filter(status='FUNDING').order_by('-created_at')
|
||||
return render(request, "core/discover.html", {"startups": startups})
|
||||
|
||||
@login_required
|
||||
def find_partners(request):
|
||||
current_profile = request.user.profile
|
||||
# Basic partner matching logic:
|
||||
# 1. Same university
|
||||
# 2. Shared interests (Tags) - if we had tags set up properly
|
||||
# For now, let's just prioritize same university and then others.
|
||||
|
||||
partners = Profile.objects.filter(role='FOUNDER').exclude(user=request.user)
|
||||
|
||||
# Sort by same university
|
||||
partners = sorted(partners, key=lambda p: p.university == current_profile.university, reverse=True)
|
||||
|
||||
return render(request, "core/partners.html", {"partners": partners})
|
||||
|
||||
@login_required
|
||||
def my_portfolio(request):
|
||||
profile = request.user.profile
|
||||
investments = Investment.objects.filter(investor=profile)
|
||||
total_invested = investments.aggregate(Sum('amount'))['amount__sum'] or 0
|
||||
my_startups = Startup.objects.filter(founder=profile)
|
||||
return render(request, "core/portfolio.html", {
|
||||
"investments": investments,
|
||||
"total_invested": total_invested,
|
||||
"my_startups": my_startups
|
||||
})
|
||||
|
||||
@login_required
|
||||
def startup_detail(request, pk):
|
||||
startup = get_object_or_404(Startup, pk=pk)
|
||||
return render(request, "core/startup_detail.html", {"startup": startup})
|
||||
|
||||
@login_required
|
||||
def invest(request, pk):
|
||||
startup = get_object_or_404(Startup, pk=pk)
|
||||
if request.method == "POST":
|
||||
amount = request.POST.get('amount')
|
||||
Investment.objects.create(
|
||||
investor=request.user.profile,
|
||||
startup=startup,
|
||||
amount=amount
|
||||
)
|
||||
startup.raised_amount += float(amount)
|
||||
startup.save()
|
||||
return redirect('portfolio')
|
||||
return render(request, "core/invest.html", {"startup": startup})
|
||||
|
||||
@login_required
|
||||
def post_startup(request):
|
||||
if request.method == "POST":
|
||||
form = StartupForm(request.POST)
|
||||
if form.is_valid():
|
||||
startup = form.save(commit=False)
|
||||
startup.founder = request.user.profile
|
||||
startup.save()
|
||||
return redirect('portfolio')
|
||||
else:
|
||||
form = StartupForm()
|
||||
return render(request, "core/create_startup.html", {"form": form})
|
||||
|
||||
@login_required
|
||||
def messages_view(request):
|
||||
profile = request.user.profile
|
||||
# Get all unique profiles the current user has chatted with
|
||||
sent_to = Message.objects.filter(sender=profile).values_list('receiver', flat=True)
|
||||
received_from = Message.objects.filter(receiver=profile).values_list('sender', flat=True)
|
||||
|
||||
chat_partner_ids = set(list(sent_to) + list(received_from))
|
||||
chat_partners = Profile.objects.filter(id__in=chat_partner_ids)
|
||||
|
||||
return render(request, "core/messages.html", {"chat_partners": chat_partners})
|
||||
|
||||
@login_required
|
||||
def chat_detail(request, pk):
|
||||
sender_profile = request.user.profile
|
||||
receiver_profile = get_object_or_404(Profile, pk=pk)
|
||||
|
||||
messages = Message.objects.filter(
|
||||
(Q(sender=sender_profile) & Q(receiver=receiver_profile)) |
|
||||
(Q(sender=receiver_profile) & Q(receiver=sender_profile))
|
||||
).order_by('timestamp')
|
||||
|
||||
# Mark messages as read
|
||||
Message.objects.filter(sender=receiver_profile, receiver=sender_profile, is_read=False).update(is_read=True)
|
||||
|
||||
if request.method == "POST":
|
||||
form = MessageForm(request.POST)
|
||||
if form.is_valid():
|
||||
message = form.save(commit=False)
|
||||
message.sender = sender_profile
|
||||
message.receiver = receiver_profile
|
||||
message.save()
|
||||
return redirect('chat_detail', pk=pk)
|
||||
else:
|
||||
form = MessageForm()
|
||||
|
||||
return render(request, "core/chat_detail.html", {
|
||||
"chat_partner": receiver_profile,
|
||||
"messages": messages,
|
||||
"form": form
|
||||
})
|
||||
|
||||
@ -1,4 +1,99 @@
|
||||
/* Custom styles for the application */
|
||||
body {
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
:root {
|
||||
--bg-dark: #0a0a0f;
|
||||
--surface-dark: #14141a;
|
||||
--surface-glass: rgba(20, 20, 26, 0.7);
|
||||
--accent-blue: #21d4fd;
|
||||
--accent-violet: #7b2ff7;
|
||||
--accent-gradient: linear-gradient(45deg, #7b2ff7, #21d4fd);
|
||||
--text-primary: #ffffff;
|
||||
--text-secondary: #b0b0b5;
|
||||
--border-glass: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--bg-dark);
|
||||
color: var(--text-primary);
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
margin: 0;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.glass-card {
|
||||
background: var(--surface-glass);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border: 1px solid var(--border-glass);
|
||||
border-radius: 24px;
|
||||
padding: 24px;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.glass-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: var(--accent-gradient);
|
||||
border: none;
|
||||
border-radius: 100px;
|
||||
color: white;
|
||||
font-weight: 700;
|
||||
padding: 12px 32px;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
opacity: 0.9;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.text-gradient {
|
||||
background: var(--accent-gradient);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
background-color: rgba(10, 10, 15, 0.8);
|
||||
backdrop-filter: blur(10px);
|
||||
border-bottom: 1px solid var(--border-glass);
|
||||
}
|
||||
|
||||
.hero-section {
|
||||
padding: 120px 0 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
font-size: 4rem;
|
||||
font-weight: 800;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.hero-subtitle {
|
||||
color: var(--text-secondary);
|
||||
font-size: 1.25rem;
|
||||
max-width: 600px;
|
||||
margin: 0 auto 40px;
|
||||
}
|
||||
|
||||
.feature-pill {
|
||||
background: var(--surface-dark);
|
||||
border: 1px solid var(--border-glass);
|
||||
border-radius: 100px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 8px 16px;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.feature-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: var(--accent-blue);
|
||||
border-radius: 50%;
|
||||
margin-right: 8px;
|
||||
box-shadow: 0 0 10px var(--accent-blue);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user