diff --git a/core/__pycache__/forms.cpython-311.pyc b/core/__pycache__/forms.cpython-311.pyc new file mode 100644 index 0000000..c967f7f Binary files /dev/null and b/core/__pycache__/forms.cpython-311.pyc differ diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index 18a063c..9529ab8 100644 Binary files a/core/__pycache__/models.cpython-311.pyc and b/core/__pycache__/models.cpython-311.pyc differ diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index ebb8c6e..9f3827a 100644 Binary files a/core/__pycache__/urls.cpython-311.pyc and b/core/__pycache__/urls.cpython-311.pyc differ diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 8d204fa..1e496a9 100644 Binary files a/core/__pycache__/views.cpython-311.pyc and b/core/__pycache__/views.cpython-311.pyc differ diff --git a/core/forms.py b/core/forms.py new file mode 100644 index 0000000..c10f469 --- /dev/null +++ b/core/forms.py @@ -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...'}), + } diff --git a/core/management/commands/__pycache__/seed_data.cpython-311.pyc b/core/management/commands/__pycache__/seed_data.cpython-311.pyc new file mode 100644 index 0000000..83014ca Binary files /dev/null and b/core/management/commands/__pycache__/seed_data.cpython-311.pyc differ diff --git a/core/management/commands/seed_data.py b/core/management/commands/seed_data.py new file mode 100644 index 0000000..c68b3c7 --- /dev/null +++ b/core/management/commands/seed_data.py @@ -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!')) diff --git a/core/migrations/0001_initial.py b/core/migrations/0001_initial.py new file mode 100644 index 0000000..68093f4 --- /dev/null +++ b/core/migrations/0001_initial.py @@ -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')), + ], + ), + ] diff --git a/core/migrations/__pycache__/0001_initial.cpython-311.pyc b/core/migrations/__pycache__/0001_initial.cpython-311.pyc new file mode 100644 index 0000000..468ae30 Binary files /dev/null and b/core/migrations/__pycache__/0001_initial.cpython-311.pyc differ diff --git a/core/models.py b/core/models.py index 71a8362..65a65c7 100644 --- a/core/models.py +++ b/core/models.py @@ -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}" \ No newline at end of file diff --git a/core/templates/base.html b/core/templates/base.html index 1e7e5fb..5ced4ff 100644 --- a/core/templates/base.html +++ b/core/templates/base.html @@ -1,9 +1,9 @@ - - {% block title %}Knowledge Base{% endblock %} + + {% block title %}Gatsby{% endblock %} {% if project_description %} @@ -14,12 +14,129 @@ {% endif %} {% load static %} + + + - {% block head %}{% endblock %} + + + + {% block extra_css %}{% endblock %} + - - {% block content %}{% endblock %} - + + +
+ {% block content %}{% endblock %} +
+ + + + + diff --git a/core/templates/core/chat_detail.html b/core/templates/core/chat_detail.html new file mode 100644 index 0000000..9dd385a --- /dev/null +++ b/core/templates/core/chat_detail.html @@ -0,0 +1,141 @@ +{% extends 'base.html' %} +{% load static %} + +{% block title %}Chat with {{ chat_partner.user.username }} - Gatsby{% endblock %} + +{% block extra_css %} + +{% endblock %} + +{% block content %} +
+
+
+ +
+ + + +
+ {{ chat_partner.user.username|first|upper }} +
+
+
{{ chat_partner.user.username }}
+

{{ chat_partner.university }} · {{ chat_partner.role|title }}

+
+
+ + +
+ {% for message in messages %} +
+ {{ message.content }} + + {% if message.sender == user.profile %}You{% else %}{{ message.sender.user.username }}{% endif %} · {{ message.timestamp|date:"H:i" }} + +
+ {% empty %} +
+

This is the beginning of your conversation with {{ chat_partner.user.username }}.

+
+ {% endfor %} +
+ + + +
+
+
+ + +{% endblock %} diff --git a/core/templates/core/create_startup.html b/core/templates/core/create_startup.html new file mode 100644 index 0000000..33cf6e3 --- /dev/null +++ b/core/templates/core/create_startup.html @@ -0,0 +1,33 @@ +{% extends 'base.html' %} +{% load static %} + +{% block title %}Post Startup - Gatsby{% endblock %} + +{% block content %} +
+
+
+

Post New Startup

+

Ready to build the future? Share your vision with the university ecosystem.

+ +
+ {% csrf_token %} + {% for field in form %} +
+ + {{ field }} + {% if field.errors %} +
{{ field.errors }}
+ {% endif %} +
+ {% endfor %} + +
+ + Cancel +
+
+
+
+
+{% endblock %} diff --git a/core/templates/core/discover.html b/core/templates/core/discover.html new file mode 100644 index 0000000..ebedc76 --- /dev/null +++ b/core/templates/core/discover.html @@ -0,0 +1,57 @@ +{% extends "base.html" %} + +{% block content %} +
+
+
+

Discover Peer-Led Startups

+

Browse through the latest student and graduate startups looking for investment and partners.

+
+ +
+ +
+ {% for startup in startups %} +
+
+
+
+ Fintech + 12 days left +
+

{{ startup.name }}

+

{{ startup.headline }}

+ +
+
+ £{{ startup.raised_amount|floatformat:0 }} raised + {{ startup.raised_amount|divisibleby:startup.target_amount|default:0 }}% of £{{ startup.target_amount|floatformat:0 }} +
+
+
+
+
+ + +
+
+
+ {% empty %} +
+
+
+ +
+

No startups found yet

+

Be the first to post your vision and start raising capital from your peers.

+ Post Your Startup +
+
+ {% endfor %} +
+
+{% endblock %} diff --git a/core/templates/core/index.html b/core/templates/core/index.html index faec813..cccd7b5 100644 --- a/core/templates/core/index.html +++ b/core/templates/core/index.html @@ -1,145 +1,93 @@ {% extends "base.html" %} - -{% block title %}{{ project_name }}{% endblock %} - -{% block head %} - - - - -{% endblock %} +{% load static %} {% block content %} -
-
-

Analyzing your requirements and generating your app…

-
- Loading… +
+
+
+
+ + Exclusive for Students & Grads +
+

Build and Fund the Future Together.

+

The only university networking and micro-investment platform where verified students and recent graduates support each other's startups.

+
-

AppWizzy AI is collecting your requirements and applying the first changes.

-

This page will refresh automatically as the plan is implemented.

-

- Runtime: Django {{ django_version }} · Python {{ python_version }} - — UTC {{ current_time|date:"Y-m-d H:i:s" }} -

-
- -{% endblock %} \ No newline at end of file + + +
+
+
+
+
+ +
+

Verify

+

Exclusively for students and graduates within the last 5 years. Use your university email to join the network.

+
+
+
+
+
+ +
+

Connect

+

Find the perfect co-founder or team member using our AI-driven matching questionnaire based on skills and vision.

+
+
+
+
+
+ +
+

Fund

+

Start raising capital from your peers or discover student-led startups to invest in, starting from as little as £50.

+
+
+
+
+ +
+
+

How it Works

+

Three simple steps to launch your vision or grow your portfolio.

+
+
+
+
+
+
+ 1 +
+
+
Create Your Verified Profile
+

Sign up with your .edu or university email. We verify your status as a current student or recent graduate instantly.

+
+
+
+
+ 2 +
+
+
Define Your Role
+

Choose to be a Founder looking for investment and partners, or an Investor looking to support peer-led innovation.

+
+
+
+
+ 3 +
+
+
Start Networking & Investing
+

Browse listings, connect with founders, or set up your startup profile and start your first funding round.

+
+
+
+
+
+
+{% endblock %} diff --git a/core/templates/core/invest.html b/core/templates/core/invest.html new file mode 100644 index 0000000..0a50b56 --- /dev/null +++ b/core/templates/core/invest.html @@ -0,0 +1,31 @@ +{% extends "base.html" %} + +{% block content %} +
+
+
+
+

Invest in {{ startup.name }}

+

Be part of the next big university startup. Minimum investment is £50.

+ +
+ {% csrf_token %} +
+ + +
Enter the amount you wish to invest in this project.
+
+ +
+ +
+
+ +
+

By clicking "Confirm Investment", you agree to our Terms of Use and acknowledge the risks involved.

+
+
+
+
+
+{% endblock %} diff --git a/core/templates/core/login.html b/core/templates/core/login.html new file mode 100644 index 0000000..e15ffe3 --- /dev/null +++ b/core/templates/core/login.html @@ -0,0 +1,38 @@ +{% extends "base.html" %} + +{% block content %} +
+
+
+
+

Welcome Back to GATSBY

+

Sign in to your account and explore the peer-to-peer startup ecosystem.

+ +
+ {% csrf_token %} + {% if form.non_field_errors %} +
{{ form.non_field_errors.0 }}
+ {% endif %} + +
+ + +
+
+ + +
+ +
+ +
+
+ +
+

Don't have an account? Sign Up

+
+
+
+
+
+{% endblock %} diff --git a/core/templates/core/messages.html b/core/templates/core/messages.html new file mode 100644 index 0000000..8560d26 --- /dev/null +++ b/core/templates/core/messages.html @@ -0,0 +1,41 @@ +{% extends 'base.html' %} +{% load static %} + +{% block title %}Messages - Gatsby{% endblock %} + +{% block content %} +
+
+

Your Chats

+ +
+
+ {% for partner in chat_partners %} + +
+
+ {{ partner.user.username|first|upper }} +
+
+
{{ partner.user.username }}
+

{{ partner.university }} · {{ partner.role|title }}

+
+
+ +
+
+
+ {% empty %} +
+
+ +
+
No messages yet
+

Start a conversation with a founder or partner from the Discover page.

+
+ {% endfor %} +
+
+
+
+{% endblock %} diff --git a/core/templates/core/onboarding/investor.html b/core/templates/core/onboarding/investor.html new file mode 100644 index 0000000..0e4b846 --- /dev/null +++ b/core/templates/core/onboarding/investor.html @@ -0,0 +1,32 @@ +{% extends "base.html" %} + +{% block content %} +
+
+
+
+

Complete Your Investor Profile

+

Sharing your background and goals helps us match you with the right startups.

+ +
+ {% csrf_token %} +
+ + +
+ +
+ + +
What's the total amount you're comfortable investing across the platform?
+
+ +
+ +
+
+
+
+
+
+{% endblock %} diff --git a/core/templates/core/onboarding/partner_matching.html b/core/templates/core/onboarding/partner_matching.html new file mode 100644 index 0000000..8e06f44 --- /dev/null +++ b/core/templates/core/onboarding/partner_matching.html @@ -0,0 +1,99 @@ +{% extends "base.html" %} + +{% block content %} +
+
+
+
+

Partner Matching Questionnaire

+

Tell us more about your vision to find the perfect co-founder or team members.

+ +
+ {% csrf_token %} +
+
1. Which area best describes your startup or project?
+ +
+ +
+
2. What role are you currently fulfilling?
+
+
+ +
+
+ +
+
+ +
+
+
+ +
+
3. What kind of partner are you looking for?
+
+
+ +
+
+ +
+
+ +
+
+
+ +
+
4. What's your primary goal for the next 3 months?
+ +
+ +
+ +
+
+
+
+
+
+ + +{% endblock %} diff --git a/core/templates/core/onboarding/role.html b/core/templates/core/onboarding/role.html new file mode 100644 index 0000000..784b7a7 --- /dev/null +++ b/core/templates/core/onboarding/role.html @@ -0,0 +1,59 @@ +{% extends "base.html" %} + +{% block content %} +
+
+
+
+

Choose Your Role

+

How do you plan to use Gatsby? You can change this later.

+ +
+ {% csrf_token %} +
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+
+ + +{% endblock %} diff --git a/core/templates/core/partners.html b/core/templates/core/partners.html new file mode 100644 index 0000000..28ed40b --- /dev/null +++ b/core/templates/core/partners.html @@ -0,0 +1,62 @@ +{% extends "base.html" %} + +{% block content %} +
+
+
+

Find Your Next Co-Founder

+

Discover and connect with talented student and graduate founders looking for partners.

+
+ +
+ +
+ {% for partner in partners %} +
+
+
+
+
+ +
+
+
{{ partner.user.username }}
+ {{ partner.university }} · '{{ partner.graduation_year }} +
+
+ +

{{ partner.bio|default:"No bio available yet. I'm excited to build something new!" }}

+ +
+
Primary Focus
+
+ Fintech + Tech +
+
+ + +
+
+
+ {% empty %} +
+
+
+ +
+

No partners found yet

+

Be the first to create your founder profile and find your perfect co-founder.

+ Create Profile +
+
+ {% endfor %} +
+
+{% endblock %} \ No newline at end of file diff --git a/core/templates/core/portfolio.html b/core/templates/core/portfolio.html new file mode 100644 index 0000000..d93f2e3 --- /dev/null +++ b/core/templates/core/portfolio.html @@ -0,0 +1,102 @@ +{% extends "base.html" %} + +{% block content %} +
+
+
+

Your Portfolio

+

Manage your investments and track your startup's progress.

+
+
+
+ Total Invested: + £{{ total_invested|floatformat:0 }} +
+
+
+ +
+ +
+
+

My Investments

+ + New Investment + +
+ + {% for investment in investments %} +
+
+
+
{{ investment.startup.name }}
+ {{ investment.timestamp|date:"d M Y" }} +
+
+ £{{ investment.amount|floatformat:0 }} +
Invested
+
+
+ £{{ investment.startup.raised_amount|floatformat:0 }} +
Total Raised
+
+ +
+
+ {% empty %} +
+
+ +
+

No investments found yet. Browse startups and start supporting your peers.

+ Explore Startups +
+ {% endfor %} +
+ + +
+
+

My Startups

+ {% if user.profile.role == 'FOUNDER' %} + + Post New Startup + + {% endif %} +
+ + {% for startup in my_startups %} +
+
+
+
{{ startup.name }}
+ {{ startup.status }} +
+
+ £{{ startup.raised_amount|floatformat:0 }} +
Raised
+
+
+ £{{ startup.target_amount|floatformat:0 }} +
Target
+
+
+ Manage +
+
+
+ {% empty %} +
+
+ +
+

You haven't posted any startups yet. Ready to build the future?

+ Post Your Startup +
+ {% endfor %} +
+
+
+{% endblock %} \ No newline at end of file diff --git a/core/templates/core/signup.html b/core/templates/core/signup.html new file mode 100644 index 0000000..2f198b8 --- /dev/null +++ b/core/templates/core/signup.html @@ -0,0 +1,38 @@ +{% extends "base.html" %} + +{% block content %} +
+
+
+
+

Create Your Profile

+

Join Gatsby and connect with the next generation of student-led startups. Only university or graduate emails from the last 5 years are accepted.

+ +
+ {% csrf_token %} + {% for field in form %} +
+ + + {% if field.help_text %} +
{{ field.help_text }}
+ {% endif %} + {% if field.errors %} +
{{ field.errors.0 }}
+ {% endif %} +
+ {% endfor %} + +
+ +
+
+ +
+

Already have an account? Sign In

+
+
+
+
+
+{% endblock %} diff --git a/core/templates/core/startup_detail.html b/core/templates/core/startup_detail.html new file mode 100644 index 0000000..71a4995 --- /dev/null +++ b/core/templates/core/startup_detail.html @@ -0,0 +1,84 @@ +{% extends "base.html" %} + +{% block content %} +
+
+
+
+
+ Fintech + 12 days left +
+

{{ startup.name }}

+

{{ startup.headline }}

+ +
+

About the Project

+

{{ startup.description }}

+
+ +
+

The Vision

+

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.

+
+ +
+
Founder
+
+
+ {{ startup.founder.user.username|first|upper }} +
+
+

{{ startup.founder.user.username }}

+

{{ startup.founder.university }} · Class of {{ startup.founder.graduation_year }}

+
+
+
+
+
+ +
+
+

Investment

+ +
+
+ £{{ startup.raised_amount|floatformat:0 }} raised + Target: £{{ startup.target_amount|floatformat:0 }} +
+
+ {% widthratio startup.raised_amount startup.target_amount 100 as percentage %} +
+
+
+ +
+
+ Minimum + £50 +
+
+ Backers + {{ startup.investments.count }} +
+
+ Valuation + £25k - £100k +
+
+ + + +
+

Join the network of alumni and students supporting this vision.

+
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/core/templates/core/verify.html b/core/templates/core/verify.html new file mode 100644 index 0000000..cb460a5 --- /dev/null +++ b/core/templates/core/verify.html @@ -0,0 +1,28 @@ +{% extends "base.html" %} + +{% block content %} +
+
+
+
+
+ +
+

Verify Your Email

+

We've sent a verification link to your university or graduate email address. Please click the link to confirm your status and access Gatsby.

+ +
+ {% csrf_token %} +
+ +
+
+ +
+

Didn't receive the email? Resend Link

+
+
+
+
+
+{% endblock %} diff --git a/core/urls.py b/core/urls.py index 6299e3d..e1c9b0e 100644 --- a/core/urls.py +++ b/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//', views.startup_detail, name='startup_detail'), + path('startup//invest/', views.invest, name='invest'), + path('startup/post/', views.post_startup, name='post_startup'), + path('messages/', views.messages_view, name='messages'), + path('chat//', views.chat_detail, name='chat_detail'), +] \ No newline at end of file diff --git a/core/views.py b/core/views.py index c9aed12..f90ff4c 100644 --- a/core/views.py +++ b/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 + }) diff --git a/static/css/custom.css b/static/css/custom.css index 925f6ed..2f5cb6e 100644 --- a/static/css/custom.css +++ b/static/css/custom.css @@ -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); +} \ No newline at end of file