~jan0sch/smederee

Showing details for patch 53bc774bbbb7d4e650bd85ebbf04e5162113b639.
2022-07-13 (Wed), 1:55 PM - Jens Grassel - 53bc774bbbb7d4e650bd85ebbf04e5162113b639

Landing page: more i18n, css, route protection

Summary of changes
10 files modified with 207 lines added and 114 lines removed
  • modules/hub/src/main/resources/assets/css/main.css with 76 added and 20 removed lines
  • modules/hub/src/main/resources/messages_en.properties with 6 added and 0 removed lines
  • modules/hub/src/main/scala/de/smederee/hub/AuthenticationRoutes.scala with 1 added and 1 removed lines
  • modules/hub/src/main/scala/de/smederee/hub/HubServer.scala with 1 added and 1 removed lines
  • modules/hub/src/main/scala/de/smederee/hub/SignupRoutes.scala with 13 added and 0 removed lines
  • modules/hub/src/main/twirl/de/smederee/hub/views/index.scala.html with 7 added and 8 removed lines
  • modules/hub/src/main/twirl/de/smederee/hub/views/login.scala.html with 35 added and 29 removed lines
  • modules/hub/src/main/twirl/de/smederee/hub/views/main.scala.html with 7 added and 3 removed lines
  • modules/hub/src/main/twirl/de/smederee/hub/views/navbar.scala.html with 16 added and 13 removed lines
  • modules/hub/src/main/twirl/de/smederee/hub/views/signup.scala.html with 45 added and 39 removed lines
diff -rN -u old-smederee/modules/hub/src/main/resources/assets/css/main.css new-smederee/modules/hub/src/main/resources/assets/css/main.css
--- old-smederee/modules/hub/src/main/resources/assets/css/main.css	2025-02-03 05:05:52.387339381 +0000
+++ new-smederee/modules/hub/src/main/resources/assets/css/main.css	2025-02-03 05:05:52.391339387 +0000
@@ -1,31 +1,87 @@
 main {
-	margin-left: 1em;
-	margin-right: 1em;
+  margin-left: 1em;
+  margin-right: 1em;
 }
 
-.button-success,
-.button-error,
-.button-warning,
-.button-secondary {
-	color: white;
+.pure-button {
+  background-color: #1f8dd6;
+  color: white;
+  padding: 0.5em 2em;
+  border-radius: 5px;
 }
 
-.button-success {
-	background: rgb(28, 184, 65);
-	/* this is a green */
+a.pure-button-primary {
+  background: white;
+  color: #1f8dd6;
+  border-radius: 5px;
+  font-size: 120%;
 }
 
-.button-error {
-	background: rgb(202, 60, 60);
-	/* this is a maroon */
+.home-menu {
+  padding: 0.5em;
+  text-align: center;
+  box-shadow: 0 1px 1px rgba(0,0,0, 0.10);
+}
+.home-menu {
+  background: #2d3e50;
+}
+
+.home-menu .pure-menu-heading {
+  color: white;
+  font-weight: 400;
+  font-size: 120%;
 }
 
-.button-warning {
-	background: rgb(223, 117, 20);
-	/* this is an orange */
+.home-menu .pure-menu-selected a {
+  color: white;
 }
 
-.button-secondary {
-	background: rgb(66, 184, 221);
-	/* this is a light blue */
+.home-menu a {
+  color: #6FBEF3;
+}
+.home-menu li a:hover,
+.home-menu li a:focus {
+  background: none;
+  border: none;
+  color: #AECFE5;
+}
+
+@media (min-width: 48em) {
+
+  /* We increase the body font size */
+  body {
+	font-size: 16px;
+  }
+
+  /* We can align the menu header to the left, but float the
+  menu items to the right. */
+  .home-menu {
+	text-align: left;
+  }
+  .home-menu ul {
+	float: right;
+  }
+
+  /* We increase the height of the splash-container */
+  /*    .splash-container {
+  height: 500px;
+  }*/
+
+  /* We decrease the width of the .splash, since we have more width
+  to work with */
+  .splash {
+	width: 50%;
+	height: 50%;
+  }
+
+  .splash-head {
+	font-size: 250%;
+  }
+
+
+  /* We remove the border-separator assigned to .l-box-lrg */
+  .l-box-lrg {
+	border: none;
+  }
+
 }
diff -rN -u old-smederee/modules/hub/src/main/resources/messages_en.properties new-smederee/modules/hub/src/main/resources/messages_en.properties
--- old-smederee/modules/hub/src/main/resources/messages_en.properties	2025-02-03 05:05:52.387339381 +0000
+++ new-smederee/modules/hub/src/main/resources/messages_en.properties	2025-02-03 05:05:52.391339387 +0000
@@ -13,6 +13,11 @@
 errors.forbidden.title = 403 - Forbidden
 
 # Forms
+form.login.button.submit=Login
+form.login.password=Password
+form.login.password.placeholder=Please enter your password here.
+form.login.username=Username
+form.login.username.placeholder=Please enter your username.
 form.signup.button.submit=Sign up for an account
 form.signup.email=Email address
 form.signup.email.help=Please enter your email address.
@@ -29,6 +34,7 @@
 global.imprint=Imprint
 global.login=Login
 global.logout=Logout
+global.navbar.top.logo=Smederee
 global.privacy=Privacy Policy
 global.signup=Sign Up
 global.terms.of.use=Terms of Use
diff -rN -u old-smederee/modules/hub/src/main/scala/de/smederee/hub/AuthenticationRoutes.scala new-smederee/modules/hub/src/main/scala/de/smederee/hub/AuthenticationRoutes.scala
--- old-smederee/modules/hub/src/main/scala/de/smederee/hub/AuthenticationRoutes.scala	2025-02-03 05:05:52.387339381 +0000
+++ new-smederee/modules/hub/src/main/scala/de/smederee/hub/AuthenticationRoutes.scala	2025-02-03 05:05:52.391339387 +0000
@@ -147,7 +147,7 @@
   }
 
   private val parseLoginFormForLoggedInUsers: AuthedRoutes[Account, F] = AuthedRoutes.of {
-    case ar @ GET -> Root / "login" as _ =>
+    case ar @ POST -> Root / "login" as _ =>
       SeeOther.apply(Location(Uri(path = Uri.Path.Root))) // Redirect already logged in users.
   }
 
diff -rN -u old-smederee/modules/hub/src/main/scala/de/smederee/hub/HubServer.scala new-smederee/modules/hub/src/main/scala/de/smederee/hub/HubServer.scala
--- old-smederee/modules/hub/src/main/scala/de/smederee/hub/HubServer.scala	2025-02-03 05:05:52.387339381 +0000
+++ new-smederee/modules/hub/src/main/scala/de/smederee/hub/HubServer.scala	2025-02-03 05:05:52.391339387 +0000
@@ -80,7 +80,7 @@
       signUpRoutes = new SignupRoutes[IO](configuration.service.signup, signUpRepo)
       landingPages = new LandingPageRoutes[IO]()
       protectedRoutesWithFallThrough = authenticationWithFallThrough(
-        authenticationRoutes.protectedRoutes <+> landingPages.protectedRoutes
+        authenticationRoutes.protectedRoutes <+> signUpRoutes.protectedRoutes <+> landingPages.protectedRoutes
       )
       globalRoutes = Router(
         Constants.assetsPath.path.toAbsolute.toString -> assetsRoutes,
diff -rN -u old-smederee/modules/hub/src/main/scala/de/smederee/hub/SignupRoutes.scala new-smederee/modules/hub/src/main/scala/de/smederee/hub/SignupRoutes.scala
--- old-smederee/modules/hub/src/main/scala/de/smederee/hub/SignupRoutes.scala	2025-02-03 05:05:52.387339381 +0000
+++ new-smederee/modules/hub/src/main/scala/de/smederee/hub/SignupRoutes.scala	2025-02-03 05:05:52.391339387 +0000
@@ -22,6 +22,7 @@
 import org.http4s.FormDataDecoder._
 import org.http4s._
 import org.http4s.dsl.Http4sDsl
+import org.http4s.headers.Location
 import org.http4s.implicits._
 import org.http4s.twirl.TwirlInstances._
 import org.slf4j.LoggerFactory
@@ -117,6 +118,11 @@
     }
   }
 
+  private val parseSignUpFormForLoggedInUsers: AuthedRoutes[Account, F] = AuthedRoutes.of {
+    case ar @ POST -> Root / "signup" as _ =>
+      SeeOther.apply(Location(Uri(path = Uri.Path.Root))) // Redirect already logged in users.
+  }
+
   // Render the signup form.
   private val showSignUpForm = HttpRoutes.of[F] { case request @ GET -> Root / "signup" =>
     for {
@@ -125,5 +131,12 @@
     } yield resp
   }
 
+  private val showSignUpFormForLoggedInUsers: AuthedRoutes[Account, F] = AuthedRoutes.of {
+    case ar @ GET -> Root / "signup" as _ =>
+      SeeOther.apply(Location(Uri(path = Uri.Path.Root))) // Redirect already logged in users.
+  }
+
+  val protectedRoutes = showSignUpFormForLoggedInUsers <+> parseSignUpFormForLoggedInUsers
+
   val routes = showSignUpForm <+> parseSignUpForm
 }
diff -rN -u old-smederee/modules/hub/src/main/twirl/de/smederee/hub/views/index.scala.html new-smederee/modules/hub/src/main/twirl/de/smederee/hub/views/index.scala.html
--- old-smederee/modules/hub/src/main/twirl/de/smederee/hub/views/index.scala.html	2025-02-03 05:05:52.387339381 +0000
+++ new-smederee/modules/hub/src/main/twirl/de/smederee/hub/views/index.scala.html	2025-02-03 05:05:52.391339387 +0000
@@ -17,12 +17,11 @@
     @customHeaders
   </head>
   <body>
-    <navbar class="header">
-      <div class="home-menu pure-menu pure-menu-horizontal pure-menu-fixed">
-        <a class="pure-menu-heading" href="">@Messages("landingpage.index.menu.heading")</a>
+    <navbar class="header navbar" id="navbar-top">
+      <nav class="home-menu pure-menu pure-menu-horizontal pure-menu-fixed">
+        <a class="logo pure-menu-heading" href="@pathPrefix/">@Messages("global.navbar.top.logo")</a>
 
         <ul class="pure-menu-list">
-          <li class="pure-menu-item pure-menu-selected"><a href="#" class="pure-menu-link">Home</a></li>
           @if(user.nonEmpty) {
             <li class="pure-menu-item">
               <form action="/logout" method="POST" accept-charset="UTF-8" class="inline">
@@ -35,7 +34,7 @@
             <li class="pure-menu-item"><a href="/signup" class="pure-menu-link">@Messages("global.signup")</a></li>
           }
         </ul>
-      </div>
+      </nav>
     </navbar>
 
     <div class="splash-container">
@@ -73,7 +72,7 @@
 
       <div class="ribbon l-box-lrg pure-g">
         <div class="l-box-lrg is-center pure-u-1 pure-u-md-1-2 pure-u-lg-2-5">
-          <img width="300" alt="File Icons" class="pure-img-responsive" src="@pathPrefix/assets/img/malcolm-lightbody-gPRvTP0sZ2M-unsplash.jpg">
+          <img width="300" alt="A smithy in which a smith is working on an anvil." class="pure-img-responsive" src="@pathPrefix/assets/img/malcolm-lightbody-gPRvTP0sZ2M-unsplash.jpg">
         </div>
         <div class="pure-u-1 pure-u-md-1-2 pure-u-lg-3-5">
           <h2 class="content-head content-head-ribbon">@Messages("landingpage.index.ribbon.title")</h2>
@@ -88,7 +87,7 @@
           <div class="l-box-lrg pure-u-1 pure-u-md-2-5">
             <div class="signup-form">
               <form action="@createFullPath(pathPrefix)(actionSignup)" class="pure-form pure-form-stacked" method="POST" accept-charset="UTF-8" class="pure-form pure-form-aligned" autocomplete="on">
-                <fieldset id="signup-data">
+                <fieldset id="signup-data" @{user.map(_ => "disabled")}>
                   <div class="pure-control-group">
                     <label for="@{fieldName}">@Messages("form.signup.username")</label>
                     <input class="pure-input-1-2" id="@{fieldName}" name="@{fieldName}" placeholder="@Messages("form.signup.username.placeholder")" maxlength="31" required="" type="text" value="" autocomplete="username">
@@ -103,7 +102,7 @@
                   </div>
                   @csrfToken(csrf)
                   <div class="pure-controls">
-                    <button type="submit" class="pure-button button-success">@Messages("form.signup.button.submit")</button>
+                    <button type="submit" class="pure-button" @{user.map(_ => "disabled")}>@Messages("form.signup.button.submit")</button>
                   </div>
                 </fieldset>
               </form>
diff -rN -u old-smederee/modules/hub/src/main/twirl/de/smederee/hub/views/login.scala.html new-smederee/modules/hub/src/main/twirl/de/smederee/hub/views/login.scala.html
--- old-smederee/modules/hub/src/main/twirl/de/smederee/hub/views/login.scala.html	2025-02-03 05:05:52.387339381 +0000
+++ new-smederee/modules/hub/src/main/twirl/de/smederee/hub/views/login.scala.html	2025-02-03 05:05:52.391339387 +0000
@@ -2,37 +2,43 @@
 
 @(lang: LanguageCode = LanguageCode("en"), pathPrefix: Option[Uri] = None)(action: Uri, csrf: Option[CsrfToken] = None, title: Option[String] = None)(formData: Map[String, String] = Map.empty, formErrors: FormErrors = FormErrors.empty)
 @main(lang, pathPrefix)()(csrf, title) {
-  <div class="pure-u-1-1 pure-u-md-1-1 login">
-    <div class="form-errors">
-      @formErrors.get(fieldGlobal).map { es =>
+@defining(lang.toLocale) { implicit locale =>
+<div class="content">
+  <div class="pure-g">
+    <div class="pure-u-1-1 pure-u-md-1-1 login">
+      <div class="form-errors">
+        @formErrors.get(fieldGlobal).map { es =>
         @for(error <- es) {
-          <p class="alert alert-danger">
-            <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
-            <span class="sr-only">Fehler:</span>
-            @error
-          </p>
+        <p class="alert alert-danger">
+        <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
+        <span class="sr-only">Fehler:</span>
+        @error
+        </p>
         }
-      }
-    </div>
-    <div class="login-form">
-      <form action="@createFullPath(pathPrefix)(action)" method="POST" accept-charset="UTF-8" class="pure-form pure-form-aligned">
-        <fieldset id="login-data">
-          <div class="pure-control-group">
-            <label for="@{fieldName}">Username</label>
-            <input class="pure-input-1-2" id="@{fieldName}" name="@{fieldName}" placeholder="Please enter your username." maxlength="31" required="" type="text" value="@{formData.get(fieldName)}">
-            @renderFormErrors(fieldName, formErrors)
-          </div>
-          <div class="pure-control-group">
-            <label for="@{fieldPassword}">Password</label>
-            <input class="pure-input-1-2" id="@{fieldPassword}" name="@{fieldPassword}" placeholder="Please enter your password here." maxlength="128" required="" type="password" value="">
-            @renderFormErrors(fieldPassword, formErrors)
-          </div>
-          @csrfToken(csrf)
-          <div class="pure-controls">
-            <button type="submit" class="pure-button button-success">Login</button>
-          </div>
-        </fieldset>
-      </form>
+        }
+      </div>
+      <div class="login-form">
+        <form action="@createFullPath(pathPrefix)(action)" method="POST" accept-charset="UTF-8" class="pure-form pure-form-aligned" autocomplete="on">
+          <fieldset id="login-data">
+            <div class="pure-control-group">
+              <label for="@{fieldName}">@Messages("form.login.username")</label>
+              <input class="pure-input-1-2" id="@{fieldName}" name="@{fieldName}" placeholder="@Messages("form.login.username.placeholder")" maxlength="31" required="" type="text" value="@{formData.get(fieldName)}" autocomplete="username">
+              @renderFormErrors(fieldName, formErrors)
+            </div>
+            <div class="pure-control-group">
+              <label for="@{fieldPassword}">@Messages("form.login.password")</label>
+              <input class="pure-input-1-2" id="@{fieldPassword}" name="@{fieldPassword}" placeholder="@Messages("form.login.password.placeholder")" maxlength="128" required="" type="password" value="" autocomplete="password">
+              @renderFormErrors(fieldPassword, formErrors)
+            </div>
+            @csrfToken(csrf)
+            <div class="pure-controls">
+              <button type="submit" class="pure-button">@Messages("form.login.button.submit")</button>
+            </div>
+          </fieldset>
+        </form>
+      </div>
     </div>
   </div>
+</div>
+}
 }
diff -rN -u old-smederee/modules/hub/src/main/twirl/de/smederee/hub/views/main.scala.html new-smederee/modules/hub/src/main/twirl/de/smederee/hub/views/main.scala.html
--- old-smederee/modules/hub/src/main/twirl/de/smederee/hub/views/main.scala.html	2025-02-03 05:05:52.387339381 +0000
+++ new-smederee/modules/hub/src/main/twirl/de/smederee/hub/views/main.scala.html	2025-02-03 05:05:52.391339387 +0000
@@ -1,4 +1,5 @@
 @(lang: LanguageCode = LanguageCode("en"), pathPrefix: Option[Uri] = None, tags: MetaTags = MetaTags.empty)(customFooters: Html = Html(""), customHeaders: Html = Html(""))(csrf: Option[CsrfToken] = None, title: Option[String] = None, user: Option[Account] = None)(content: Html)
+@defining(lang.toLocale) { implicit locale =>
 <!DOCTYPE html>
 <html lang="@lang">
 <head>
@@ -12,8 +13,11 @@
   @customHeaders
 </head>
 <body>
-  <navbar class="navbar" id="navbar-top">@navbar(lang, pathPrefix)(csrf, user)</navbar>
-  <main class="pure-g">@content</main>
-  <footer class="footer">@customFooters</footer>
+  <navbar class="header navbar" id="navbar-top">@navbar(lang, pathPrefix)(csrf, user)</navbar>
+  <main class="content-wrapper">
+    @content
+    <footer class="footer">@customFooters</footer>
+  </main>
 </body>
 </html>
+}
diff -rN -u old-smederee/modules/hub/src/main/twirl/de/smederee/hub/views/navbar.scala.html new-smederee/modules/hub/src/main/twirl/de/smederee/hub/views/navbar.scala.html
--- old-smederee/modules/hub/src/main/twirl/de/smederee/hub/views/navbar.scala.html	2025-02-03 05:05:52.391339387 +0000
+++ new-smederee/modules/hub/src/main/twirl/de/smederee/hub/views/navbar.scala.html	2025-02-03 05:05:52.391339387 +0000
@@ -1,17 +1,20 @@
 @(lang: LanguageCode, pathPrefix: Option[Uri])(csrf: Option[CsrfToken] = None, user: Option[Account] = None)
-<nav class="home-menu pure-menu pure-menu-horizontal pure-menu-fixed">
-  <a class="logo pure-menu-heading" href="#">Smederee</a>
+@defining(lang.toLocale) { implicit locale =>
+<nav class="home-menu pure-menu pure-menu-horizontal">
+  <a class="logo pure-menu-heading" href="@pathPrefix/">@Messages("global.navbar.top.logo")</a>
+
   <ul class="pure-menu-list">
-    <li class=""><a class="pure-menu-link" href="#">...</a></li>
-  @if(user.nonEmpty) {
-    <li class="pure-menu-item">
-      <form action="/logout" method="POST" accept-charset="UTF-8" class="inline">
-        @csrfToken(csrf)
-        <button class="pure-menu-link" type="submit">Logout</button>
-      </form>
-    </li>
-  } else {
-    <li class="pure-menu-item"><a class="btn right" href="">Login</a></li>
-  }
+    @if(user.nonEmpty) {
+      <li class="pure-menu-item">
+        <form action="/logout" method="POST" accept-charset="UTF-8" class="inline">
+          @csrfToken(csrf)
+          <button class="pure-menu-link" type="submit">@Messages("global.logout")</button>
+        </form>
+      </li>
+    } else {
+      <li class="pure-menu-item"><a href="/login" class="pure-menu-link">@Messages("global.login")</a></li>
+      <li class="pure-menu-item"><a href="/signup" class="pure-menu-link">@Messages("global.signup")</a></li>
+    }
   </ul>
 </nav>
+}
diff -rN -u old-smederee/modules/hub/src/main/twirl/de/smederee/hub/views/signup.scala.html new-smederee/modules/hub/src/main/twirl/de/smederee/hub/views/signup.scala.html
--- old-smederee/modules/hub/src/main/twirl/de/smederee/hub/views/signup.scala.html	2025-02-03 05:05:52.391339387 +0000
+++ new-smederee/modules/hub/src/main/twirl/de/smederee/hub/views/signup.scala.html	2025-02-03 05:05:52.391339387 +0000
@@ -2,45 +2,51 @@
 
 @(lang: LanguageCode = LanguageCode("en"), pathPrefix: Option[Uri] = None)(action: Uri, csrf: Option[CsrfToken] = None, title: Option[String] = None)(formData: Map[String, String] = Map.empty, formErrors: FormErrors = FormErrors.empty)
 @main(lang, pathPrefix)()(csrf, title) {
-  <div class="pure-u-1-1 pure-u-md-1-1 signup">
-    <div class="form-errors">
-      @formErrors.get(fieldGlobal).map { es =>
-        @for(error <- es) {
-          <p class="alert alert-danger">
-            <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
-            <span class="sr-only">Fehler:</span>
-            @error
-          </p>
-        }
-      }
-    </div>
-    <div class="signup-form">
-      <form action="@createFullPath(pathPrefix)(action)" method="POST" accept-charset="UTF-8" class="pure-form pure-form-aligned" autocomplete="on">
-        <fieldset id="signup-data">
-          <div class="pure-control-group">
-            <label for="@{fieldName}">Username</label>
-            <input class="pure-input-1-2" id="@{fieldName}" name="@{fieldName}" placeholder="Please choose your username." maxlength="31" required="" type="text" value="@{formData.get(fieldName)}" autocomplete="username">
-            <small class="pure-form-message" id="@{fieldName}.help">A username is required because it will be used to group your projects and other stuff. It must be between 2 and 31 characters long and contain only lowercase alphanumeric characters and start with a character (letter).</small>
-            @renderFormErrors(fieldName, formErrors)
-          </div>
-          <div class="pure-control-group">
-            <label for="@{fieldEmail}">Email address</label>
-            <input class="pure-input-1-2" id="@{fieldEmail}" name="@{fieldEmail}" placeholder="some@@somewhere.org" maxlength="128" required="" type="email" value="@{formData.get(fieldEmail)}" autocomplete="email">
-            <small class="pure-form-message" id="@{fieldEmail}.help">Please enter your email address.</small>
-            @renderFormErrors(fieldEmail, formErrors)
-          </div>
-          <div class="pure-control-group">
-            <label for="@{fieldPassword}">Password</label>
-            <input class="pure-input-1-2" id="@{fieldPassword}" name="@{fieldPassword}" placeholder="Please choose a secure password!" maxlength="128" required="" type="password" value="" autocomplete="new-password">
-            <small class="pure-form-message" id="@{fieldPassword}.help">Your password must be at least 12 characters long.</small>
-            @renderFormErrors(fieldPassword, formErrors)
-          </div>
-          @csrfToken(csrf)
-          <div class="pure-controls">
-            <button type="submit" class="pure-button button-success">Sign up for an account</button>
-          </div>
-        </fieldset>
-      </form>
+@defining(lang.toLocale) { implicit locale =>
+  <div class="content">
+    <div class="pure-g">
+      <div class="pure-u-1-1 pure-u-md-1-1 signup">
+        <div class="form-errors">
+          @formErrors.get(fieldGlobal).map { es =>
+            @for(error <- es) {
+              <p class="alert alert-danger">
+                <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
+                <span class="sr-only">Fehler:</span>
+                @error
+              </p>
+            }
+          }
+        </div>
+        <div class="signup-form">
+          <form action="@createFullPath(pathPrefix)(action)" method="POST" accept-charset="UTF-8" class="pure-form pure-form-aligned" autocomplete="on">
+            <fieldset id="signup-data">
+              <div class="pure-control-group">
+                <label for="@{fieldName}">Username</label>
+                <input class="pure-input-1-2" id="@{fieldName}" name="@{fieldName}" placeholder="Please choose your username." maxlength="31" required="" type="text" value="@{formData.get(fieldName)}" autocomplete="username">
+                <small class="pure-form-message" id="@{fieldName}.help">A username is required because it will be used to group your projects and other stuff. It must be between 2 and 31 characters long and contain only lowercase alphanumeric characters and start with a character (letter).</small>
+                @renderFormErrors(fieldName, formErrors)
+              </div>
+              <div class="pure-control-group">
+                <label for="@{fieldEmail}">Email address</label>
+                <input class="pure-input-1-2" id="@{fieldEmail}" name="@{fieldEmail}" placeholder="some@@somewhere.org" maxlength="128" required="" type="email" value="@{formData.get(fieldEmail)}" autocomplete="email">
+                <small class="pure-form-message" id="@{fieldEmail}.help">Please enter your email address.</small>
+                @renderFormErrors(fieldEmail, formErrors)
+              </div>
+              <div class="pure-control-group">
+                <label for="@{fieldPassword}">Password</label>
+                <input class="pure-input-1-2" id="@{fieldPassword}" name="@{fieldPassword}" placeholder="Please choose a secure password!" maxlength="128" required="" type="password" value="" autocomplete="new-password">
+                <small class="pure-form-message" id="@{fieldPassword}.help">Your password must be at least 12 characters long.</small>
+                @renderFormErrors(fieldPassword, formErrors)
+              </div>
+              @csrfToken(csrf)
+              <div class="pure-controls">
+                <button type="submit" class="pure-button">@Messages("form.signup.button.submit")</button>
+              </div>
+            </fieldset>
+          </form>
+        </div>
+      </div>
     </div>
   </div>
 }
+}