Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement basic homepage #33

Merged
merged 3 commits into from
Apr 30, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ We spend all our hipster tech tokens for this project to help us build a front-e
- [css-scope-inline](https://github.com/gnat/css-scope-inline)
- [surreal](https://github.com/gnat/surreal?tab=readme-ov-file)

Having never used any of these toys before, we'll see how this pans out.
Having never used any of these toys before, we'll see how this pans out. (Update: so far, I sorta like HTMX but don't love how I've structured everything with the other two. And I hate Django templates as much as I remember.)

(Others under consideration include [django-slippers](https://github.com/mixxorz/slippers), [django-template-partials](https://github.com/carltongibson/django-template-partials), and [django-components](https://github.com/EmilStenstrom/django-components). And don't forget the [django-htmx-patterns](https://github.com/spookylukey/django-htmx-patterns/) documentation.)

26 changes: 13 additions & 13 deletions server/static/img/voter_bowl_logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 11 additions & 1 deletion server/vb/models.py
Original file line number Diff line number Diff line change
@@ -88,6 +88,16 @@ def __str__(self):
"""Return the school model's string representation."""
return f"School: {self.name}"

@property
def relative_url(self) -> str:
"""Return the relative URL for the school."""
return reverse("vb:school", args=[self.slug])

@property
def absolute_url(self) -> str:
"""Return the absolute URL for the school."""
return f"{settings.BASE_URL}{self.relative_url}"


class Logo(models.Model):
"""A single logo for a school."""
@@ -163,7 +173,7 @@ def current(self, when: datetime.datetime | None = None) -> "Contest | None":
class Contest(models.Model):
"""A single contest in the competition."""

objects = ContestManager()
objects: ContestManager = ContestManager()

# For now, we assume that each contest is associated with a single school.
school = models.ForeignKey(
139 changes: 139 additions & 0 deletions server/vb/templates/components/ongoing_contest.dhtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
<div data-logo-bg-color="{{ contest.school.logo.bg_color }}">
<style>
me {
border: 3px solid black;
color: black;
font-weight: 400;
font-size: 18px;
line-height: 140%;
padding-left: 1em;
padding-right: 1em;
position: relative;
}

me .content {
display: flex;
flex-direction: column;
}

me .logo {
--logo-bg-color: transparent;
border-radius: 100%;
border: 2px solid black;
background-color: var(--logo-bg-color);
overflow: hidden;
width: 60px;
height: 60px;
margin: 1.5em auto 1em auto;
}

me .logo img {
width: 100%;
height: 100%;
object-fit: contain;
}

me .school {
margin: 0;
font-weight: 500;
font-size: 24px;
line-height: 100%;
display: flex;
justify-content: center;
}

me .description {
margin-bottom: 0;
}

me .button-holder {
width: 100%;
}

me .button-holder a {
width: 100%;
}

/* A centered box at the top of the card */
me .box {
position: absolute;
top: -1em;
left: 50%;
transform: translateX(-50%);
border: 3px solid black;
background-color: #cdff64;
font-weight: 600;
line-height: 100%;
letter-spacing: 4%;
min-width: 30%;
padding: 0.25rem;
text-transform: uppercase;
}
</style>
<script>
(function(self) {
onloadAdd(() => {
const logo = self.querySelector(".logo");
logo.style.setProperty("--logo-bg-color", self.dataset.logoBgColor);
});
})(me());
</script>
<div class="content">
<div class="logo">
<img src="{{ contest.school.logo.url }}"
alt="{{ school.short_name }} {{ school.mascot }} logo" />
</div>
<p class="school">{{ contest.school.name }}</p>
<p class="description">
Check your voter registration status
{% if not contest.is_giveaway %}for a 1 in {{ contest.in_n }} chance{% endif %}
to win a ${{ contest.amount }} Amazon gift card.
</p>
<div class="button-holder">
{% include "components/button.dhtml" with text="Visit event" href=contest.school.relative_url bg_color="black" color="white" %}
</div>
</div>
<div class="box" data-end-at="{{ contest.end_at|date:'c' }}">
<script>
(function(self) {
function countdown(self) {
// compute the deadline
const deadline = new Date(self.dataset.endAt);
const deadlineTime = deadline.getTime();

/** Update the countdown. */
function updateCountdown() {
const now = new Date().getTime();
const diff = deadlineTime - now;

if (diff <= 0) {
clearInterval(interval);
self.innerText = "Just ended!";
return;
}

const hours = Math.floor(diff / (1000 * 60 * 60));
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((diff % (1000 * 60)) / 1000);

const h0digit = Math.floor(hours / 10);
const h1digit = hours % 10;
const m0digit = Math.floor(minutes / 10);
const m1digit = minutes % 10;
const s0digit = Math.floor(seconds / 10);
const s1digit = seconds % 10;

const endsIn = `Ends in ${h0digit}${h1digit}:${m0digit}${m1digit}:${s0digit}${s1digit}`;
self.innerText = endsIn;
}

updateCountdown();
const interval = setInterval(updateCountdown, 1000);
}

onloadAdd(() => countdown(self));
})(me());
</script>
Ends in ...
</div>
</div>
54 changes: 54 additions & 0 deletions server/vb/templates/components/upcoming_contest.dhtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<div data-logo-bg-color="{{ contest.school.logo.bg_color }}">
<style>
me {
border: 3px solid black;
padding: 1rem;
color: black;
font-size: 18px;
font-weight: 440;
font-variation-settings: "wght" 440;
line-height: 1;
}

me .content {
display: flex;
align-items: center;
gap: 1em;
}

me .logo {
--logo-bg-color: transparent;
border-radius: 100%;
border: 2px solid black;
background-color: var(--logo-bg-color);
overflow: hidden;
width: 36px;
height: 36px;
}

me .logo img {
width: 100%;
height: 100%;
object-fit: contain;
}

me p {
margin: 0;
}
</style>
<script>
(function(self) {
onloadAdd(() => {
const logo = self.querySelector(".logo");
logo.style.setProperty("--logo-bg-color", self.dataset.logoBgColor);
});
})(me());
</script>
<div class="content">
<div class="logo">
<img src="{{ contest.school.logo.url }}"
alt="{{ school.short_name }} {{ school.mascot }} logo" />
</div>
<p class="school">{{ contest.school.name }}</p>
</div>
</div>
121 changes: 118 additions & 3 deletions server/vb/templates/home.dhtml
Original file line number Diff line number Diff line change
@@ -1,16 +1,131 @@
{% extends "base.dhtml" %}
{% load static %}

{% block title %}
Voter Bowl
{% endblock title %}

{% block body %}
<style>
body {
background-color: #cdff64;
}
</style>
<div>
<style>
me h1 {
font-weight: 200;
me {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
background-color: #cdff64;
color: black;
}

me main {
width: 100%;
text-align: center;
padding: 2rem 0;
}

me main svg {
width: 104px;
margin: 1.5rem 0;
}

@media screen and (min-width: 768px) {
me main svg {
width: 112px;
}
}

me main p {
font-weight: 378;
font-size: 20px;
line-height: 130%;
}

me main h2 {
font-weight: 500;
font-size: 28px;
line-height: 140%;
}

@media screen and (min-width: 768px) {
me main h2 {
font-size: 32px;
}
}

me .faq {
width: 100%;
color: white;
padding: 2rem 0;
}

me .button-holder {
display: flex;
justify-content: center;
margin: 1.5rem 0;
}

me .faq {
background-color: black;
}

me .ongoing {
display: flex;
flex-direction: column;
justify-content: center;
gap: 2rem;
margin: 2rem 0;
}

me .upcoming {
display: flex;
flex-direction: column;
justify-content: center;
gap: 0.5rem;
margin: 0.5rem 0;
}

me .coming-soon {
text-transform: uppercase;
font-weight: bold;
font-size: 20px;
line-height: 130%;
display: flex;
justify-content: center;
margin: 1.5rem 0;
}
</style>
<h1>Voter Bowl</h1>
<main>
<div class="container">
<div class="center">{% include "voter_bowl_logo.svg" %}</div>
<h2>College students win prizes by checking if they are registered to vote.</h2>
{% if ongoing_contests %}
<div class="ongoing">
{% for contest in ongoing_contests %}
{% include "components/ongoing_contest.dhtml" with contest=contest %}
{% endfor %}
</div>
{% endif %}
{% if upcoming_contests %}
<p class="coming-soon">Coming Soon</p>
<div class="upcoming">
{% for contest in upcoming_contests %}
{% include "components/upcoming_contest.dhtml" with contest=contest %}
{% endfor %}
</div>
{% endif %}
{% if not ongoing_contests and not upcoming_contests %}
<p>There are no contests at this time. Check back later!</p>
{% endif %}
</div>
</main>
<div class="faq">
<div class="container">{% include "includes/faq.dhtml" %}</div>
</div>
{% include "includes/footer.dhtml" %}
</div>
{% endblock body %}
Loading