Merge pull request 'Add banner to ask users to be a beta tester' (#32) from calling-testers into main
All checks were successful
Deploy / deploy (push) Successful in 30s
All checks were successful
Deploy / deploy (push) Successful in 30s
Reviewed-on: #32
This commit is contained in:
commit
efe4ba9e98
16 changed files with 287 additions and 14 deletions
|
@ -20,8 +20,17 @@ jobs:
|
||||||
chmod 600 ./deploy.key
|
chmod 600 ./deploy.key
|
||||||
echo "${{ secrets.SSH_KNOWN_HOSTS }}" > ~/.ssh/known_hosts
|
echo "${{ secrets.SSH_KNOWN_HOSTS }}" > ~/.ssh/known_hosts
|
||||||
|
|
||||||
|
- name: Create env file
|
||||||
|
run: |
|
||||||
|
touch .env
|
||||||
|
echo EMAIL_ACCESS_KEY=${{ secrets.EMAIL_ACCESS_KEY }} >> .env
|
||||||
|
echo EMAIL_BASE_URL=${{ secrets.EMAIL_BASE_URL }} >> .env
|
||||||
|
|
||||||
|
- name: Verify .env file creation
|
||||||
|
run: cat .env
|
||||||
|
|
||||||
- name: Install PM2
|
- name: Install PM2
|
||||||
run: npm i -g pm2
|
run: npm i -g pm2
|
||||||
|
|
||||||
- name: Deploy
|
- name: Deploy
|
||||||
run: pm2 deploy ecosystem.config.cjs production
|
run: env $(cat .env | grep -v \"#\" | xargs) pm2 deploy ecosystem.config.cjs production
|
||||||
|
|
|
@ -10,6 +10,8 @@ module.exports = {
|
||||||
watch: false,
|
watch: false,
|
||||||
max_memory_restart: '1G',
|
max_memory_restart: '1G',
|
||||||
env: {
|
env: {
|
||||||
|
EMAIL_ACCESS_KEY: process.env.EMAIL_ACCESS_KEY,
|
||||||
|
EMAIL_BASE_URL: process.env.EMAIL_BASE_URL,
|
||||||
NODE_ENV: 'production',
|
NODE_ENV: 'production',
|
||||||
PORT: 7281,
|
PORT: 7281,
|
||||||
},
|
},
|
||||||
|
@ -27,6 +29,8 @@ module.exports = {
|
||||||
'post-deploy':
|
'post-deploy':
|
||||||
'npm run build && pm2 reload ecosystem.config.cjs --only embroidery-viewer-prod --env production && pm2 save',
|
'npm run build && pm2 reload ecosystem.config.cjs --only embroidery-viewer-prod --env production && pm2 save',
|
||||||
env: {
|
env: {
|
||||||
|
EMAIL_ACCESS_KEY: process.env.EMAIL_ACCESS_KEY,
|
||||||
|
EMAIL_BASE_URL: process.env.EMAIL_BASE_URL,
|
||||||
PORT: 7281,
|
PORT: 7281,
|
||||||
NODE_ENV: 'production',
|
NODE_ENV: 'production',
|
||||||
},
|
},
|
||||||
|
|
BIN
src/lib/assets/app-with-frame-pt.png
Normal file
BIN
src/lib/assets/app-with-frame-pt.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 MiB |
BIN
src/lib/assets/app-with-frame.png
Normal file
BIN
src/lib/assets/app-with-frame.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 MiB |
|
@ -18,5 +18,14 @@
|
||||||
"seo.description": "Upload and preview embroidery files instantly with Embroidery Viewer. Supports DST, PES, JEF, EXP, VP3 and more. No installs, no uploads – 100% browser-based and free.",
|
"seo.description": "Upload and preview embroidery files instantly with Embroidery Viewer. Supports DST, PES, JEF, EXP, VP3 and more. No installs, no uploads – 100% browser-based and free.",
|
||||||
"seo.keywords": "embroidery viewer, online embroidery viewer, embroidery file preview, DST viewer, PES viewer, free embroidery tool, JEF viewer, EXP embroidery, VP3 embroidery viewer, embroidery preview tool, browser embroidery renderer, convert embroidery to PNG",
|
"seo.keywords": "embroidery viewer, online embroidery viewer, embroidery file preview, DST viewer, PES viewer, free embroidery tool, JEF viewer, EXP embroidery, VP3 embroidery viewer, embroidery preview tool, browser embroidery renderer, convert embroidery to PNG",
|
||||||
"seo.url": "https://embroideryviewer.xyz",
|
"seo.url": "https://embroideryviewer.xyz",
|
||||||
"seo.image": "https://embroideryviewer.xyz/og/"
|
"seo.image": "https://embroideryviewer.xyz/og/",
|
||||||
|
"banner.title": "🚀 Be a Beta Tester",
|
||||||
|
"banner.subtitle": "We’re launching the <strong style=\"color: white\">Embroidery Viewer Android App </strong> — and you can be one of the first to try it!",
|
||||||
|
"banner.description": "Enjoy a smooth, ad-free experience to visualize PES, JEF, PEC, and VP3 embroidery files right from your phone. Help us improve it and get early access before everyone else.",
|
||||||
|
"banner.name": "Name",
|
||||||
|
"banner.email": "Email",
|
||||||
|
"banner.cta": "Join the Beta",
|
||||||
|
"banner.cta.loading": "Joining...",
|
||||||
|
"banner.feedback.success": "Name and email sent successfully! We'll contact you soon!",
|
||||||
|
"banner.feedback.error": "Something went wrong. Try sending you name and e-mail directly to leo@leomurca.xyz."
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,5 +18,14 @@
|
||||||
"seo.description": "Envie e visualize arquivos de bordado instantaneamente com o Embroidery Viewer. Compatível com DST, PES, JEF, EXP, VP3 e mais. Sem instalações, sem uploads – 100% no navegador e gratuito.",
|
"seo.description": "Envie e visualize arquivos de bordado instantaneamente com o Embroidery Viewer. Compatível com DST, PES, JEF, EXP, VP3 e mais. Sem instalações, sem uploads – 100% no navegador e gratuito.",
|
||||||
"seo.keywords": "visualizador de bordado, visualizador online de bordado, visualizar arquivos de bordado, visualizar DST, visualizar PES, ferramenta gratuita de bordado, visualizador JEF, bordado EXP, visualizador VP3, pré-visualização de bordado, renderizador de bordado no navegador, converter bordado em PNG",
|
"seo.keywords": "visualizador de bordado, visualizador online de bordado, visualizar arquivos de bordado, visualizar DST, visualizar PES, ferramenta gratuita de bordado, visualizador JEF, bordado EXP, visualizador VP3, pré-visualização de bordado, renderizador de bordado no navegador, converter bordado em PNG",
|
||||||
"seo.url": "https://embroideryviewer.xyz",
|
"seo.url": "https://embroideryviewer.xyz",
|
||||||
"seo.image": "https://embroideryviewer.xyz/og/"
|
"seo.image": "https://embroideryviewer.xyz/og/",
|
||||||
|
"banner.title": "🚀 Seja um Testador Beta",
|
||||||
|
"banner.subtitle": "Estamos lançando o aplicativo <strong style=\"color: white\">Embroidery Viewer para Android</strong> — e você pode ser um dos primeiros a experimentá-lo!",
|
||||||
|
"banner.description": "Desfrute de uma experiência fluida e sem anúncios para visualizar arquivos de bordado PES, JEF, PEC e VP3 diretamente do seu celular. Ajude-nos a melhorá-lo e obtenha acesso antecipado antes de todo mundo.",
|
||||||
|
"banner.name": "Nome",
|
||||||
|
"banner.email": "Email",
|
||||||
|
"banner.cta": "Seja um Testador",
|
||||||
|
"banner.cta.loading": "Enviando...",
|
||||||
|
"banner.feedback.success": "Nome e e-mail enviados com sucesso! Entraremos em contato em breve!",
|
||||||
|
"banner.feedback.error": "Algo deu errado. Tente enviar seu nome e e-mail diretamente para leo@leomurca.xyz."
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
<style>
|
<style>
|
||||||
main {
|
main {
|
||||||
flex: 1; /* This pushes footer to bottom */
|
flex: 1; /* This pushes footer to bottom */
|
||||||
padding: 20px;
|
padding: 0;
|
||||||
min-height: 90vh;
|
min-height: 90vh;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
|
||||||
/** @type {import('./$types').PageLoad} */
|
/** @type {import('./$types').PageLoad} */
|
||||||
export function load() {
|
export function load() {
|
||||||
return {
|
return {
|
||||||
|
|
34
src/routes/+page.server.js
Normal file
34
src/routes/+page.server.js
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import { EMAIL_ACCESS_KEY, EMAIL_BASE_URL } from '$env/static/private';
|
||||||
|
|
||||||
|
|
||||||
|
/** @type {import('./$types').Actions} */
|
||||||
|
export const actions = {
|
||||||
|
default: async ({ request }) => {
|
||||||
|
const formData = await request.formData();
|
||||||
|
console.log(formData);
|
||||||
|
|
||||||
|
const response = await fetch(`${EMAIL_BASE_URL}/submit`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
accessKey: EMAIL_ACCESS_KEY,
|
||||||
|
subject: 'Contato - Embroidery Viewer Beta Testers!',
|
||||||
|
name: formData.get('name'),
|
||||||
|
email: formData.get('email'),
|
||||||
|
}),
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
});
|
||||||
|
|
||||||
|
const json = await response.json();
|
||||||
|
if (json.error === undefined) {
|
||||||
|
return {
|
||||||
|
message: "home.banner.feedback.success",
|
||||||
|
textColor: 'green'
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
message: 'home.banner.feedback.error',
|
||||||
|
textColor: 'red'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -1,15 +1,84 @@
|
||||||
<script>
|
<script>
|
||||||
import Seo from '$lib/components/Seo.svelte';
|
// @ts-nocheck
|
||||||
import { t } from '$lib/translations';
|
|
||||||
|
import { applyAction, enhance } from '$app/forms';
|
||||||
|
import { invalidateAll } from '$app/navigation';
|
||||||
|
import { t, locale, SUPPORTED_LOCALES } from '$lib/translations';
|
||||||
|
|
||||||
|
import Seo from '$lib/components/Seo.svelte';
|
||||||
|
import appScreenshot from '$lib/assets/app-with-frame.png';
|
||||||
|
import appScreenshotPt from '$lib/assets/app-with-frame-pt.png';
|
||||||
|
import { browser } from '$app/environment';
|
||||||
|
|
||||||
/** @type {import('./$types').PageProps} */
|
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
|
/**
|
||||||
|
* @type {{ textColor: String; message: String; } | null}
|
||||||
|
*/
|
||||||
|
let feedbackMessage = $state(null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
let loading = $state(false);
|
||||||
|
|
||||||
const metadata = data.metadata;
|
const metadata = data.metadata;
|
||||||
|
|
||||||
|
const resetFeedback = () => {
|
||||||
|
if (feedbackMessage) feedbackMessage = null
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Seo {...metadata} />
|
<Seo {...metadata} />
|
||||||
|
|
||||||
|
<div class="beta-section">
|
||||||
|
<div class="beta-content">
|
||||||
|
<div class="beta-image">
|
||||||
|
|
||||||
|
<img src={$locale === SUPPORTED_LOCALES.EN_US ? appScreenshot : appScreenshotPt} alt="Embroidery Viewer App Screenshot" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="beta-text">
|
||||||
|
<h1>{$t("home.banner.title")}</h1>
|
||||||
|
<p class="lead">
|
||||||
|
{@html $t('home.banner.subtitle')}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{$t('home.banner.description')}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
<form
|
||||||
|
action="/"
|
||||||
|
method="POST"
|
||||||
|
class="beta-form"
|
||||||
|
use:enhance={() => {
|
||||||
|
loading = true;
|
||||||
|
return async ({ result }) => {
|
||||||
|
loading = false;
|
||||||
|
feedbackMessage = result.data;
|
||||||
|
if (result.type === 'success') await invalidateAll();
|
||||||
|
applyAction(result);
|
||||||
|
};
|
||||||
|
}}>
|
||||||
|
|
||||||
|
<label for="name">{$t("home.banner.name")}</label>
|
||||||
|
<input type="text" name="name" id="name" oninput={resetFeedback} required />
|
||||||
|
|
||||||
|
<label for="email">{$t("home.banner.email")}</label>
|
||||||
|
<input type="email" name="_replyto" id="email" oninput={resetFeedback} required />
|
||||||
|
|
||||||
|
<button type="submit">{$t(loading ? 'home.banner.cta.loading' : 'home.banner.cta') }</button>
|
||||||
|
{#if feedbackMessage !== null}
|
||||||
|
<p style="margin-top: 1rem; color: {feedbackMessage.textColor}">
|
||||||
|
{$t(feedbackMessage.message)}
|
||||||
|
</p>
|
||||||
|
{/if}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="home-container">
|
<div class="home-container">
|
||||||
<section aria-labelledby="main-title">
|
<section aria-labelledby="main-title">
|
||||||
<h1 id="main-title">{$t('home.main.title')}</h1>
|
<h1 id="main-title">{$t('home.main.title')}</h1>
|
||||||
|
@ -51,14 +120,152 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
||||||
|
.beta-form {
|
||||||
|
background: white;
|
||||||
|
color: #000;
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
max-width: 400px;
|
||||||
|
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.beta-form label {
|
||||||
|
display: block;
|
||||||
|
margin-top: 1rem;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.beta-form input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.6rem;
|
||||||
|
margin-top: 0.3rem;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.beta-form button {
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
width: 100%;
|
||||||
|
background-color: #05345f;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 0.8rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.beta-form button:disabled {
|
||||||
|
background-color: #7aa3c1;
|
||||||
|
cursor: wait;
|
||||||
|
}
|
||||||
|
|
||||||
|
.beta-form button:hover:enabled {
|
||||||
|
background-color: #042b4f;
|
||||||
|
}
|
||||||
.home-container {
|
.home-container {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
width: 70%;
|
width: 70%;
|
||||||
|
padding-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.home-container {
|
.home-container {
|
||||||
width: 100%;
|
width: 90%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.beta-section {
|
||||||
|
background-color: #05345f;
|
||||||
|
color: white;
|
||||||
|
padding: 3rem 1rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.beta-content {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 2rem;
|
||||||
|
max-width: 1000px;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.beta-image img {
|
||||||
|
max-width: 300px;
|
||||||
|
border-radius: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.beta-text {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 280px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.beta-text h1 {
|
||||||
|
font-size: 2rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.beta-text .lead {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.beta-text p {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.beta-form {
|
||||||
|
background: white;
|
||||||
|
color: #000;
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
max-width: 400px;
|
||||||
|
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.beta-form label {
|
||||||
|
display: block;
|
||||||
|
margin-top: 1rem;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.beta-form input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.6rem;
|
||||||
|
margin-top: 0.3rem;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.beta-form button {
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
width: 100%;
|
||||||
|
background-color: #05345f;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 0.8rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.beta-form button:hover {
|
||||||
|
background-color: #042b4f;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.beta-content {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.beta-form {
|
||||||
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
section {
|
section {
|
||||||
width: 100%;
|
width: 90%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -164,7 +164,7 @@
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.donate-container {
|
.donate-container {
|
||||||
width: 100%;
|
width: 90%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.donation-options {
|
.donation-options {
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
section {
|
section {
|
||||||
width: 100%;
|
width: 90%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
section {
|
section {
|
||||||
width: 100%;
|
width: 90%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
section {
|
section {
|
||||||
width: 100%;
|
width: 90%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -147,7 +147,7 @@
|
||||||
|
|
||||||
@media only screen and (max-device-width: 768px) {
|
@media only screen and (max-device-width: 768px) {
|
||||||
#form {
|
#form {
|
||||||
width: 100%;
|
width: 90%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Add table
Reference in a new issue