Some tweaks

This commit is contained in:
Ben Visness 2022-08-06 20:21:12 -05:00
parent 97ed892ce3
commit 48490d83a9
8 changed files with 244 additions and 200 deletions

View File

@ -28,7 +28,7 @@ var WRJ2021 = Jam{
var WRJ2022 = Jam{ var WRJ2022 = Jam{
Name: "Wheel Reinvention Jam 2022", Name: "Wheel Reinvention Jam 2022",
Slug: "WRJ2022", Slug: "WRJ2022",
StartTime: time.Date(2022, 8, 3, 8, 0, 0, 0, utils.Must1(time.LoadLocation("America/Los_Angeles"))), StartTime: time.Date(2022, 8, 15, 8, 0, 0, 0, utils.Must1(time.LoadLocation("America/Los_Angeles"))),
EndTime: time.Date(2022, 8, 22, 8, 0, 0, 0, utils.Must1(time.LoadLocation("America/Los_Angeles"))), EndTime: time.Date(2022, 8, 22, 8, 0, 0, 0, utils.Must1(time.LoadLocation("America/Los_Angeles"))),
} }

View File

@ -146,7 +146,20 @@
<img class="h3" src="{{ static "wheeljam2022/logo.svg" }}"> <img class="h3" src="{{ static "wheeljam2022/logo.svg" }}">
<div id="jam-title-container" class="flex flex-column pl3-m pl4-l pv3 pv0-ns"> <div id="jam-title-container" class="flex flex-column pl3-m pl4-l pv3 pv0-ns">
<h3 id="jam-title">Wheel Reinvention Jam</h3> <h3 id="jam-title">Wheel Reinvention Jam</h3>
<div id="jam-details">August 15 - 21. Change the status quo.</div> <div id="jam-details">
August 15 - 21.
{{ if gt .JamDaysUntilEnd 0 }}
{{ if eq .JamDaysUntilStart 0 }}
<b>Happening now.</b>
{{ else if eq .JamDaysUntilStart 1 }}
<b>Starting tomorrow.</b>
{{ else }}
<b>In {{ .JamDaysUntilStart }} days.</b>
{{ end }}
{{ else }}
<b>See the results.</b>
{{ end }}
</div>
</div> </div>
<div class="flex-grow-1"></div> <div class="flex-grow-1"></div>
<div id="jam-learn-more"> <div id="jam-learn-more">

View File

@ -27,7 +27,6 @@
<meta name="theme-color" content="#346ba6"> <meta name="theme-color" content="#346ba6">
<script src="{{ static "js/templates.js" }}"></script> <script src="{{ static "js/templates.js" }}"></script>
<script src="{{ static "js/showcase.js" }}"></script>
<link rel="stylesheet" href="{{ static "fonts/mohave/stylesheet.css" }}"> <link rel="stylesheet" href="{{ static "fonts/mohave/stylesheet.css" }}">
<link href='https://fonts.googleapis.com/css?family=Fira+Sans:300,400,500,600' rel='stylesheet' type='text/css'> <link href='https://fonts.googleapis.com/css?family=Fira+Sans:300,400,500,600' rel='stylesheet' type='text/css'>
@ -264,6 +263,31 @@
margin-top: 1.6rem; margin-top: 1.6rem;
} }
} }
h3.mt0 {
margin-top: 0; /* ugh seriously */
}
.back-to-normal * {
font-family: "Fira Sans", sans-serif;
}
.back-to-normal h1,
.back-to-normal h2,
.back-to-normal h3,
.back-to-normal h4,
.back-to-normal h5
{
font-weight: 500;
margin: 0;
margin-bottom: 0.5rem;
font-size: 1.5rem;
line-height: 1.25em;
}
.back-to-normal a {
text-decoration: none;
}
</style> </style>
<script src="{{ static "js/carousel.js" }}"></script> <script src="{{ static "js/carousel.js" }}"></script>

View File

@ -90,16 +90,15 @@
{{ if .ProjectSettings.JamParticipation }} {{ if .ProjectSettings.JamParticipation }}
<div class="edit-form-row"> <div class="edit-form-row">
<div class="pt-input-ns">Jam Participation</div> <div class="pt-input-ns">Jam Participation</div>
</div> <div class="pt-input-ns">
{{ range .ProjectSettings.JamParticipation }} {{ range .ProjectSettings.JamParticipation }}
<div class="edit-form-row"> <div class="pb1">
<div>{{ .JamName }}:</div> <input id="jam_{{ .JamSlug }}" type="checkbox" name="jam_participation" value="{{ .JamSlug }}" {{ if .Participating }}checked{{ end }} />
<div> <label for="jam_{{ .JamSlug }}">{{ .JamName }}</label>
<input id="jam_{{ .JamSlug }}" type="checkbox" name="jam_participation" value="{{ .JamSlug }}" {{ if .Participating }}checked{{ end }} /> </div>
<label for="jam_{{ .JamSlug }}">Participating</label> {{ end }}
</div>
</div> </div>
{{ end }} </div>
{{ end }} {{ end }}
{{ if and .Editing .User.IsStaff }} {{ if and .Editing .User.IsStaff }}
<div class="edit-form-row"> <div class="edit-form-row">

View File

@ -5,39 +5,6 @@
#title { #title {
margin-top: 0; margin-top: 0;
} }
h3.mt0 {
margin-top: 0; /* ugh seriously */
}
.back-to-normal * {
font-family: "Fira Sans", sans-serif;
}
.back-to-normal h1,
.back-to-normal h2,
.back-to-normal h3,
.back-to-normal h4,
.back-to-normal h5
{
font-weight: 500;
margin: 0;
margin-bottom: 0.5rem;
font-size: 1.5rem;
line-height: 1.25em;
}
.back-to-normal a {
text-decoration: none;
}
@media screen and (min-width: 30em) {
/* not small styles */
}
@media screen and (min-width: 30em) {
/* large styles */
}
</style> </style>
<div id="top-container" class="flex flex-column items-center ph3"> <div id="top-container" class="flex flex-column items-center ph3">
@ -50,7 +17,7 @@
<div class="section bg-black-20 pt4 pb3 pb4-ns"> <div class="section bg-black-20 pt4 pb3 pb4-ns">
<div class="mw8 margin-center ph3 ph4-l flex flex-column flex-row-ns g3"> <div class="mw8 margin-center ph3 ph4-l flex flex-column flex-row-ns g3">
<div> <div class="flex-grow-1">
{{ if eq .DaysUntilEnd 0 }} {{ if eq .DaysUntilEnd 0 }}
<h3 class="mt0 mb3">Project updates</h3> <h3 class="mt0 mb3">Project updates</h3>
{{ else }} {{ else }}

View File

@ -3,6 +3,21 @@
{{ define "content" }} {{ define "content" }}
{{ $discordInviteURL := "https://discord.gg/zFt8Rf59?event=1004511448107602031" }} {{ $discordInviteURL := "https://discord.gg/zFt8Rf59?event=1004511448107602031" }}
<style>
.projects {
display: grid;
grid-template-columns: 1fr;
}
@media screen and (min-width: 30em) {
/* not small styles */
.projects {
grid-template-columns: 1fr 1fr;
}
}
</style>
<div id="top-container" class="flex flex-column items-center ph3"> <div id="top-container" class="flex flex-column items-center ph3">
<img id="logo" src="{{ static "wheeljam2022/logo.svg" }}"> <img id="logo" src="{{ static "wheeljam2022/logo.svg" }}">
<h1 id="title">Wheel Reinvention Jam</h1> <h1 id="title">Wheel Reinvention Jam</h1>
@ -22,14 +37,14 @@
<div class="actions flex justify-center"> <div class="actions flex justify-center">
{{ if gt .DaysUntilStart 0 }} {{ if gt .DaysUntilStart 0 }}
<a class="ba b--white br2 pv2 pv3-ns ph3 ph4-ns" target="_blank" href="https://github.com/HandmadeNetwork/wishlist/discussions">Find a project</a> <a class="ba b--white br2 pv2 pv3-ns ph3 ph4-ns" target="_blank" href="https://github.com/HandmadeNetwork/wishlist/discussions">Find a project</a>
{{ else }} {{ else if gt .DaysUntilEnd 0 }}
{{ if gt .DaysUntilEnd 0 }} {{ if .SubmittedProjectUrl }}
{{ if .SubmittedProjectUrl }} <a class="ba b--white br2 pv2 pv3-ns ph3 ph4-ns" target="_blank" href="{{ .SubmittedProjectUrl }}">Share your progress</a>
<a class="ba b--white br2 pv2 pv3-ns ph3 ph4-ns" target="_blank" href="{{ .SubmittedProjectUrl }}">Share your progress</a> {{ else }}
{{ else }} <a class="ba b--white br2 pv2 pv3-ns ph3 ph4-ns ml3" target="_blank" href="{{ .ProjectSubmissionUrl }}">Create your project</a>
<a class="ba b--white br2 pv2 pv3-ns ph3 ph4-ns ml3" target="_blank" href="{{ .ProjectSubmissionUrl }}">Create your project</a>
{{ end }}
{{ end }} {{ end }}
{{ else }}
<a class="ba b--white br2 pv2 pv3-ns ph3 ph4-ns ml3" href="{{ .ShowcaseFeedUrl }}">See the results</a>
{{ end }} {{ end }}
<a class="ba b--white br2 pv2 pv3-ns ph3 ph4-ns ml3" target="_blank" href="{{ $discordInviteURL }}">Join the Discord</a> <a class="ba b--white br2 pv2 pv3-ns ph3 ph4-ns ml3" target="_blank" href="{{ $discordInviteURL }}">Join the Discord</a>
</div> </div>
@ -50,13 +65,27 @@
</p> </p>
</div> </div>
{{ if not (eq .ShowcaseJson "[]") }} {{ if eq .DaysUntilEnd 0 }}
<div class="section bg-black-20 pv4 overflow-hidden">
<div class="mw8 margin-center ph3 ph4-l">
<h2>Submitted projects</h2>
<div class="mt3 projects g3 back-to-normal">
{{ range .JamProjects }}
{{ template "project_card.html" projectcarddata . "" }}
{{ end }}
</div>
<div class="actions flex justify-center">
<a class="ba b--white br2 pv2 pv3-ns ph3 ph4-ns ml3" href="{{ .ShowcaseFeedUrl }}">See all updates</a>
</div>
</div>
</div>
{{ else if and (eq .DaysUntilStart 0) (not (eq .ShowcaseJson "[]")) }}
<div id="showcase-outer-container" class="bg-black-20 pt4 pb3 pb4-ns"> <div id="showcase-outer-container" class="bg-black-20 pt4 pb3 pb4-ns">
<div class="section mw8 margin-center ph3 ph4-l"> <div class="section mw8 margin-center ph3 ph4-l">
{{ if gt .DaysUntilEnd 0 }} {{ if gt .DaysUntilEnd 0 }}
<h2>Recent updates</h2> <h2>Recent updates</h2>
<p> <p>
These screenshots and videos were shared by jam participants in <b>#project-showcase</b> on our <a href="{{ $discordInviteURL }}" target="_blank">Discord</a>. Join us and share what you're working on! These screenshots and videos were shared by jam participants in <b>#project-showcase</b> on our <a href="{{ $discordInviteURL }}" target="_blank">Discord</a>. Join us and share what you're working on! <a class="b" href="{{ .ShowcaseFeedUrl }}">See all ➜</a>
</p> </p>
{{ else }} {{ else }}
<h2>Community showcase</h2> <h2>Community showcase</h2>
@ -66,10 +95,114 @@
{{ end }} {{ end }}
<div id="showcase-container" class="mw8 center-layout mh2 mh0-ns"></div> <div id="showcase-container" class="mw8 center-layout mh2 mh0-ns"></div>
<div class="actions flex justify-center"> <div class="actions flex justify-center">
<a class="ba b--white br2 pv2 pv3-ns ph3 ph4-ns ml3" target="_blank" href="{{ .ShowcaseFeedUrl }}">See more</a> <a class="ba b--white br2 pv2 pv3-ns ph3 ph4-ns ml3" target="_blank" href="{{ .ShowcaseFeedUrl }}">See all</a>
</div> </div>
</div> </div>
</div> </div>
{{ template "showcase_templates.html" }}
<!-- Copy-pasted and mangled from showcase.html -->
<script>
const ROW_HEIGHT = 300;
const ITEM_SPACING = 4;
const showcaseItems = JSON.parse("{{ .ShowcaseJson }}");
const addThumbnailFuncs = new Array(showcaseItems.length);
const showcaseOuterContainer = document.querySelector('#showcase-outer-container');
let showcaseContainer = document.querySelector('#showcase-container');
// showcaseOuterContainer.classList.toggle('dn', showcaseItems.length === 0);
const itemElements = []; // array of arrays
for (let i = 0; i < showcaseItems.length; i++) {
const item = showcaseItems[i];
const [itemEl, addThumbnail] = makeShowcaseItem(item);
itemEl.container.setAttribute('data-index', i);
itemEl.container.setAttribute('data-date', item.date);
addThumbnailFuncs[i] = addThumbnail;
itemElements.push(itemEl.container);
}
function layout() {
const width = showcaseContainer.getBoundingClientRect().width;
showcaseContainer = emptyElement(showcaseContainer);
function addRow(itemEls, rowWidth, container) {
const totalSpacing = ITEM_SPACING * (itemEls.length - 1);
const scaleFactor = (width / Math.max(rowWidth, width));
const row = document.createElement('div');
row.classList.add('flex');
row.classList.toggle('justify-between', rowWidth >= width);
row.style.marginBottom = `${ITEM_SPACING}px`;
for (const itemEl of itemEls) {
const index = parseInt(itemEl.getAttribute('data-index'), 10);
const item = showcaseItems[index];
const aspect = item.width / item.height;
const baseWidth = (aspect * ROW_HEIGHT) * scaleFactor;
const actualWidth = baseWidth - (totalSpacing / itemEls.length);
itemEl.style.width = `${actualWidth}px`;
itemEl.style.height = `${scaleFactor * ROW_HEIGHT}px`;
itemEl.style.marginRight = `${ITEM_SPACING}px`;
row.appendChild(itemEl);
}
container.appendChild(row);
}
let rowItemEls = [];
let rowWidth = 0;
let numRows = 0;
for (const itemEl of itemElements) {
const index = parseInt(itemEl.getAttribute('data-index'), 10);
const item = showcaseItems[index];
const aspect = item.width / item.height;
rowWidth += aspect * ROW_HEIGHT;
rowItemEls.push(itemEl);
if (rowWidth > width) {
addRow(rowItemEls, rowWidth, showcaseContainer);
numRows += 1;
if (numRows == 3) {
return;
}
rowItemEls = [];
rowWidth = 0;
}
}
addRow(rowItemEls, rowWidth, showcaseContainer);
}
function loadImages() {
const items = showcaseContainer.querySelectorAll('.showcase-item');
for (const item of items) {
const i = parseInt(item.getAttribute('data-index'), 10);
addThumbnailFuncs[i]();
}
}
layout();
layout(); // scrollbars are fun!!
loadImages();
window.addEventListener('resize', () => {
layout();
});
</script>
{{ else }} {{ else }}
<div class="section bg-black-20 pv4 overflow-hidden"> <div class="section bg-black-20 pv4 overflow-hidden">
<div class="mw8 margin-center ph3 ph4-l"> <div class="mw8 margin-center ph3 ph4-l">
@ -229,110 +362,6 @@
</div> </div>
</div> </div>
</div> </div>
{{ template "showcase_templates.html" }}
<!-- Copy-pasted and mangled from showcase.html -->
<script>
const ROW_HEIGHT = 300;
const ITEM_SPACING = 4;
const showcaseItems = JSON.parse("{{ .ShowcaseJson }}");
const addThumbnailFuncs = new Array(showcaseItems.length);
const showcaseOuterContainer = document.querySelector('#showcase-outer-container');
let showcaseContainer = document.querySelector('#showcase-container');
// showcaseOuterContainer.classList.toggle('dn', showcaseItems.length === 0);
const itemElements = []; // array of arrays
for (let i = 0; i < showcaseItems.length; i++) {
const item = showcaseItems[i];
const [itemEl, addThumbnail] = makeShowcaseItem(item);
itemEl.container.setAttribute('data-index', i);
itemEl.container.setAttribute('data-date', item.date);
addThumbnailFuncs[i] = addThumbnail;
itemElements.push(itemEl.container);
}
function layout() {
const width = showcaseContainer.getBoundingClientRect().width;
showcaseContainer = emptyElement(showcaseContainer);
function addRow(itemEls, rowWidth, container) {
const totalSpacing = ITEM_SPACING * (itemEls.length - 1);
const scaleFactor = (width / Math.max(rowWidth, width));
const row = document.createElement('div');
row.classList.add('flex');
row.classList.toggle('justify-between', rowWidth >= width);
row.style.marginBottom = `${ITEM_SPACING}px`;
for (const itemEl of itemEls) {
const index = parseInt(itemEl.getAttribute('data-index'), 10);
const item = showcaseItems[index];
const aspect = item.width / item.height;
const baseWidth = (aspect * ROW_HEIGHT) * scaleFactor;
const actualWidth = baseWidth - (totalSpacing / itemEls.length);
itemEl.style.width = `${actualWidth}px`;
itemEl.style.height = `${scaleFactor * ROW_HEIGHT}px`;
itemEl.style.marginRight = `${ITEM_SPACING}px`;
row.appendChild(itemEl);
}
container.appendChild(row);
}
let rowItemEls = [];
let rowWidth = 0;
let numRows = 0;
for (const itemEl of itemElements) {
const index = parseInt(itemEl.getAttribute('data-index'), 10);
const item = showcaseItems[index];
const aspect = item.width / item.height;
rowWidth += aspect * ROW_HEIGHT;
rowItemEls.push(itemEl);
if (rowWidth > width) {
addRow(rowItemEls, rowWidth, showcaseContainer);
numRows += 1;
if (numRows == 3) {
return;
}
rowItemEls = [];
rowWidth = 0;
}
}
addRow(rowItemEls, rowWidth, showcaseContainer);
}
function loadImages() {
const items = showcaseContainer.querySelectorAll('.showcase-item');
for (const item of items) {
const i = parseInt(item.getAttribute('data-index'), 10);
addThumbnailFuncs[i]();
}
}
layout();
layout(); // scrollbars are fun!!
loadImages();
window.addEventListener('resize', () => {
layout();
});
</script>
<script> <script>
const carouselContainer = document.querySelector('.carousel-container'); const carouselContainer = document.querySelector('.carousel-container');

View File

@ -30,59 +30,67 @@ func JamIndex2022(c *RequestContext) ResponseData {
templates.BaseData templates.BaseData
DaysUntilStart, DaysUntilEnd int DaysUntilStart, DaysUntilEnd int
StartTimeUnix, EndTimeUnix int64 StartTimeUnix, EndTimeUnix int64
SubmittedProjectUrl string
ProjectSubmissionUrl string SubmittedProjectUrl string
ShowcaseFeedUrl string ProjectSubmissionUrl string
ShowcaseJson string ShowcaseFeedUrl string
ShowcaseJson string
JamProjects []templates.Project
} }
var showcaseItems []templates.TimelineItem var showcaseItems []templates.TimelineItem
submittedProjectUrl := "" submittedProjectUrl := ""
if daysUntilStart <= 0 && daysUntilEnd > 0 {
if c.CurrentUser != nil {
projects, err := hmndata.FetchProjects(c, c.Conn, c.CurrentUser, hmndata.ProjectsQuery{
OwnerIDs: []int{c.CurrentUser.ID},
JamSlugs: []string{hmndata.WRJ2022.Slug},
Limit: 1,
})
if err != nil {
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch jam projects for current user"))
}
if len(projects) > 0 {
urlContext := hmndata.UrlContextForProject(&projects[0].Project)
submittedProjectUrl = urlContext.BuildHomepage()
}
}
jamProjects, err := hmndata.FetchProjects(c, c.Conn, c.CurrentUser, hmndata.ProjectsQuery{ if c.CurrentUser != nil {
projects, err := hmndata.FetchProjects(c, c.Conn, c.CurrentUser, hmndata.ProjectsQuery{
OwnerIDs: []int{c.CurrentUser.ID},
JamSlugs: []string{hmndata.WRJ2022.Slug}, JamSlugs: []string{hmndata.WRJ2022.Slug},
Limit: 1,
}) })
if err != nil { if err != nil {
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch jam projects for current user")) return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch jam projects for current user"))
} }
if len(projects) > 0 {
projectIds := make([]int, 0, len(jamProjects)) urlContext := hmndata.UrlContextForProject(&projects[0].Project)
for _, jp := range jamProjects { submittedProjectUrl = urlContext.BuildHomepage()
projectIds = append(projectIds, jp.Project.ID)
} }
}
if len(projectIds) > 0 { jamProjects, err := hmndata.FetchProjects(c, c.Conn, c.CurrentUser, hmndata.ProjectsQuery{
snippets, err := hmndata.FetchSnippets(c, c.Conn, c.CurrentUser, hmndata.SnippetQuery{ JamSlugs: []string{hmndata.WRJ2022.Slug},
ProjectIDs: projectIds, })
Limit: 12, if err != nil {
}) return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch jam projects for current user"))
if err != nil { }
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch snippets for jam showcase"))
} pageProjects := make([]templates.Project, 0, len(jamProjects))
showcaseItems = make([]templates.TimelineItem, 0, len(snippets)) for _, p := range jamProjects {
for _, s := range snippets { pageProjects = append(pageProjects, templates.ProjectAndStuffToTemplate(&p, hmndata.UrlContextForProject(&p.Project).BuildHomepage(), c.Theme))
timelineItem := SnippetToTimelineItem(&s.Snippet, s.Asset, s.DiscordMessage, s.Projects, s.Owner, c.Theme, false) }
if timelineItem.CanShowcase {
showcaseItems = append(showcaseItems, timelineItem) projectIds := make([]int, 0, len(jamProjects))
} for _, jp := range jamProjects {
projectIds = append(projectIds, jp.Project.ID)
}
if len(projectIds) > 0 {
snippets, err := hmndata.FetchSnippets(c, c.Conn, c.CurrentUser, hmndata.SnippetQuery{
ProjectIDs: projectIds,
Limit: 12,
})
if err != nil {
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch snippets for jam showcase"))
}
showcaseItems = make([]templates.TimelineItem, 0, len(snippets))
for _, s := range snippets {
timelineItem := SnippetToTimelineItem(&s.Snippet, s.Asset, s.DiscordMessage, s.Projects, s.Owner, c.Theme, false)
if timelineItem.CanShowcase {
showcaseItems = append(showcaseItems, timelineItem)
} }
} }
} }
showcaseJson := templates.TimelineItemsToJSON(showcaseItems) showcaseJson := templates.TimelineItemsToJSON(showcaseItems)
res.MustWriteTemplate("wheeljam_2022_index.html", JamPageData{ res.MustWriteTemplate("wheeljam_2022_index.html", JamPageData{
@ -95,6 +103,7 @@ func JamIndex2022(c *RequestContext) ResponseData {
SubmittedProjectUrl: submittedProjectUrl, SubmittedProjectUrl: submittedProjectUrl,
ShowcaseFeedUrl: hmnurl.BuildJamFeed2022(), ShowcaseFeedUrl: hmnurl.BuildJamFeed2022(),
ShowcaseJson: showcaseJson, ShowcaseJson: showcaseJson,
JamProjects: pageProjects,
}, c.Perf) }, c.Perf)
return res return res
} }

View File

@ -29,7 +29,8 @@ type LandingTemplateData struct {
AtomFeedUrl string AtomFeedUrl string
MarkAllReadUrl string MarkAllReadUrl string
JamUrl string JamUrl string
JamDaysUntilStart, JamDaysUntilEnd int
} }
func Index(c *RequestContext) ResponseData { func Index(c *RequestContext) ResponseData {
@ -149,7 +150,9 @@ func Index(c *RequestContext) ResponseData {
AtomFeedUrl: hmnurl.BuildAtomFeed(), AtomFeedUrl: hmnurl.BuildAtomFeed(),
MarkAllReadUrl: hmnurl.HMNProjectContext.BuildForumMarkRead(0), MarkAllReadUrl: hmnurl.HMNProjectContext.BuildForumMarkRead(0),
JamUrl: hmnurl.BuildJamIndex(), JamUrl: hmnurl.BuildJamIndex(),
JamDaysUntilStart: daysUntil(hmndata.WRJ2022.StartTime),
JamDaysUntilEnd: daysUntil(hmndata.WRJ2022.EndTime),
}, c.Perf) }, c.Perf)
if err != nil { if err != nil {
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to render landing page template")) return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to render landing page template"))