Skip to content

Commit f6c293c

Browse files
authored
Better formula submission page (#18)
* Scaffold of a better suggestion page with a preview * I'm a golden god (fixed list items not having dot in preview) * Oops add back term page rendering * Fix tags in preview render * Changes to about page * Preview renders automatically on first load. Add instructions. Add description field * submit button that submits to discord webhook lmao * Fix submit/preview button looking bad after click * Rate limit submissions * suggest -> submit * Sanitize inputs to prevent xss * padding, capitalization * Prevent / from triggering search when on the formula suggestion page * Move javascript from submit.html to separate file for better organization cough
1 parent 2799a91 commit f6c293c

File tree

9 files changed

+358
-22
lines changed

9 files changed

+358
-22
lines changed

assets/css/custom.css

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,3 +314,28 @@ img[alt=diagram] {
314314
background-color: rgba(var(--color-primary-600), 1);
315315
border-radius: 0.09rem;
316316
}
317+
318+
.p-20 {
319+
padding: 20px;
320+
}
321+
322+
.rounded-md {
323+
border-radius: 0.375rem;
324+
}
325+
326+
327+
.prose :where(ul):not(:where([class~="not-prose"] *)) {
328+
list-style-type: disc;
329+
}
330+
331+
.p-12 {
332+
padding: 3rem;
333+
}
334+
335+
.pt-20 {
336+
padding-top: 5rem;
337+
}
338+
339+
.pb-8 {
340+
padding-bottom: 2rem;
341+
}

assets/js/formula-suggest.js

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
function sanitizeInput(input) {
2+
return DOMPurify.sanitize(input);
3+
}
4+
// Function to render the preview
5+
function renderPreview() {
6+
const title = sanitizeInput(document.getElementById('title').value);
7+
const body = sanitizeInput(document.getElementById('body').value);
8+
const latex = sanitizeInput(document.getElementById('latex').value);
9+
const tags = sanitizeInput(document.getElementById('tags').value).split(',').map(tag => tag.trim());
10+
const parsedBody = marked.parse(body);
11+
12+
const tagsHTML = tags
13+
.map((tag) => {
14+
return `
15+
<a
16+
class="rounded-md border border-neutral-200 px-2 py-[1px] hover:border-black hover:text-primary-700 dark:border-neutral-600 dark:hover:border-primary-600 dark:hover:text-primary-400"
17+
>
18+
${tag}
19+
</a>
20+
`;
21+
})
22+
.join(' '); // Join the tags with a space
23+
24+
const previewContent = `
25+
<article>
26+
<header class="max-w-prose">
27+
<h1 class="mt-0 text-4xl font-extrabold text-neutral-900 dark:text-neutral">
28+
${title}
29+
</h1>
30+
<div class="my-2 text-xs text-neutral-500 dark:text-neutral-400">
31+
${tagsHTML}
32+
</div>
33+
<div class="mt-8 mb-12 text-base text-neutral-500 dark:text-neutral-400 print:hidden prose">
34+
${parsedBody}
35+
</div>
36+
</header>
37+
</article>
38+
`;
39+
40+
document.getElementById('preview-content').innerHTML = previewContent;
41+
document.getElementById('preview').classList.remove('hidden');
42+
43+
// Render LaTeX
44+
if (typeof renderMathInElement !== 'undefined') {
45+
renderMathInElement(document.getElementById('preview-content'), {
46+
delimiters: [
47+
{ left: '$$', right: '$$', display: true },
48+
{ left: '\\(', right: '\\)', display: false }
49+
]
50+
});
51+
}
52+
}
53+
54+
// Function to send data to Discord webhook
55+
function submitToDiscord() {
56+
const title = sanitizeInput(document.getElementById('title').value);
57+
const description = sanitizeInput(document.getElementById('description').value);
58+
const tags = sanitizeInput(document.getElementById('tags').value);
59+
const latex = sanitizeInput(document.getElementById('latex').value);
60+
const body = sanitizeInput(document.getElementById('body').value);
61+
62+
const webhookURL = 'https://discord.com/api/webhooks/1059998584889688134/eUqXbInp90bcFdL1A3ly141TAtn9jiPjbYwCzSfjPV2-4kx2UIX3M-soCJxWTrvSNbLP'; // Replace with your Discord webhook URL
63+
64+
const data = {
65+
embeds: [
66+
{
67+
title: "New stemformulas.com formula suggestion",
68+
fields: [
69+
{
70+
name: "Title",
71+
value: title,
72+
inline: true,
73+
},
74+
{
75+
name: "Description",
76+
value: description,
77+
inline: true,
78+
},
79+
{
80+
name: "Tags",
81+
value: tags,
82+
inline: true,
83+
},
84+
{
85+
name: "LaTeX",
86+
value: `\`\`\`latex\n${latex}\n\`\`\``,
87+
inline: false,
88+
},
89+
{
90+
name: "Body",
91+
value: `\`\`\`markdown\n${body}\n\`\`\``,
92+
inline: false,
93+
},
94+
],
95+
color: 0x00ff00, // Green color for the embed
96+
},
97+
],
98+
};
99+
100+
fetch(webhookURL, {
101+
method: 'POST',
102+
headers: {
103+
'Content-Type': 'application/json',
104+
},
105+
body: JSON.stringify(data),
106+
})
107+
.then((response) => {
108+
if (response.ok) {
109+
alert('Submission sent successfully. Thank you!');
110+
} else {
111+
alert('Failed to send submission. Please try again.');
112+
}
113+
})
114+
.catch((error) => {
115+
console.error('Error:', error);
116+
alert('An error occurred. Please try again.');
117+
});
118+
}
119+
120+
// Attach the form submission handler
121+
document.getElementById('formula-suggestion-form').addEventListener('submit', function(event) {
122+
event.preventDefault();
123+
renderPreview();
124+
// Show the "Submit" button once the preview has been rendered once
125+
document.getElementById('submit-button').classList.remove('hidden');
126+
});
127+
128+
function handleRateLimit() {
129+
// limit submissions so we can't get spammed
130+
const submitButton = document.getElementById('submit-button');
131+
const lastSubmissionTime = localStorage.getItem('lastSubmissionTime');
132+
const currentTime = new Date().getTime();
133+
134+
// Check if 5 minutes have passed since the last submission
135+
const timeLeft = 120000 - (currentTime - lastSubmissionTime);
136+
if (lastSubmissionTime && timeLeft > 0) {
137+
alert(`Please wait ${Math.ceil(timeLeft / 1000)} seconds before submitting again.`);
138+
return;
139+
}
140+
141+
// Disable the button and store the current time
142+
submitButton.disabled = true;
143+
localStorage.setItem('lastSubmissionTime', currentTime);
144+
145+
// Submit the data to Discord
146+
submitToDiscord();
147+
148+
// Re-enable the button after the correct time has elapsed
149+
setTimeout(() => {
150+
submitButton.disabled = false;
151+
}, timeLeft);
152+
}
153+
154+
// Attach the "Submit" button click handler
155+
document.getElementById('submit-button').addEventListener('click', function() {
156+
handleRateLimit();
157+
});
158+
159+
// Render the preview when the page loads
160+
document.addEventListener('DOMContentLoaded', function() {
161+
renderPreview();
162+
});

assets/js/search.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
var fuse;
22
var onMainPage = document.getElementById("search-query-main");
3+
var onSubmitPage = document.getElementById("formula-suggestion-form")
34
var showButtons = document.querySelectorAll("[id^='search-button']");
45
if(onMainPage){
56
var input = document.getElementById("search-query-main");
@@ -41,6 +42,12 @@ if (!(onMainPage)){
4142
document.addEventListener("keydown", function (event) {
4243
// Forward slash to open search wrapper
4344
if (event.key == "/") {
45+
// ignore if we're on the suggest page
46+
if (onSubmitPage){
47+
event.preventDefault();
48+
return;
49+
}
50+
4451
if (!searchVisible) {
4552
event.preventDefault();
4653
displaySearch();

config/_default/menus.en.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@
2626
weight = 30
2727

2828
[[main]]
29-
name = "suggest"
30-
pageRef = "suggest"
29+
name = "submit"
30+
pageRef = "submit"
3131
weight = 40
3232

3333
[[main]]

content/about/_index.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ showTableOfContents: true
55
---
66
Stemformulas is a website dedicated to providing a single place to look for STEM formulas. It has a long way to go to become that, but the foundation is laid out.
77

8-
It was made by a few engineering students who were frustrated with the current state of formula searching online. This frustration lead to the focus of the features of the site, which include:
8+
It was made by a few engineering students (lead by [@linguinelabs](https://x.com/linguinelabs)) who were frustrated with the current state of formula searching online. This frustration lead to the focus of the features of the site, which include:
99

1010
- The search bar being in focus on site load, so you can search for a formula quickly
1111
- LaTeX being copyable by just clicking on it on any formula's page
@@ -24,7 +24,8 @@ It is hosted on [GitHub Pages](https://pages.github.com/), deployed conveniently
2424
## Contributing
2525
If you want to add a formula to this site, there are two ways you can do so.
2626

27-
1. Submit a suggestion for a formula on our [Google Form](https://forms.gle/EWjwFmiEQrrjsZEF9) (can also be filled out on our [suggest](/suggest) page). This form is also used for general feedback, e.g. if one of our pages has a mistake.
27+
1. Submit a formula on our [suggest](/suggest) page). This has been revamped to have a preview for ease of use.
2828

29-
2. Create a pull request on the
30-
[GitHub repo](https://github.com/stemformulas/stemformulas.github.io). More detailed instructions can be found in the [README](https://github.com/stemformulas/stemformulas.github.io#adding-a-formula-by-submitting-a-pull-request).
29+
2. Create a pull request directly on the
30+
[GitHub repo](https://github.com/stemformulas/stemformulas.github.io). This is of course easier for us to merge, but requires more effort from you.
31+
More detailed contribution instructions can be found in the [README](https://github.com/stemformulas/stemformulas.github.io?tab=readme-ov-file#adding-a-formula).
Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
title: "Suggest"
33
description: "Make a suggestion"
44
---
5-
<div class="form-container">
5+
{{<katex>}}
6+
<!-- katex tag is required lol -->
7+
8+
<!-- old google form -->
9+
<!-- <div class="form-container">
610
<iframe allowfullscreen src="https://docs.google.com/forms/d/e/1FAIpQLScQP-w7M7BqscA68Htup4KYBE25EgntDg2EhhuVYX50PJHmTg/viewform?embedded=true" width="100%" height="2000" frameborder="0" marginheight="0" marginwidth="0">Loading…</iframe>
7-
</div>
11+
</div> -->

layouts/_default/submit.html

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
{{ define "main" }}
2+
<!-- Custom html/js page for submitting formulas. Rather than use hugo templating, which has to be static,
3+
I kind of reverse engineered the formula pages and render a preview using the user's inputs-->
4+
5+
<article class="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8">
6+
<header class="max-w-prose">
7+
<h1 class="mt-0 mb-2 text-4xl font-extrabold text-neutral-900 dark:text-neutral">
8+
submit a formula
9+
</h1>
10+
<p class="text-lg text-neutral-500 dark:text-neutral-400 mb-6">
11+
Replace the example fields below, preview your submission, and then submit!
12+
</p>
13+
</header>
14+
15+
<section class="bg-white dark:bg-neutral-800 rounded-md shadow-sm border border-neutral-200 dark:border-neutral-700">
16+
<div class="p-6">
17+
<form id="formula-suggestion-form" class="space-y-6 p-20">
18+
<div class="space-y-2">
19+
<label
20+
for="title"
21+
class="block text-lg mb-1 font-medium text-neutral-700 dark:text-neutral-300">
22+
Title
23+
</label>
24+
<input
25+
type="text"
26+
id="title"
27+
name="title"
28+
placeholder="Gaussian/Normal Distribution"
29+
value="Gaussian/Normal Distribution"
30+
required
31+
class="w-full px-4 py-2 mb-6 rounded-md dark:bg-neutral-800 border border-neutral-200 dark:border-neutral-600 focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
32+
>
33+
</div>
34+
35+
<div class="space-y-2">
36+
<label
37+
for="description"
38+
class="block text-lg mb-1 font-medium text-neutral-700 dark:text-neutral-300">
39+
Short Description (used in search results only)
40+
</label>
41+
<input
42+
type="text"
43+
id="description"
44+
name="description"
45+
placeholder="The formula for the normal distribution."
46+
value="The formula for the normal distribution."
47+
required
48+
class="w-full px-4 py-2 mb-6 rounded-md dark:bg-neutral-800 border border-neutral-200 dark:border-neutral-600 focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
49+
>
50+
</div>
51+
52+
<div class="space-y-2">
53+
<label for="tags" class="block text-lg mb-1 font-medium text-neutral-700 dark:text-neutral-300">
54+
Tags (comma-separated)
55+
</label>
56+
<input
57+
type="text"
58+
id="tags"
59+
name="tags"
60+
placeholder="math, statistics, probability theory"
61+
value="math, statistics, probability theory"
62+
required
63+
class="w-full px-4 py-2 mb-6 rounded-md dark:bg-neutral-800 border border-neutral-200 dark:border-neutral-600 focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
64+
>
65+
</div>
66+
67+
<div class="space-y-2">
68+
<label for="latex" class="block text-lg mb-1 font-medium text-neutral-700 dark:text-neutral-300">
69+
LaTeX for card preview
70+
</label>
71+
<textarea
72+
id="latex"
73+
name="latex"
74+
placeholder="f(x) = \frac{1}{\sigma \sqrt{2\pi}} e^{-\frac{1}{2}\left(\frac{x-\mu}{\sigma}\right)^2}"
75+
required
76+
rows="2"
77+
class="w-full px-4 py-2 mb-6 rounded-md dark:bg-neutral-800 border border-neutral-200 dark:border-neutral-600 focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
78+
>f(x) = \frac{1}{\sigma \sqrt{2\pi}} e^{-\frac{1}{2}\left(\frac{x-\mu}{\sigma}\right)^2}
79+
</textarea>
80+
</div>
81+
82+
<div class="space-y-2">
83+
<label for="body" class="block text-lg mb-1 font-medium text-neutral-700 dark:text-neutral-300">
84+
Body Text (Markdown, LaTeX)
85+
</label>
86+
<textarea
87+
id="body"
88+
name="body"
89+
required
90+
rows="8"
91+
class="w-full px-4 py-2 mb-6 rounded-md dark:bg-neutral-800 border border-neutral-200 dark:border-neutral-600 focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
92+
>The formula for the normal distribution as a function of the variable x is:
93+
$$ \frac{1}{\sigma \sqrt{2\pi}} e^{-\frac{1}{2}\left(\frac{x-\mu}{\sigma}\right)^2} $$
94+
95+
Where
96+
97+
* \\( \small e\ \\) is Euler's number,
98+
* \\( \small \mu\ \\) is the mean of the distribution, and
99+
* \\\( \small \sigma\ \\) is the standard deviation of the distribution.
100+
101+
## Sources
102+
103+
- [Wikipedia](https://en.wikipedia.org/wiki/Normal_distribution)
104+
- [Britannica](https://www.britannica.com/topic/uniform-distribution-statistics)</textarea>
105+
</div>
106+
107+
<button
108+
type="submit"
109+
class="px-6 py-2 text-base font-semibold text-white rounded-md border-neutral-200 dark:bg-neutral-700 hover:bg-primary-100 dark:hover:bg-primary-600 focus:outline-dotted focus:outline-transparent focus:outline-2"
110+
>
111+
Preview
112+
</button>
113+
</form>
114+
</div>
115+
</section>
116+
117+
<section id="preview" class="max-w-prose mt-8 hidden">
118+
<h2 class="mb-6 text-2xl font-bold text-neutral-900 dark:text-neutral">Preview</h2>
119+
<div id="preview-content" class="bg-white dark:bg-neutral-800 rounded-lg shadow-sm border border-neutral-200 dark:border-neutral-700 p-12 pt-20 pb-8">
120+
</div>
121+
<!-- Add a "Submit" button below the preview -->
122+
<button
123+
id="submit-button"
124+
class="mt-6 px-6 py-2 text-base font-semibold text-white rounded-md border-neutral-200 dark:bg-neutral-700 hover:bg-primary-100 dark:hover:bg-primary-600 focus:outline-dotted focus:outline-transparent focus:outline-2 hidden"
125+
>
126+
Submit
127+
</button>
128+
</section>
129+
</article>
130+
<script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.2.3/purify.min.js"></script>
131+
{{ $js := resources.Get "js/formula-suggest.js" | fingerprint }}
132+
<script src="{{ $js.Permalink }}"></script>
133+
{{ end }}

0 commit comments

Comments
 (0)