feat: finished innovate tj basic loadout

This commit is contained in:
Rushil Umaretiya 2021-02-03 19:37:16 -05:00
parent 25af1684cc
commit 7fb60268df
No known key found for this signature in database
GPG Key ID: 4E8FAF9C926AF959
17 changed files with 646 additions and 15 deletions

View File

@ -48,6 +48,7 @@ INSTALLED_APPS = [
'whitenoise.runserver_nostatic',
'django.contrib.staticfiles',
'crispy_forms',
'django_secrets'
]
@ -75,6 +76,7 @@ TEMPLATES = [
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'django.template.context_processors.media',
],
},
},

View File

@ -13,14 +13,20 @@ Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import path, include
from users import views as user_views
from innovate.admin import admin_site as innovate_admin
urlpatterns = [
path('', include('launchx.urls')),
path('innovate/', include('innovate.urls'), name='innovate'),
path('login/', user_views.login, name='login'),
path('logout/', user_views.login, name='logout'),
path('admin/', admin.site.urls),
path('admin/', innovate_admin.urls),
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View File

@ -1,3 +1,18 @@
from django.contrib import admin
from .models import Team, Competitor
from django.contrib.auth.models import User, Group
# Register your models here.
class LaunchXAdminSite(admin.AdminSite):
site_header = "LaunchX Admin"
site_title = "LaunchX Admin Portal"
index_title = "Welcome to LaunchX Admin Page"
admin_site = LaunchXAdminSite(name='launchx-admin')
admin_site.register(User)
admin_site.register(Group)
admin_site.register(Competitor)
admin_site.register(Team)

41
innovate/forms.py Normal file
View File

@ -0,0 +1,41 @@
from django import forms
from django.forms import modelformset_factory
from .models import Competitor, Team
class CompetitorForm(forms.ModelForm):
name = forms.CharField(label='Full Name', widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'John Doe'}))
email = forms.EmailField(label='Email', widget=forms.EmailInput(attrs={'class': 'form-control', 'placeholder': 'example@email.com'}))
class Meta:
model = Competitor
fields = ['name', 'email']
def __init__(self, *args, **kwargs):
super(CompetitorForm, self).__init__(*args, **kwargs)
self.label_suffix = ''
CompetitorFormset = modelformset_factory(
Competitor,
form=CompetitorForm,
min_num=2, max_num=4, extra=0)
class TeamForm(forms.ModelForm):
name = forms.CharField(required=False, label="Team Name (Optional)", widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'BusinessX'}))
reciept = forms.FileField(required=False)
class Meta:
model = Team
fields = ['name', 'reciept']
def __init__(self, *args, **kwargs):
super(TeamForm, self).__init__(*args, **kwargs)
self.label_suffix = ''
def save(self, commit=True):
m = super(TeamForm, self).save(commit=False)
m.number = Team.objects.all().count() + 1
if commit:
m.save()
return m

View File

@ -0,0 +1,42 @@
# Generated by Django 3.1.6 on 2021-02-03 17:45
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Team',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('number', models.IntegerField()),
('name', models.CharField(blank=True, max_length=20, null=True)),
('reciept', models.FileField(blank=True, null=True, upload_to='reciepts/')),
],
options={
'verbose_name': 'Team',
'verbose_name_plural': 'Teams',
},
),
migrations.CreateModel(
name='Competitor',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(blank=True, default='', max_length=20)),
('email', models.EmailField(max_length=254)),
('is_leader', models.BooleanField(default=False)),
('team', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='innovate.team')),
],
options={
'verbose_name': 'Competitor',
'verbose_name_plural': 'Competitors',
},
),
]

View File

@ -1,3 +1,43 @@
from django.db import models
from django.core.exceptions import ValidationError
# Create your models here.
class Team(models.Model):
number = models.IntegerField()
name = models.CharField(max_length=20, null=True, blank=True)
reciept = models.FileField(upload_to='reciepts/', null=True, blank=True)
class Meta:
verbose_name = "Team"
verbose_name_plural = "Teams"
def __str__(self):
return self.name
def clean(self):
# Don't allow teams to have the same name.
if Team.objects.filter(name=self.name).count() > 0:
raise ValidationError({'name': 'That name is already taken! Sorry.'})
class Competitor(models.Model):
name = models.CharField(max_length=20, blank=True, default='')
email = models.EmailField(max_length = 254)
is_leader = models.BooleanField(default=False)
team = models.ForeignKey(Team, on_delete=models.CASCADE)
class Meta:
verbose_name = "Competitor"
verbose_name_plural = "Competitors"
def __str__(self):
return self.name
def clean(self):
# Don't allow teams to have the same name.
if Competitor.objects.filter(name=self.name).count() > 0:
raise ValidationError({'name': 'Somebody with that name is already registered!'})
if Competitor.objects.filter(email=self.email).count() > 0:
raise ValidationError({'email': 'Somebody with that email is already registered!'})

View File

@ -0,0 +1,250 @@
/**
* jQuery Formset 1.5-pre
* @author Stanislaus Madueke (stan DOT madueke AT gmail DOT com)
* @requires jQuery 1.2.6 or later
*
* Copyright (c) 2009, Stanislaus Madueke
* All rights reserved.
*
* Licensed under the New BSD License
* See: http://www.opensource.org/licenses/bsd-license.php
*/
;(function($) {
$.fn.formset = function(opts)
{
var options = $.extend({}, $.fn.formset.defaults, opts),
flatExtraClasses = options.extraClasses.join(' '),
totalForms = $('#id_' + options.prefix + '-TOTAL_FORMS'),
maxForms = $('#id_' + options.prefix + '-MAX_NUM_FORMS'),
minForms = $('#id_' + options.prefix + '-MIN_NUM_FORMS'),
childElementSelector = 'input,select,textarea,label,div',
$$ = $(this),
applyExtraClasses = function(row, ndx) {
if (options.extraClasses) {
row.removeClass(flatExtraClasses);
row.addClass(options.extraClasses[ndx % options.extraClasses.length]);
}
},
updateElementIndex = function(elem, prefix, ndx) {
var idRegex = new RegExp(prefix + '-(\\d+|__prefix__)-'),
replacement = prefix + '-' + ndx + '-';
if (elem.attr("for")) elem.attr("for", elem.attr("for").replace(idRegex, replacement));
if (elem.attr('id')) elem.attr('id', elem.attr('id').replace(idRegex, replacement));
if (elem.attr('name')) elem.attr('name', elem.attr('name').replace(idRegex, replacement));
},
hasChildElements = function(row) {
return row.find(childElementSelector).length > 0;
},
showAddButton = function() {
return maxForms.length == 0 || // For Django versions pre 1.2
(maxForms.val() == '' || (maxForms.val() - totalForms.val() > 0));
},
/**
* Indicates whether delete link(s) can be displayed - when total forms > min forms
*/
showDeleteLinks = function() {
return minForms.length == 0 || // For Django versions pre 1.7
(minForms.val() == '' || (totalForms.val() - minForms.val() > 0));
},
insertDeleteLink = function(row) {
var delCssSelector = $.trim(options.deleteCssClass).replace(/\s+/g, '.'),
addCssSelector = $.trim(options.addCssClass).replace(/\s+/g, '.');
var delButtonHTML = '<a class="' + options.deleteCssClass + '" href="javascript:void(0)">' + options.deleteText +'</a>';
if (options.deleteContainerClass) {
// If we have a specific container for the remove button,
// place it as the last child of that container:
row.find('[class*="' + options.deleteContainerClass + '"]').append(delButtonHTML);
} else if (row.is('TR')) {
// If the forms are laid out in table rows, insert
// the remove button into the last table cell:
row.children(':last').append(delButtonHTML);
} else if (row.is('UL') || row.is('OL')) {
// If they're laid out as an ordered/unordered list,
// insert an <li> after the last list item:
row.append('<li>' + delButtonHTML + '</li>');
} else {
// Otherwise, just insert the remove button as the
// last child element of the form's container:
row.append(delButtonHTML);
}
// Check if we're under the minimum number of forms - not to display delete link at rendering
if (!showDeleteLinks()){
row.find('a.' + delCssSelector).hide();
}
row.find('a.' + delCssSelector).click(function() {
var row = $(this).parents('.' + options.formCssClass),
del = row.find('input:hidden[id $= "-DELETE"]'),
buttonRow = row.siblings("a." + addCssSelector + ', .' + options.formCssClass + '-add'),
forms;
if (del.length) {
// We're dealing with an inline formset.
// Rather than remove this form from the DOM, we'll mark it as deleted
// and hide it, then let Django handle the deleting:
del.val('on');
row.hide();
forms = $('.' + options.formCssClass).not(':hidden');
totalForms.val(forms.length);
} else {
row.remove();
// Update the TOTAL_FORMS count:
forms = $('.' + options.formCssClass).not('.formset-custom-template');
totalForms.val(forms.length);
}
for (var i=0, formCount=forms.length; i<formCount; i++) {
// Apply `extraClasses` to form rows so they're nicely alternating:
applyExtraClasses(forms.eq(i), i);
if (!del.length) {
// Also update names and IDs for all child controls (if this isn't
// a delete-able inline formset) so they remain in sequence:
forms.eq(i).find(childElementSelector).each(function() {
updateElementIndex($(this), options.prefix, i);
});
}
}
// Check if we've reached the minimum number of forms - hide all delete link(s)
if (!showDeleteLinks()){
$('a.' + delCssSelector).each(function(){$(this).hide();});
}
// Check if we need to show the add button:
if (buttonRow.is(':hidden') && showAddButton()) buttonRow.show();
// If a post-delete callback was provided, call it with the deleted form:
if (options.removed) options.removed(row);
return false;
});
};
$$.each(function(i) {
var row = $(this),
del = row.find('input:checkbox[id $= "-DELETE"]');
if (del.length) {
// If you specify "can_delete = True" when creating an inline formset,
// Django adds a checkbox to each form in the formset.
// Replace the default checkbox with a hidden field:
if (del.is(':checked')) {
// If an inline formset containing deleted forms fails validation, make sure
// we keep the forms hidden (thanks for the bug report and suggested fix Mike)
del.before('<input type="hidden" name="' + del.attr('name') +'" id="' + del.attr('id') +'" value="on" />');
row.hide();
} else {
del.before('<input type="hidden" name="' + del.attr('name') +'" id="' + del.attr('id') +'" />');
}
// Hide any labels associated with the DELETE checkbox:
$('label[for="' + del.attr('id') + '"]').hide();
del.remove();
}
if (hasChildElements(row)) {
row.addClass(options.formCssClass);
if (row.is(':visible')) {
insertDeleteLink(row);
applyExtraClasses(row, i);
}
}
});
if ($$.length) {
var hideAddButton = !showAddButton(),
addButton, template;
if (options.formTemplate) {
// If a form template was specified, we'll clone it to generate new form instances:
template = (options.formTemplate instanceof $) ? options.formTemplate : $(options.formTemplate);
template.removeAttr('id').addClass(options.formCssClass + ' formset-custom-template');
template.find(childElementSelector).each(function() {
updateElementIndex($(this), options.prefix, '__prefix__');
});
insertDeleteLink(template);
} else {
// Otherwise, use the last form in the formset; this works much better if you've got
// extra (>= 1) forms (thnaks to justhamade for pointing this out):
if (options.hideLastAddForm) $('.' + options.formCssClass + ':last').hide();
template = $('.' + options.formCssClass + ':last').clone(true).removeAttr('id');
template.find('input:hidden[id $= "-DELETE"]').remove();
// Clear all cloned fields, except those the user wants to keep (thanks to brunogola for the suggestion):
template.find(childElementSelector).not(options.keepFieldValues).each(function() {
var elem = $(this);
// If this is a checkbox or radiobutton, uncheck it.
// This fixes Issue 1, reported by Wilson.Andrew.J:
if (elem.is('input:checkbox') || elem.is('input:radio')) {
elem.attr('checked', false);
} else {
elem.val('');
}
});
}
// FIXME: Perhaps using $.data would be a better idea?
options.formTemplate = template;
var addButtonHTML = '<a class="' + options.addCssClass + '" href="javascript:void(0)">' + options.addText + '</a>';
if (options.addContainerClass) {
// If we have a specific container for the "add" button,
// place it as the last child of that container:
var addContainer = $('[class*="' + options.addContainerClass + '"');
addContainer.append(addButtonHTML);
addButton = addContainer.find('[class="' + options.addCssClass + '"]');
} else if ($$.is('TR')) {
// If forms are laid out as table rows, insert the
// "add" button in a new table row:
var numCols = $$.eq(0).children().length, // This is a bit of an assumption :|
buttonRow = $('<tr><td colspan="' + numCols + '">' + addButtonHTML + '</tr>').addClass(options.formCssClass + '-add');
$$.parent().append(buttonRow);
addButton = buttonRow.find('a');
} else {
// Otherwise, insert it immediately after the last form:
$$.filter(':last').after(addButtonHTML);
addButton = $$.filter(':last').next();
}
if (hideAddButton) addButton.hide();
addButton.click(function() {
var formCount = parseInt(totalForms.val()),
row = options.formTemplate.clone(true).removeClass('formset-custom-template'),
buttonRow = $($(this).parents('tr.' + options.formCssClass + '-add').get(0) || this),
delCssSelector = $.trim(options.deleteCssClass).replace(/\s+/g, '.');
applyExtraClasses(row, formCount);
row.insertBefore(buttonRow).show();
row.find(childElementSelector).each(function() {
updateElementIndex($(this), options.prefix, formCount);
});
totalForms.val(formCount + 1);
// Check if we're above the minimum allowed number of forms -> show all delete link(s)
if (showDeleteLinks()){
$('a.' + delCssSelector).each(function(){$(this).show();});
}
// Check if we've exceeded the maximum allowed number of forms:
if (!showAddButton()) buttonRow.hide();
// If a post-add callback was supplied, call it with the added form:
if (options.added) options.added(row);
return false;
});
}
return $$;
};
/* Setup plugin defaults */
$.fn.formset.defaults = {
prefix: 'form', // The form prefix for your django formset
formTemplate: null, // The jQuery selection cloned to generate new form instances
addText: 'Add Another', // Text for the add link
deleteText: 'Remove', // Text for the delete link
addContainerClass: null, // Container CSS class for the add link
deleteContainerClass: null, // Container CSS class for the delete link
addCssClass: 'add-row btn btn-outline-light mt-2', // CSS class applied to the add link
deleteCssClass: 'delete-row btn btn-outline-danger mt-2', // CSS class applied to the delete link
formCssClass: 'dynamic-form', // CSS class applied to each form in a formset
extraClasses: [], // Additional CSS classes, which will be applied to each form in turn
keepFieldValues: '', // jQuery selector for fields whose values should be kept when the form is cloned
added: null, // Function called each time a new form is added
removed: null, // Function called each time a form is deleted
hideLastAddForm: false // When set to true, hide last empty add form (becomes visible when clicking on add button)
};
})(jQuery);

View File

@ -0,0 +1,42 @@
.header {
text-align: center;
}
.header h1 {
font-size: 5em;
line-height: 0.8em;
}
.header h1 small {
font-size: 0.6em;
}
.header h3 {
font-size: 1.6em;
color: #ececec;
text-decoration: underline;
}
.basic-info ul {
list-style: none;
font-size: 2em;
}
li {
font-size: 1em;
font-weight: bold;
}
li:before {
content: '- ';
}
.list-text {
font-size: 0.7em;
font-weight: normal;
}
.sign-up {
margin: auto auto;
text-align: center;
}

View File

@ -0,0 +1,56 @@
.row1 .header:after {
content: '1';
}
.row2 .header:after {
content: '2';
}
.row3 .header:after {
content: '3';
}
.row4 .header:after {
content: '4';
}
fieldset {
border-width: 15px;
padding: 20px;
}
legend {
padding-left: 20px;
}
.form-floating {
color: black;
}
.form-floating label {
margin-left: 20px;
}
.add-row {
text-decoration: none;
color: white;
display: block;
}
button {
display: block;
margin: 0 auto;
}
.reciept .form-floating input {
color: white;
}
#payment-instructions {
font-size: 0.9em;
text-decoration: none;
}
#payment-instructions:hover {
text-decoration: underline;
}

View File

@ -0,0 +1,13 @@
{% extends 'launchx/base.html' %}
{% load static %}
{% block styles %}
<link rel="stylesheet" href="{% static 'innovate/confirm.css' %}">
{% endblock %}
{% block content %}
<main class="signup">
<h1>Congrats!</h1>
<h3>You've successfully signed up for InnovateTJ, please check your email for a confirmation. We'll see you </h3>
</main>
{% endblock %}

View File

@ -1,4 +1,28 @@
{% extends "launchx/base.html" %}
{% block content %}
<h1 style='color: white;'>InnovateTJ!</h1>
{% load static %}
{% block styles %}
<link rel="stylesheet" href="{% static 'innovate/index.css' %}">
{% endblock %}
{% block content %}
<header class="header">
<h1><small>It's time to</small><br>InnovateTJ!</h1>
<h3>TJ's very own entrpreneurial pitch and workshop event!</h3>
</header>
<section class="basic-info row mt-sm-5">
<div class="col-sm-8">
<ul>
<li>Who: <span class="list-text">Teams of 2-4 high school students</span></li>
<li>What: <span class="list-text">A collaborative event where you and other innovators will develop a business pitch for real entrepreneurs with the chance of winning real money and other prizes!</span></li>
<li>When: <span class="list-text">Feb 20th, 2021 @ 9AM - 5PM</span></li>
<li>Where: <span class="list-text">Online</span></li>
<li>How: <span class="list-text">$20 per team </span></li>
</ul>
</div>
<div class="col-sm-4 sign-up">
<h1>Sign up </h>
<a href="{% url 'competitor-signup' %}" class="btn btn-light signup">Here</a>
</div>
</section>
{% endblock %}

View File

@ -0,0 +1,70 @@
{% extends 'launchx/base.html' %}
{% load static %}
{% block styles %}
<link rel="stylesheet" href="{% static 'innovate/signup.css' %}">
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script src="{% static 'innovate/formsets.js' %}"></script>
<script type="text/javascript">
$(function() {
$('form .competitor-container').formset({
extraClasses: ['row1', 'row2', 'row3', 'row4']
})
})
</script>
{% endblock %}
{% load crispy_forms_tags %}
{% block content %}
<form class="form col-sm-6 offset-sm-3" method="POST" action="" enctype="multipart/form-data">
{% csrf_token %}
{{ formset.management_form|crispy }}
{% for form in formset %}
{{ form.non_field_errors }}
{{ form.errors }}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
<fieldset class="competitor-container container col">
<legend class="header row">
Competitor&nbsp;
</legend>
<div class="row">
<div class="form-floating col-5">
{{ form.name }}
{{ form.name.label_tag }}
</div>
<div class="form-floating col-5">
{{ form.email }}
{{ form.email.label_tag }}
</div>
</div>
</fieldset>
{% endfor %}
{% for hidden in team_form.hidden_fields %}
{{ hidden }}
{% endfor %}
{{ team_form.non_field_errors }}
{{ team_form.errors }}
<fieldset class="container col">
<legend class="team-header row">
Team Info
</legend>
<div class="form-floating row">
{{ team_form.name }}
{{ team_form.name.label_tag }}
</div>
</fieldset>
<fieldset class="container col reciept">
<legend class="team-header row">
Team Payment (Optional)
<a id="payment-instructions" href="#">How do I pay?</a>
</legend>
<div class="form-floating row">
{{ team_form.reciept }}
</div>
</fieldset>
<button class="mt-3 btn btn-light" type="submit">Sign Up!</button>
{% endblock %}

View File

@ -2,5 +2,7 @@ from django.urls import path
from . import views
urlpatterns = [
path('', views.home, name='innovate-home')
path('', views.home, name='innovate-home'),
path('signup/', views.signup, name='competitor-signup'),
path('signup/confirm/', views.confirm, name='competitor-signup-confirm'),
]

View File

@ -1,5 +1,31 @@
from django.shortcuts import render
from django.shortcuts import render, redirect
from .forms import CompetitorFormset, CompetitorForm, TeamForm
from .models import Competitor
# Create your views here.
def home(request):
return render(request, 'innovate/index.html')
def signup(request):
if request.method == 'POST':
formset = CompetitorFormset(request.POST)
team_form = TeamForm(request.POST, request.FILES)
if formset.is_valid() and team_form.is_valid():
team = team_form.save()
is_leader = True
for form in formset:
name = form.cleaned_data.get('name')
email = form.cleaned_data.get('email')
if name and email:
m = Competitor(name=name, email=email, is_leader=is_leader, team=team)
m.save()
is_leader = False
return redirect('landing')
formset = CompetitorFormset()
team_form = TeamForm()
return render(request, 'innovate/signup.html', {'formset': formset, 'team_form': team_form})
def confirm(request):
return render(request, 'innovate/confirm.html')

View File

@ -8,4 +8,5 @@
body {
background-color: #08060e;
color: #ffffff;
}

View File

@ -8,7 +8,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/>
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/5.0.0-alpha2/css/bootstrap.min.css" integrity="sha384-DhY6onE6f3zzKbjUPRc2hOzGAdEf4/Dz+WJwBvEYL/lkkIsI3ihufq9hk9K4lVoK" crossorigin="anonymous">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="{% static 'launchx/styles.css' %}">
{% block styles %}{% endblock %}
@ -28,15 +28,15 @@
</button>
<div class="collapse navbar-collapse" id="navbarToggle">
<div class="navbar-nav mr-auto">
<a class="nav-item nav-link" href="{% url 'landing' %}">Home</a>
<a class="nav-item nav-link" href="{% url 'calendar' %}">Calendar</a>
<a class="nav-item nav-link" href="{% url 'officers' %}">Officers</a>
<a class="nav-item nav-link" href="{% url 'innovate-home' %}">InnovateTJ</a>
<a class="nav-item nav-link {% if request.resolver_match.url_name == 'landing' %}active{% endif %}" href="{% url 'landing' %}">Home</a>
<a class="nav-item nav-link {% if request.resolver_match.url_name == 'innovate-home' %}active{% endif %}" href="{% url 'innovate-home' %}">InnovateTJ</a>
<a class="nav-item nav-link {% if request.resolver_match.url_name == 'calendar' %}active{% endif %}" href="{% url 'calendar' %}">Calendar</a>
<a class="nav-item nav-link {% if request.resolver_match.url_name == 'officers' %}active{% endif %}" href="{% url 'officers' %}">Officers</a>
</div>
<!-- Navbar Right Side -->
<div class="navbar-nav">
{% if user.is_authenticated %}
<!-- authenticated links -->
<a class="nav-item nav-link" href="{% url 'logout' %}">Logout</a>
{% else %}
<a class="nav-item nav-link" href="{% url 'login' %}">Login</a>
{% endif %}
@ -45,12 +45,13 @@
</div>
</nav>
</header>
{% block scripts %}{% endblock %}
<main role="main" class="container">
{% block content %}{% endblock %}
</main>
<!-- Bootstrap JS -->
<script src="https://stackpath.bootstrapcdn.com/bootstrap/5.0.0-alpha2/js/bootstrap.min.js" integrity="sha384-5h4UG+6GOuV9qXh6HqOLwZMY4mnLPraeTrjT5v07o347pj6IkfuoASuGBhfDsp3d" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" crossorigin="anonymous"></script>
</body>
</html>