Jihan Kim 2014-06-12
Project: improve validations for new project form
@d37c4bbac556628cd946a612c014be75394561de
app/assets/stylesheets/less/_page.less
--- app/assets/stylesheets/less/_page.less
+++ app/assets/stylesheets/less/_page.less
@@ -1405,6 +1405,7 @@
 //-- new project
 .form-wrap {
     &.new-project {
+        position:relative;
         width:700px;
         margin: 30px auto;
 
@@ -1439,6 +1440,10 @@
             background:#fafafa;
             .border-radius(10px);
         }
+
+        .popover {
+            max-width:144px;
+        }
     }
 }
 
app/assets/stylesheets/less/_yobiUI.less
--- app/assets/stylesheets/less/_yobiUI.less
+++ app/assets/stylesheets/less/_yobiUI.less
@@ -50,6 +50,10 @@
     .border-radius(2px);
     .box-shadow(-2px 2px 1px rgba(0,0,0,0.1));
 }
+.popover-content {
+    padding:9px 10px;
+    line-height:120%;
+}
 
 /** yobi.ui.Dropdown **/
 .dropdown-toggle {
app/views/project/create.scala.html
--- app/views/project/create.scala.html
+++ app/views/project/create.scala.html
@@ -28,7 +28,7 @@
 <div class="page-wrap-outer">
   <div class="project-page-wrap">
     <div class="form-wrap new-project">
-      <form action="@routes.ProjectApp.newProject()" method="post" name="newproject" class="frm-wrap">
+      <form id="newProjectForm" action="@routes.ProjectApp.newProject()" method="post" class="frm-wrap">
         <legend>
           @Messages("title.newProject")
           <span>
@@ -64,11 +64,9 @@
                     </option>
                 }
             </select>
-            <div class="n-alert" data-errType="owner">
-              <div class="orange-txt">
-                @if(newProjectForm.error("owner") != null) { <span class="warning">@Messages(newProjectForm.error("owner").message())</span> }
-              </div>
-            </div>
+            @if(newProjectForm.error("owner") != null) {
+              <span class="orange-text">@Messages(newProjectForm.error("owner").message())</span>
+            }
           </dd>
           <!-- // -->
 
@@ -81,13 +79,7 @@
           </dt>
           <dd>
             <input id="project-name" type="text" name="name" class="text" maxlength="250" pattern="[a-zA-Z0-9-]+"
-                   value="@newProjectForm.field("name").value" placeholder="@Messages("project.name.alert")"  >
-            <div class="n-alert" data-errType="name">
-              <div class="orange-txt">
-                @if(newProjectForm.error("name") != null) { <span class="warning">@Messages(newProjectForm.error("name").message())</span> }
-                <span class="msg wrongName" style="display: none;"></span>
-              </div>
-            </div>
+                   value="@newProjectForm.field("name").value" placeholder="@Messages("project.name.placeholder")"  >
           </dd>
           <!-- // -->
 
@@ -187,9 +179,10 @@
 
 @common.select2()
 <script type="text/javascript">
-    $(document).ready(function() {
+    $(function() {
         $yobi.loadModule("project.New", {
-            "sFormName" : "newproject"
+            "sFormId": "#newProjectForm",
+            "htError": @Html(newProjectForm.errorsAsJson.toString)
         });
     });
 </script>
app/views/project/importing.scala.html
--- app/views/project/importing.scala.html
+++ app/views/project/importing.scala.html
@@ -27,7 +27,7 @@
 <div class="page-wrap-outer">
   <div class="project-page-wrap">
     <div class="form-wrap new-project">
-      <form id="importGit" action="@routes.ImportApp.newProject()" method="post" name="newproject" class="frm-wrap">
+      <form id="importGit" action="@routes.ImportApp.newProject()" method="post" class="frm-wrap">
         <legend>
           @Messages("project.import.from.git")
             <span>
@@ -48,11 +48,6 @@
           </dt>
           <dd>
             <input id="url" type="text" name="url" class="text" placeholder="@Messages("project.git.url.alert")" value="@newProjectForm.field("url").value">
-            <div class="n-alert" data-errType="url">
-              <div class="orange-txt">
-                @if(newProjectForm.error("url") != null) { <span class="warning">@Messages(newProjectForm.error("url").message())</span> }
-              </div>
-            </div>
           </dd>
           <!-- // -->
           <!-- ProjectOwner -->
@@ -79,11 +74,9 @@
               </option>
               }
             </select>
-            <div class="n-alert" data-errType="owner">
-              <div class="orange-txt">
-                @if(newProjectForm.error("owner") != null) { <span class="warning">@Messages(newProjectForm.error("owner").message())</span> }
-              </div>
-            </div>
+            @if(newProjectForm.error("owner") != null) {
+              <span class="orange-text">@Messages(newProjectForm.error("owner").message())</span>
+            }
           </dd>
           <!-- // -->
 
@@ -97,12 +90,6 @@
           <dd>
             <input id="project-name" type="text" name="name" class="text" maxlength="250" pattern="[a-zA-Z0-9-]+"
                    value="@newProjectForm.field("name").value" placeholder="@Messages("project.name.alert")"  >
-            <div class="n-alert" data-errType="name">
-              <div class="orange-txt">
-                @if(newProjectForm.error("name") != null) { <span class="warning">@Messages(newProjectForm.error("name").message())</span> }
-                <span class="msg wrongName" style="display: none;"></span>
-              </div>
-            </div>
           </dd>
           <!-- // -->
 
@@ -188,9 +175,10 @@
 
 @common.select2()
 <script type="text/javascript">
-  $(document).ready(function(){
+  $(function() {
       $yobi.loadModule("project.New", {
-          "sFormName": "newproject"
+          "sFormId": "#importGit",
+          "htError": @Html(newProjectForm.errorsAsJson.toString)
       });
   });
 </script>
conf/messages
--- conf/messages
+++ conf/messages
@@ -470,9 +470,9 @@
 project.members = Project members
 project.members.addMember = Add new member ID.
 project.name = Project name
-project.name.alert = Characters that can be used in URL are allowed
+project.name.alert = Enter name in alphabet, number or hyphen
 project.name.duplicate = Same project name already exists.
-project.name.placeholder = Enter project name
+project.name.placeholder = Enter project name in alphabet, number or hyphen
 project.name.reserved.alert = You can''t use reserved names.
 project.name.rule = Naming rules
 project.new.agreement = I have read this user agreement and agree to all of its provisions.
conf/messages.ja
--- conf/messages.ja
+++ conf/messages.ja
@@ -361,10 +361,10 @@
 project.members = メンバー
 project.members.addMember = 追加するメンバーのIDを入力してください
 project.name = プロジェクト名
-project.name.alert = プロジェクト名はURLで使える文字(半角ローマ字,数字,-'ハイフン')だけ使えます
+project.name.alert = 半角英数字,ハイフン(-)だけにしてください
 project.name.duplicate = すでに同じな名前のプロジェクトがあります
-project.name.placeholder = プロジェクト名を入力してください
-project.name.reserved.alert = Name is reserved
+project.name.placeholder = プロジェクト名には半角英数字,ハイフン(-)のみ使用できます
+project.name.reserved.alert = 予約語は使用できません
 project.name.rule = 名前規則
 project.new.agreement = 利用契約に同意する
 project.new.vcsType.git = Git
@@ -373,9 +373,12 @@
 project.onwatching = <strong>{0}</strong>
 project.owner = 作成者名
 project.private = 非公開
-project.private.notice = 非公開プロジェクトもプロジェクト名、説明、ロゴなどは誰にでも見えます。
+project.private.notice = プロジェクトメンバーだけに接近を許します。ただし、プロジェクト名、説明、ロゴなどは一般に公開されます
 project.projects = プロジェクト
+project.protected = グループ内公開
+project.protected.notice = 同じグループ内の人々とプロジェクトメンバーだけに接近を許します
 project.public = 公開
+project.public.notice = 誰もこのプロジェクトに接近できます
 project.readme = プロジェクトに関する説明をREADME.mdファイルで作成してリポジトリールートに追加したらここに表示されます
 project.searchPlaceholder = このプロジェクトで検索
 project.setting = 設定
conf/messages.ko
--- conf/messages.ko
+++ conf/messages.ko
@@ -468,10 +468,10 @@
 project.members = 프로젝트 멤버
 project.members.addMember = 새로운 멤버의 아이디를 입력하세요
 project.name = 프로젝트 이름
-project.name.alert = 프로젝트 이름은 URL로 사용할 수 있는 글자(영문자,숫자,-'하이픈')만 허용합니다
+project.name.alert = 영문, 숫자, 하이픈(-)만 입력해주세요
 project.name.duplicate = 이미 같은 이름의 프로젝트가 있습니다
-project.name.placeholder = 프로젝트 이름을 입력해주세요.
-project.name.reserved.alert = 예약된 이름으로 사용할 수 없습니다.
+project.name.placeholder = 프로젝트 이름은 영문, 숫자, 하이픈(-)만 사용할 수 있습니다
+project.name.reserved.alert = 예약된 이름으로 사용할 수 없습니다
 project.name.rule = 이름 규칙
 project.new.agreement = 본인은 약관에 대한 안내를 읽었으며 이에 동의합니다
 project.new.vcsType.git = Git
public/javascripts/service/yobi.project.New.js
--- public/javascripts/service/yobi.project.New.js
+++ public/javascripts/service/yobi.project.New.js
@@ -27,20 +27,18 @@
         var htElement = {};
 
         function _init(htOptions){
-            _initVar(htOptions);
-            _initElement();
+            _initVar();
+            _initElement(htOptions);
             _attachEvent();
+            _focusOnFirstField();
 
-            _initFormValidator();
-
-            htElement.welInputProjectName.focus();
+            _showErrors(htOptions.htError);
         }
 
         /**
          * initialize variables
          */
-        function _initVar(htOptions){
-            htVar.sFormName = htOptions.sFormName || "newproject";
+        function _initVar(){
             htVar.rxPrjName = /^[0-9A-Za-z-_\.]+$/;
             htVar.aReservedWords = [".", "..", ".git"];
         }
@@ -48,11 +46,14 @@
         /**
          * initialize element
          */
-        function _initElement(){
-            htElement.vcsSelect = $("#vcs");
-            htElement.svnWarning = $("#svn");
+        function _initElement(htOptions){
+            htElement.welForm = $(htOptions.sFormId);
             htElement.welInputProjectName = $("#project-name");
             htElement.welInputProjectOwner = $("#project-owner");
+            htElement.welInputGitRepoURL = $("#url");
+
+            htElement.vcsSelect = $("#vcs");
+            htElement.svnWarning = $("#svn");
             htElement.welProtected = $("#opt-protected");
         }
 
@@ -62,6 +63,7 @@
         function _attachEvent(){
             htElement.vcsSelect.on("change", _onChangeVCSItem);
             htElement.welInputProjectOwner.on("change", _onChangeProjectOwner);
+            htElement.welForm.on("submit", _validateForm);
         }
 
         function _onChangeVCSItem(evt){
@@ -74,6 +76,7 @@
 
         function _onChangeProjectOwner() {
             var sType = $("#project-owner option:selected").data("type");
+
             if (sType == "user") {
                 if ($("#protected").is(":checked")) {
                     $("#public").prop("checked", true);
@@ -84,63 +87,76 @@
             }
         }
 
-        /**
-         * initialize formValidator
-         * @require validate.js
-         */
-        function _initFormValidator(){
-            // name : name of input element
-            // rules: rules to apply to the input element.
-            var aRules = [];
-
-            htVar.oValidator = new FormValidator(htVar.sFormName, aRules, function(aErrors){
-                var oForm = $(document.forms[htVar.sFormName]);
-                var oElement = oForm.find("input[name=name]");
-                var sPrjName = oElement.val();
-                if(!htVar.rxPrjName.test(sPrjName)){
-                    aErrors.push({
-                        id: oElement.attr("id"),
-                        name: oElement.attr("name"),
-                        message: Messages("project.name.alert")
-                    });
-                }
-                if(htVar.aReservedWords.indexOf(sPrjName) >= 0){
-                    aErrors.push({
-                        id: oElement.attr("id"),
-                        name: oElement.attr("name"),
-                        message: Messages("project.name.reserved.alert")
-                    });
-                }
-                _onFormValidate(aErrors);
-            });
+        function _focusOnFirstField(){
+            if(htElement.welInputGitRepoURL.length > 0){
+                htElement.welInputGitRepoURL.focus();
+            } else {
+                htElement.welInputProjectName.focus();
+            }
         }
 
         /**
-         * handler for validation errors.
+         * Validate form on submit
+         *
+         * @private
          */
-        function _onFormValidate(aErrors){
-            if(aErrors.length > 0){
-                $('span.warning').hide();
-                $('span.msg').html(aErrors[0].message).show();
-            } else {
-                new Spinner({
-                    lines: 13, // The number of lines to draw
-                    length: 10, // The length of each line
-                    width: 5, // The line thickness
-                    radius: 10, // The radius of the inner circle
-                    corners: 1, // Corner roundness (0..1)
-                    rotate: 0, // The rotation offset
-                    direction: 1, // 1: clockwise, -1: counterclockwise
-                    color: '#000', // #rgb or #rrggbb
-                    speed: 1, // Rounds per second
-                    trail: 60, // Afterglow percentage
-                    shadow: false, // Whether to render a shadow
-                    hwaccel: false, // Whether to use hardware acceleration
-                    className: 'spinner', // The CSS class to assign to the spinner
-                    zIndex: 2e9, // The z-index (defaults to 2000000000)
-                    top: 'auto', // Top position relative to parent in px
-                    left: 'auto' // Left position relative to parent in px
-                }).spin(document.forms[htVar.sFormName]);
+        function _validateForm(evt){
+            var error = {};
+            var projectName = htElement.welInputProjectName.val();
+
+            if(projectName.length === 0){
+                error.name = error.name || [];
+                error.name.push(Messages("project.name.alert"));
+            }
+
+            if(!htVar.rxPrjName.test(projectName)){
+                error.name = error.name || [];
+                error.name.push(Messages("project.name.alert"));
+            }
+
+            if(htVar.aReservedWords.indexOf(projectName) > -1){
+                error.name = error.name || [];
+                error.name.push(Messages("project.name.reserved.alert"));
+            }
+
+            if(htElement.welInputGitRepoURL.length > 0 &&
+               htElement.welInputGitRepoURL.val().trim().length === 0 ){
+                error.url = error.url || [];
+                error.url.push(Messages("project.import.error.empty.url"));
+            }
+
+            if(!$.isEmptyObject(error)){
+                _showErrors(error);
+                evt.preventDefault();
+                return false;
+            }
+
+            yobi.ui.Spinner.show();
+        }
+
+        /**
+         * Show error message on target element with $.popover
+         *
+         * @param error
+         * @private
+         */
+        function _showErrors(error){
+            if(!error){
+                return;
+            }
+
+            var targetElement;
+
+            for(var target in error){
+                targetElement = htElement.welForm.find("[name=" + target + "]");
+
+                if(targetElement.length > 0) {
+                    targetElement.popover({
+                        "trigger"  : "manual",
+                        "placement": "left",
+                        "content"  : error[target].shift()
+                    }).popover("show");
+                }
             }
         }
 
Add a comment
List