Skip to content

Commit 3078b9f

Browse files
add Hub CE registration form
1 parent 0ac4079 commit 3078b9f

5 files changed

Lines changed: 313 additions & 0 deletions

File tree

assets/js/hubce.js

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
"use strict";
2+
3+
// requires newsletter.js
4+
const VERIFY_EMAIL_URL = API_BASE_URL + '/connect/email/verify';
5+
const GET_LICENSE_URL = API_BASE_URL + '/licenses/hub';
6+
7+
class HubCE {
8+
9+
constructor(form, feedbackData, submitData, searchParams) {
10+
this._form = form;
11+
this._feedbackData = feedbackData;
12+
this._submitData = submitData;
13+
this._searchParams = searchParams;
14+
this._submitData.hubId = searchParams.get('hubId');
15+
16+
// continue after email verified:
17+
if (searchParams.get('verifiedEmail')) {
18+
feedbackData.currentStep = 2;
19+
feedbackData.success = true;
20+
this.getHubLicense();
21+
}
22+
}
23+
24+
validateEmail() {
25+
if (!$(this._form)[0].checkValidity()) {
26+
$(this._form).find(':input').addClass('show-invalid');
27+
this._feedbackData.errorMessage = 'Please fill in all required fields.';
28+
return;
29+
}
30+
this.onValidationSucceeded();
31+
}
32+
33+
onValidationFailed(error) {
34+
this._feedbackData.inProgress = false;
35+
this._feedbackData.errorMessage = error;
36+
}
37+
38+
onValidationSucceeded() {
39+
this._feedbackData.currentStep++;
40+
this._feedbackData.inProgress = false;
41+
this._feedbackData.errorMessage = '';
42+
}
43+
44+
sendConfirmationEmail() {
45+
if (!$(this._form)[0].checkValidity()) {
46+
$(this._form).find(':input').addClass('show-invalid');
47+
this._feedbackData.errorMessage = 'Please fill in all required fields.';
48+
return;
49+
}
50+
51+
this._feedbackData.success = false;
52+
this._feedbackData.inProgress = true;
53+
this._feedbackData.errorMessage = '';
54+
$.ajax({
55+
url: VERIFY_EMAIL_URL,
56+
type: 'POST',
57+
data: {
58+
email: this._submitData.email,
59+
hubId: this._submitData.hubId,
60+
verifyCaptcha: this._submitData.captcha,
61+
verifyEmail: this._submitData.email,
62+
verifyTarget: 'registerhubce'
63+
}
64+
}).done(_ => {
65+
this.onRequestSucceeded();
66+
if (this._submitData.acceptNewsletter) {
67+
subscribeToNewsletter(this._submitData.email, 7); // FIXME move to backend
68+
}
69+
}).fail(xhr => {
70+
this.onRequestFailed(xhr.responseJSON?.message || 'Sending confirmation email failed.');
71+
});
72+
}
73+
74+
getHubLicense() {
75+
$.ajax({
76+
url: GET_LICENSE_URL,
77+
type: 'GET',
78+
data: {
79+
hubId: this._submitData.hubId
80+
}
81+
}).done(response => {
82+
this._feedbackData.licenseText = response;
83+
}).fail(xhr => {
84+
this.onRequestFailed(xhr.responseJSON?.message || 'Fetching license failed.');
85+
});
86+
}
87+
88+
onRequestFailed(error) {
89+
this._feedbackData.inProgress = false;
90+
this._feedbackData.errorMessage = error;
91+
}
92+
93+
onRequestSucceeded() {
94+
this._feedbackData.currentStep++;
95+
this._feedbackData.inProgress = false;
96+
this._feedbackData.errorMessage = '';
97+
}
98+
99+
}

content/hub-register.de.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
title: "Cryptomator Hub: Registrieren"
3+
url: "/de/hub/register"
4+
type: "hub-register"
5+
---

content/hub-register.en.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
title: "Cryptomator Hub: Register"
3+
url: "/hub/register"
4+
type: "hub-register"
5+
---

i18n/en.yaml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,35 @@
659659
- id: hub_demo_contact_us_button
660660
translation: "Contact Us"
661661

662+
# Hub CE Registration
663+
- id: hub_ce_registration_description
664+
translation: "Register your Cryptomator Hub instance for a free Community Edition license."
665+
666+
- id: hub_ce_registration_steps_title
667+
translation: "Step <span x-text=\"feedbackData.currentStep + 1\"></span> of <span x-text=\"steps.length\"></span>"
668+
- id: hub_ce_registration_steps_next
669+
translation: "Next"
670+
671+
- id: hub_ce_registration_step_1_nav_title
672+
translation: "Email Address"
673+
- id: hub_ce_registration_step_1_title
674+
translation: "What's your email address?"
675+
- id: hub_ce_registration_step_1_email_placeholder
676+
translation: "Email address"
677+
678+
- id: hub_ce_registration_step_2_confirmation_nav_title
679+
translation: "Confirmation"
680+
- id: hub_ce_registration_step_2_confirmation_title
681+
translation: "Please confirm your email address"
682+
- id: hub_ce_registration_step_2_instructions
683+
translation: "We are going to send a confirmation email to <span x-text=\"submitData.email\"></span>"
684+
685+
- id: hub_ce_registration_step_3_license_nav_title
686+
translation: "License Key"
687+
- id: hub_ce_registration_step_3_license_title
688+
translation: "Your Community Edition License Key"
689+
690+
662691
# Hub Managed
663692
- id: hub_managed_description
664693
translation: "Request access to a managed instance of Cryptomator Hub and get your team on board with client-side encryption for your cloud storage."

layouts/hub-register/single.html

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
{{ define "preloads" }}
2+
{{ partial "altcha-css.html" . }}
3+
{{ end }}
4+
{{ define "main" }}
5+
<section x-data="{steps: ['{{ i18n "hub_ce_registration_step_1_nav_title" }}', '{{ i18n "hub_ce_registration_step_2_confirmation_nav_title" }}', '{{ i18n "hub_ce_registration_step_3_license_nav_title" }}'], feedbackData: {currentStep: 0, success: false, inProgress: false, errorMessage: '', licenseText: null}, submitData: {captcha: null, hubId: '', email: '', acceptNewsletter: false}, acceptTerms: false, hubCE: null, captchaState: null}" x-init="hubCE = new HubCE($refs.form, feedbackData, submitData, new URLSearchParams(location.search))" class="container py-12">
6+
<header class="mb-6">
7+
<h1 class="font-h1 mb-8">{{ .Title }}</h1>
8+
<p class="lead">{{ i18n "hub_ce_registration_description" }}</p>
9+
</header>
10+
11+
<form x-ref="form" @submit.prevent="hubCE.sendConfirmationEmail(); $refs.captcha.reset()">
12+
<section class="white-box md:min-h-110 px-4 py-5 md:p-6 md:grid md:grid-cols-3 md:gap-6">
13+
<header class="mb-8 md:col-span-1 md:mt-4">
14+
<nav class="flex items-center justify-center gap-6" aria-label="Progress">
15+
<p class="font-p text-sm text-gray-500 md:hidden">
16+
{{ i18n "hub_ce_registration_steps_title" | safeHTML }}
17+
</p>
18+
<ol role="list" class="flex items-center gap-3 md:flex-col md:items-start md:gap-6">
19+
<template x-for="(step, index) in steps" :key="index">
20+
<li>
21+
<!-- Complete Step -->
22+
<template x-if="index < feedbackData.currentStep">
23+
<a href="#" class="group" @click.prevent="feedbackData.currentStep = index" :data-umami-event="`hub-managed-nav-step-${index + 1}`">
24+
<span class="flex items-center gap-3">
25+
<div class="relative flex w-5 h-5 shrink-0 items-center justify-center">
26+
<svg class="w-full h-full text-primary group-hover:text-secondary" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
27+
<path fill-rule="evenodd" d="M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16Zm3.857-9.809a.75.75 0 0 0-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 1 0-1.06 1.061l2.5 2.5a.75.75 0 0 0 1.137-.089l4-5.5Z" clip-rule="evenodd" />
28+
</svg>
29+
</div>
30+
<p class="hidden md:block font-p text-sm text-gray-500 group-hover:text-gray-900" x-text="step"></p>
31+
</span>
32+
</a>
33+
</template>
34+
35+
<!-- Current Step -->
36+
<template x-if="index === feedbackData.currentStep">
37+
<div class="flex items-center gap-3" aria-current="step">
38+
<div class="relative flex w-5 h-5 shrink-0 items-center justify-center" aria-hidden="true">
39+
<span class="absolute w-4 h-4 rounded-full bg-primary-l2"></span>
40+
<span class="relative block w-2 h-2 rounded-full bg-primary"></span>
41+
</div>
42+
<p class="hidden md:block font-p text-sm font-medium text-primary" x-text="step"></p>
43+
</div>
44+
</template>
45+
46+
<!-- Upcoming Step -->
47+
<template x-if="index > feedbackData.currentStep">
48+
<div class="flex items-center gap-3">
49+
<div class="relative flex w-5 h-5 shrink-0 items-center justify-center" aria-hidden="true">
50+
<div class="w-2 h-2 rounded-full bg-gray-300"></div>
51+
</div>
52+
<p class="hidden md:block font-p text-sm text-gray-500" x-text="step"></p>
53+
</div>
54+
</template>
55+
</li>
56+
</template>
57+
</ol>
58+
</nav>
59+
</header>
60+
61+
<!-- Step 1: Email Address -->
62+
<template x-if="feedbackData.currentStep == 0">
63+
<div class="md:col-span-2 grid grid-cols-6 gap-6">
64+
<div class="flex flex-col col-span-6 lg:col-span-4">
65+
<p class="hidden md:block font-p text-sm text-gray-500 mb-2">
66+
{{ i18n "hub_managed_steps_title" | safeHTML }}
67+
</p>
68+
<h2 class="font-h2 mb-6">
69+
{{ i18n "hub_ce_registration_step_1_title" }}
70+
</h2>
71+
<input type="email" id="email" class="block input-box w-full mb-8" placeholder="{{ i18n "hub_ce_registration_step_1_email_placeholder" }}" x-init="$el.focus()" x-model="submitData.email" @blur="$el.classList.add('show-invalid')" required>
72+
<div class="mt-auto">
73+
<p :class="{'hidden': !feedbackData.errorMessage}" class="text-sm text-red-600 mb-2" x-text="feedbackData.errorMessage"></p>
74+
<button :disabled="feedbackData.inProgress || !submitData.hubId" @click.prevent="hubCE.validateEmail()" class="btn btn-primary w-full md:w-64" data-umami-event="hub-ce-registration-step-1">
75+
<i :class="{'fa-chevron-right': !feedbackData.inProgress, 'fa-spinner fa-spin': feedbackData.inProgress}" class="fa-solid" aria-hidden="true"></i>
76+
{{ i18n "hub_ce_registration_steps_next" }}
77+
</button>
78+
</div>
79+
</div>
80+
</div>
81+
</template>
82+
83+
<!-- Step 2: Confirmation -->
84+
<template x-if="feedbackData.currentStep == 1">
85+
<div class="md:col-span-2 grid grid-cols-6 gap-6">
86+
<div class="flex flex-col col-span-6 lg:col-span-4">
87+
<p class="hidden md:block font-p text-sm text-gray-500 mb-2">
88+
{{ i18n "hub_managed_steps_title" | safeHTML }}
89+
</p>
90+
<h2 class="font-h2 mb-6">
91+
{{ i18n "hub_ce_registration_step_2_confirmation_title" }}
92+
</h2>
93+
<p class="font-p mb-6">{{ i18n "hub_ce_registration_step_2_instructions" | safeHTML }}</p>
94+
<p class="font-p text-sm mb-2">
95+
{{ partial "checkbox.html" (dict "context" . "alpineVariable" "acceptTerms" "label" (i18n "accept_hub_managed_terms_and_privacy" | safeHTML)) }}
96+
</p>
97+
<p class="font-p text-sm mb-2">
98+
{{ partial "checkbox.html" (dict "context" . "alpineVariable" "submitData.acceptNewsletter" "label" (i18n "accept_hub_newsletter_optional")) }}
99+
</p>
100+
<div class="mt-auto">
101+
<p :class="{'hidden': !feedbackData.errorMessage}" class="text-sm text-red-600 mb-2" x-text="feedbackData.errorMessage"></p>
102+
<button :disabled="feedbackData.inProgress || !acceptTerms || captchaState == 'verifying'" type="submit" class="btn btn-primary w-full md:w-64" data-umami-event="hub-managed-form" x-cloak>
103+
<i :class="{'fa-paper-plane': !feedbackData.inProgress, 'fa-spinner fa-spin': feedbackData.inProgress}" class="fa-solid" aria-hidden="true"></i>
104+
{{ i18n "hub_managed_step_4_submit" }}
105+
</button>
106+
{{ $challengeUrl := printf "%s/connect/email/challenge" .Site.Params.apiBaseUrl }}
107+
{{ partial "captcha.html" (dict "challengeUrl" $challengeUrl "captchaPayload" "submitData.captcha" "captchaState" "captchaState") }}
108+
</div>
109+
</div>
110+
</div>
111+
</template>
112+
113+
<!-- Step 3: License Key (loading...) -->
114+
<template x-if="feedbackData.currentStep == 2 &amp;&amp; !feedbackData.success">
115+
<div class="md:col-span-2 grid grid-cols-6 gap-6">
116+
<div class="flex flex-col col-span-6 lg:col-span-4">
117+
<p class="hidden md:block font-p text-sm text-gray-500 mb-2">
118+
{{ i18n "hub_managed_steps_title" | safeHTML }}
119+
</p>
120+
<h2 class="font-h2 mb-6">
121+
{{ i18n "hub_ce_registration_step_3_license_title" }}
122+
</h2>
123+
<p>Todo: Please check your e-mails.</p>
124+
</div>
125+
</div>
126+
</template>
127+
128+
<!-- Step 3: License Key (success) -->
129+
<template x-if="feedbackData.currentStep == 2 &amp;&amp; feedbackData.success">
130+
<div class="md:col-span-2 grid grid-cols-6 gap-6">
131+
<div class="flex flex-col col-span-6 lg:col-span-4">
132+
<p class="hidden md:block font-p text-sm text-gray-500 mb-2">
133+
{{ i18n "hub_managed_steps_title" | safeHTML }}
134+
</p>
135+
<h2 class="font-h2 mb-6">
136+
{{ i18n "hub_ce_registration_step_3_license_title" }}
137+
</h2>
138+
<p>Todo: return URL</p>
139+
<textarea class="block input-box w-full h-48 mb-8" x-text="feedbackData.licenseText" readonly></textarea>
140+
141+
<div class="mt-auto">
142+
<p :class="{'hidden': !feedbackData.errorMessage}" class="text-sm text-red-600 mb-2" x-text="feedbackData.errorMessage"></p>
143+
<button class="btn btn-primary w-full md:w-64" data-umami-event="hub-ce-registration-step-1">
144+
<i :class="{'fa-chevron-right': !feedbackData.inProgress, 'fa-spinner fa-spin': feedbackData.inProgress}" class="fa-solid" aria-hidden="true"></i>
145+
Todo: Return to Hub
146+
</button>
147+
</div>
148+
</div>
149+
</div>
150+
</template>
151+
</section>
152+
</form>
153+
</section>
154+
{{ end }}
155+
{{ define "script" }}
156+
{{ if hugo.IsDevelopment }}
157+
{{ $newsletterJs := resources.Get "js/newsletter.js" }}
158+
<script type="text/javascript" src="{{ $newsletterJs.RelPermalink }}" defer></script>
159+
{{ $hubCeJs := resources.Get "js/hubce.js" }}
160+
<script type="text/javascript" src="{{ $hubCeJs.RelPermalink }}" defer></script>
161+
{{ $altchaJs := resources.Get "js/altcha/altcha.js" }}
162+
<script type="module" src="{{ $altchaJs.RelPermalink }}" defer></script>
163+
{{ $altchaWorkerJs := resources.Get "js/altcha/worker.js" }}
164+
<script type="module" src="{{ $altchaWorkerJs.RelPermalink }}" defer></script>
165+
{{ else }}
166+
{{ $newsletterJs := resources.Get "js/newsletter.js" | minify | fingerprint }}
167+
<script type="text/javascript" src="{{ $newsletterJs.RelPermalink }}" integrity="{{ $newsletterJs.Data.Integrity }}" defer></script>
168+
{{ $hubCeJs := resources.Get "js/hubce.js" | minify | fingerprint }}
169+
<script type="text/javascript" src="{{ $hubCeJs.RelPermalink }}" integrity="{{ $hubCeJs.Data.Integrity }}" defer></script>
170+
{{ $altchaJs := resources.Get "js/altcha/altcha.js" | minify | fingerprint }}
171+
<script type="module" src="{{ $altchaJs.RelPermalink }}" integrity="{{ $altchaJs.Data.Integrity }}" defer></script>
172+
{{ $altchaWorkerJs := resources.Get "js/altcha/worker.js" }}
173+
<script type="module" src="{{ $altchaWorkerJs.RelPermalink }}" integrity="{{ $altchaWorkerJs.Data.Integrity }}" defer></script>
174+
{{ end }}
175+
{{ end }}

0 commit comments

Comments
 (0)