Browse Source

HUE-749 [beeswax] Create a new table from file page is broken

Styled page
Enrico Berti 13 years ago
parent
commit
8b1089b

+ 8 - 3
apps/beeswax/src/beeswax/create_table.py

@@ -129,11 +129,16 @@ def import_wizard(request):
       do_s3_column_def = request.POST.get('submit_delim')       # Step 2 -> 3
       do_hive_create = request.POST.get('submit_create')        # Step 3 -> execute
 
+      cancel_s2_user_delim = request.POST.get('cancel_delim')   # Step 2 -> 1
+      cancel_s3_column_def = request.POST.get('cancel_create')  # Step 3 -> 2
+
       # Exactly one of these should be True
       assert len(filter(None, (do_s2_auto_delim,
                                do_s2_user_delim,
                                do_s3_column_def,
-                               do_hive_create))) == 1, 'Invalid form submission'
+                               do_hive_create,
+                               cancel_s2_user_delim,
+                               cancel_s3_column_def))) == 1, 'Invalid form submission'
 
       #
       # Fix up what we should do in case any form is invalid
@@ -164,7 +169,7 @@ def import_wizard(request):
                                               [ reader.TYPE for reader in FILE_READERS ],
                                               DELIMITERS)
 
-      if (do_s2_user_delim or do_s3_column_def) and s2_delim_form.is_valid():
+      if (do_s2_user_delim or do_s3_column_def or cancel_s3_column_def) and s2_delim_form.is_valid():
         # Delimit based on input
         fields_list, n_cols, s2_delim_form = _delim_preview(
                                               request.fs,
@@ -173,7 +178,7 @@ def import_wizard(request):
                                               (s2_delim_form.cleaned_data['file_type'],),
                                               (s2_delim_form.cleaned_data['delimiter'],))
 
-      if do_s2_auto_delim or do_s2_user_delim:
+      if do_s2_auto_delim or do_s2_user_delim or cancel_s3_column_def:
         return render('choose_delimiter.mako', request, dict(
           action=urlresolvers.reverse(import_wizard),
           delim_readable=DELIMITER_READABLE[s2_delim_form['delimiter'].data[0]],

+ 33 - 24
apps/beeswax/src/beeswax/templates/beeswax_components.mako

@@ -20,6 +20,10 @@
 
 </%def>
 
+<%def name="bootstrapLabel(field)">
+    <label for="${field.html_name | n}" class="control-label">${field.label | n}</label>
+</%def>
+
 <%def name="label(
   field,
   render_default=False,
@@ -93,7 +97,9 @@
   dt_attrs=None,
   title_klass=None,
   button_text=False,
-  placeholder=None
+  placeholder=None,
+  file_chooser=False,
+  show_errors=True
   )">
 <%
   if value is None:
@@ -106,7 +112,7 @@
     for key, value in attributes.iteritems():
       if key == "klass":
         key = "class"
-      ret_str += "%s='%s'" % (key.replace("_", "-"), unicode(value))
+      ret_str += "%s='%s' " % (key.replace("_", "-"), unicode(value))
     return ret_str
 
   if not attrs:
@@ -135,32 +141,35 @@
   if placeholder:
 	plc = "placeholder=\"%s\"" % placeholder
 %>
-  % if field.is_hidden:
-    ${unicode(field) | n}
-  % else:
-      % if render_default:
+    % if field.is_hidden:
         ${unicode(field) | n}
-      % else:
-        % if tag == 'textarea':
-          <textarea name="${field.html_name | n}" ${make_attr_str(attrs) | n} />${extract_field_data(field) or ''}</textarea>
-        % elif tag == 'button':
-          <button name="${field.html_name | n}" ${make_attr_str(attrs) | n} value="${value}"/>${button_text or field.name or ''}</button>
-        % elif tag == 'checkbox':
-			% if help:
-				<input type="checkbox" name="${field.html_name | n}" ${make_attr_str(attrs) | n} ${value and "CHECKED" or ""}/ /> <span rel="tooltip" data-original-title="${help}" >${button_text or field.name or ''}</span>
-			% else:
-				<input type="checkbox" name="${field.html_name | n}" ${make_attr_str(attrs) | n} ${value and "CHECKED" or ""}/> <span>${button_text or field.name or ''}</span>
-			% endif
+    % else:
+        % if render_default:
+            ${unicode(field) | n}
         % else:
-          <${tag} name="${field.html_name | n}" value="${extract_field_data(field) or ''}" ${make_attr_str(attrs) | n} class="${cls}" ${plc} />
-        % endif
+            % if tag == 'textarea':
+                <textarea name="${field.html_name | n}" ${make_attr_str(attrs) | n} />${extract_field_data(field) or ''}</textarea>
+            % elif tag == 'button':
+                <button name="${field.html_name | n}" ${make_attr_str(attrs) | n} value="${value}"/>${button_text or field.name or ''}</button>
+            % elif tag == 'checkbox':
+                % if help:
+                    <input type="checkbox" name="${field.html_name | n}" ${make_attr_str(attrs) | n} ${value and "CHECKED" or ""}/ /> <span rel="tooltip" data-original-title="${help}" >${button_text or field.name or ''}</span>
+                % else:
+                    <input type="checkbox" name="${field.html_name | n}" ${make_attr_str(attrs) | n} ${value and "CHECKED" or ""}/> <span>${button_text or field.name or ''}</span>
+                % endif
+            % else:
+                %if file_chooser:
+                    <${tag} name="${field.html_name | n}" value="${extract_field_data(field) or ''}" ${make_attr_str(attrs) | n} class="${cls}" ${plc} /><a class="btn fileChooserBtn" href="#" data-filechooser-destination="${field.html_name | n}">..</a>
+                %else:
+                    <${tag} name="${field.html_name | n}" value="${extract_field_data(field) or ''}" ${make_attr_str(attrs) | n} class="${cls}" ${plc} />
+                %endif
+            % endif
 
-      % endif
-    % if len(field.errors):
-         ${unicode(field.errors) | n}
+        % endif
+        % if show_errors and len(field.errors):
+            ${unicode(field.errors) | n}
+        % endif
     % endif
-
-  % endif
 </%def>
 
 

+ 101 - 88
apps/beeswax/src/beeswax/templates/choose_delimiter.mako

@@ -23,97 +23,110 @@ ${commonheader("Beeswax: Create table from file", "beeswax", "100px")}
 ${layout.menubar(section='tables')}
 
 <div class="container-fluid">
-	<h1>Create a new table from file</h1>
-	<div class="row-fluid">
-		<div class="span3">
-			<div class="well sidebar-nav">
-				<ul class="nav nav-list">
-					<li class="nav-header">Actions</li>
-					<li><a href="${ url('beeswax.create_table.import_wizard')}">Create a new table from file</a></li>
-					<li><a href="${ url('beeswax.create_table.create_table')}">Create a new table manually</a></li>
-				</ul>
-			</div>
-		</div>
-		<div class="span9">
-			<ul class="nav nav-pills">
-			  <li><a href="${ url('beeswax.create_table.import_wizard') }">Step 1: Choose File</a></li>
-			  <li class="active"><a href="#">Step 2: Choose Delimiter</a></li>
-			  <li><a href="#">Step 3: Define Columns</a></li>
-			</ul>
-			<br/>
-			<form id="delimiterForm" action="${action}" method="POST">
-				<div class="hidden">
-					${util.render_form(file_form)}
-					${comps.field(delim_form['file_type'])}
-				</div>
-				<fieldset>
-					<legend>Choose A Delimiter</legend>
-					<div class="clearfix">
-						${comps.label(delim_form["delimiter"])}
-						<div class="input">
-							${comps.field(delim_form["delimiter"], render_default=True, attrs=dict(
-								klass=""
-							))}
-							% if initial:
-								<span class="help-inline">
-									Beeswax has determined that this file is delimited by ${delim_readable}.
-								</span>
-							% endif
-							<span class="help-block">
-							Enter the column delimiter.  Must be a single character.  Use syntax like "\001" or "\t" for special characters.
-							</span>
-						</div>
-					</div>
-				</fieldset>
-				<div class="actions">
-					<input id="submit_preview" class="btn" type="submit" value="Preview" name="submit_preview"/>
-					<input class="btn primary" type="submit" name="submit_delim" value="Select this delimiter" />
-				</div>
-			</form>
-			<br/>
-			<ul class="nav nav-tabs">
-				<li class="active"><a href="#tablePreview" data-toggle="tab">Table preview</a></li>
-			</ul>
-			<div class="tab-content">
-				<div class="active tab-pane" id="tablePreview">
-					<table class="table table-striped table-condensed">
-				      <thead>
-				        <tr>
-				          % for i in range(n_cols):
-				            <th>col_${i+1}</th>
-				          % endfor
-				        </tr>
-				      </thead>
-				      <tbody>
-				        % for row in fields_list:
-				          <tr>
-				            % for val in row:
-				            <td>${val}</td>
-				            % endfor
-				          </tr>
-				        % endfor
-				      </tbody>
-				    </table>
-				</div>
-			</div>
-		</div>
-	</div>
+    <h1>Create a new table from file</h1>
+    <div class="row-fluid">
+        <div class="span3">
+            <div class="well sidebar-nav">
+                <ul class="nav nav-list">
+                    <li class="nav-header">Actions</li>
+                    <li><a href="${ url('beeswax.create_table.import_wizard')}">Create a new table from file</a></li>
+                    <li><a href="${ url('beeswax.create_table.create_table')}">Create a new table manually</a></li>
+                </ul>
+            </div>
+        </div>
+        <div class="span9">
+            <ul class="nav nav-pills">
+                <li><a id="step1" href="#">Step 1: Choose File</a></li>
+                <li class="active"><a href="#">Step 2: Choose Delimiter</a></li>
+                <li><a id="step3" href="#">Step 3: Define Columns</a></li>
+            </ul>
+            <form id="delimiterForm" action="${action}" method="POST" class="form-horizontal">
+                <div class="hide">
+                    ${util.render_form(file_form)}
+                    ${comps.field(delim_form['file_type'])}
+                </div>
+                <fieldset>
+                    <div class="alert alert-info"><h3>Choose A Delimiter</h3>
+                        % if initial:
+                                Beeswax has determined that this file is delimited by <strong>${delim_readable}</strong>.
+                        % endif
+                    </div>
+                    <div class="control-group">
+                        ${comps.bootstrapLabel(delim_form["delimiter"])}
+                        <div class="controls">
+                            ${comps.field(delim_form["delimiter"], render_default=True)}
+                            <input id="submit_preview" class="btn btn-info" type="submit" value="Preview" name="submit_preview"/>
+                            <span class="help-block">
+                            Enter the column delimiter.  Must be a single character.  Use syntax like "\001" or "\t" for special characters.
+                            </span>
+                        </div>
+                    </div>
+                    <div class="control-group">
+                        <label class="control-label">Table preview</label>
+                        <div class="controls">
+                            <div class="scrollable">
+                                <table class="table table-striped table-condensed">
+                                    <thead>
+                                    <tr>
+                                            % for i in range(n_cols):
+                                                <th>col_${i+1}</th>
+                                            % endfor
+                                    </tr>
+                                    </thead>
+                                    <tbody>
+                                            % for row in fields_list:
+                                            <tr>
+                                                % for val in row:
+                                                    <td>${val}</td>
+                                                % endfor
+                                            </tr>
+                                            % endfor
+                                    </tbody>
+                                </table>
+                            </div>
+                        </div>
+                    </div>
+                </fieldset>
+
+                <div class="form-actions">
+                    <input class="btn" type="submit" value="Previous" name="cancel_delim"/>
+                    <input class="btn primary" type="submit" name="submit_delim" value="Next" />
+                </div>
+            </form>
+        </div>
+    </div>
 </div>
 
+<style>
+    .scrollable {
+        width: 100%;
+        overflow-x: auto;
+    }
+</style>
 
 <script type="text/javascript" charset="utf-8">
-	$(document).ready(function(){
-		$("#id_delimiter_1").css("margin-left","4px").attr("placeholder","Please write here your delimiter").hide();
-		$("#id_delimiter_0").change(function(){
-			if ($(this).val() == "__other__"){
-				$("#id_delimiter_1").show();
-			}
-			else {
-				$("#id_delimiter_1").hide();
-				$("#submit_preview").click();
-			}
-		});
-	});
+    $(document).ready(function(){
+        $(".scrollable").width($(".form-actions").width());
+
+        $("#id_delimiter_1").css("margin-left","4px").attr("placeholder","Please write here your delimiter").hide();
+        $("#id_delimiter_0").change(function(){
+            if ($(this).val() == "__other__"){
+                $("#id_delimiter_1").show();
+            }
+            else {
+                $("#id_delimiter_1").hide();
+            }
+        });
+
+        $("#step1").click(function(e){
+            e.preventDefault();
+            $("input[name='cancel_delim']").click();
+        });
+        $("#step3").click(function(e){
+            e.preventDefault();
+            $("input[name='submit_delim']").click();
+        });
+    });
 </script>
 
-${commonfooter()}
+${commonfooter()}

+ 141 - 107
apps/beeswax/src/beeswax/templates/choose_file.mako

@@ -23,122 +23,156 @@ ${layout.menubar(section='tables')}
 
 
 <div class="container-fluid">
-	<h1>Create a new table from file</h1>
-	<div class="row-fluid">
-		<div class="span3">
-			<div class="well sidebar-nav">
-				<ul class="nav nav-list">
-					<li class="nav-header">Actions</li>
-					<li><a href="${ url('beeswax.create_table.import_wizard')}">Create a new table from file</a></li>
-					<li><a href="${ url('beeswax.create_table.create_table')}">Create a new table manually</a></li>
-				</ul>
-			</div>
-		</div>
-		<div class="span9">
-		<ul class="nav nav-pills">
-		  <li class="active"><a href="${ url('beeswax.create_table.import_wizard') }">Step 1: Choose File</a></li>
-		  <li><a href="#">Step 2: Choose Delimiter</a></li>
-		  <li><a href="#">Step 3: Define Columns</a></li>
-		</ul>
-		<br/>
-		<form action="${action}" method="POST">
-			<fieldset>
-				<legend>Name Your Table and Choose A File</legend>
-				<div class="clearfix">
-					${comps.label(file_form["name"])}
-					<div class="input">
-						${comps.field(file_form["name"], attrs=dict(
-							placeholder="table_name",
-							klass=""
-						))}
-						<span class="help-block">
-						Name of the new table.  Table names must be globally unique.  Table names tend to correspond as well to the directory where the data will be stored.
-						</span>
-					</div>
-				</div>
-				<div class="clearfix">
-					${comps.label(file_form["comment"])}
-					<div class="input">
-						${comps.field(file_form["comment"], attrs=dict(
-							placeholder="Optional",
-							klass=""
-						))}
-						<span class="help-block">
-						Use a table comment to describe your table.  For example, you might mention the data's provenance, and any caveats users of this table should expect.
-						</span>
-					</div>
-				</div>
-				<div class="clearfix">
-					${comps.label(file_form["path"])}
-					<div class="input">
-						${comps.field(file_form["path"], attrs=dict(
-							placeholder="/user/user_name/data_dir",
-							klass=""
-						))}
-						<span class="help-inline"><a id="pathChooser" href="#" data-filechooser-destination="path">Choose File</a></span>
-						<span class="help-block">
-						The HDFS path to the file that you would like to base this new table definition on.  It can be compressed (gzip) or not.
-						</span>
-					</div>
-				</div>
-				<div class="clearfix">
-					${comps.label(file_form["do_import"])}
-					<div class="input">
-						${comps.field(file_form["do_import"], render_default=True, attrs=dict(
-							klass=""
-						))}
-						<span class="help-block">
-						Check this box if you want to import the data in this file after creating the table definition.  Leave it unchecked if you just want to define an empty table.
-						</span>
-					</div>
-				</div>
-			</fieldset>
-			<div class="actions">
-				<input type="submit" class="btn primary" name="submit_file" value="Choose a delimiter" />
-			</div>
-		</form>
-		</div>
-	</div>
+    <h1>Create a new table from file</h1>
+    <div class="row-fluid">
+        <div class="span3">
+            <div class="well sidebar-nav">
+                <ul class="nav nav-list">
+                    <li class="nav-header">Actions</li>
+                    <li><a href="${ url('beeswax.create_table.import_wizard')}">Create a new table from file</a></li>
+                    <li><a href="${ url('beeswax.create_table.create_table')}">Create a new table manually</a></li>
+                </ul>
+            </div>
+        </div>
+        <div class="span9">
+            <ul class="nav nav-pills">
+                <li class="active"><a href="${ url('beeswax.create_table.import_wizard') }">Step 1: Choose File</a></li>
+                <li><a id="step2" href="#">Step 2: Choose Delimiter</a></li>
+                <li><a href="#">Step 3: Define Columns</a></li>
+            </ul>
+            <form action="${action}" method="POST" class="form-horizontal">
+                <fieldset>
+                    <div class="alert alert-info"><h3>Name Your Table and Choose A File</h3></div>
+                    <div class="control-group">
+                        ${comps.bootstrapLabel(file_form["name"])}
+                        <div class="controls">
+                            ${comps.field(file_form["name"],
+                            placeholder='table_name',
+                            show_errors=False
+                            )}
+                            <span  class="help-inline">${unicode(file_form["name"].errors) | n}</span>
+                        <span class="help-block">
+                            Name of the new table.  Table names must be globally unique.  Table names tend to correspond as well to the directory where the data will be stored.
+                        </span>
+                        </div>
+                    </div>
+                    <div class="control-group">
+                        ${comps.bootstrapLabel(file_form["comment"])}
+                        <div class="controls">
+                            ${comps.field(file_form["comment"],
+                            placeholder="Optional",
+                            klass="",
+                            show_errors=False
+                            )}
+                            <span  class="help-inline">${unicode(file_form["comment"].errors) | n}</span>
+                        <span class="help-block">
+                        Use a table comment to describe your table.  For example, you might mention the data's provenance, and any caveats users of this table should expect.
+                        </span>
+                        </div>
+                    </div>
+                    <div class="control-group">
+                        ${comps.bootstrapLabel(file_form["path"])}
+                        <div class="controls">
+                            ${comps.field(file_form["path"],
+                            placeholder="/user/user_name/data_dir",
+                            klass="pathChooser",
+                            file_chooser=True,
+                            show_errors=False
+                            )}
+                            <span  class="help-inline">${unicode(file_form["path"].errors) | n}</span>
+                        <span class="help-block">
+                        The HDFS path to the file that you would like to base this new table definition on.  It can be compressed (gzip) or not.
+                        </span>
+                        </div>
+                    </div>
+                    <div class="control-group">
+                        ${comps.bootstrapLabel(file_form["do_import"])}
+                        <div class="controls">
+                            ${comps.field(file_form["do_import"], render_default=True)}
+                            <span class="help-block">
+                        Check this box if you want to import the data in this file after creating the table definition.  Leave it unchecked if you just want to define an empty table.
+                        <div id="fileWillBeMoved" class="alert">
+                            <strong>Warning!</strong> The selected file is going to be moved during the import.
+                        </div>
+                        </span>
+                        </div>
+                    </div>
+                </fieldset>
+                <div class="form-actions">
+                    <input type="submit" class="btn primary" name="submit_file" value="Next" />
+                </div>
+            </form>
+        </div>
+    </div>
 </div>
 
 
 <div id="chooseFile" class="modal hide fade">
-	<div class="modal-header">
-		<a href="#" class="close" data-dismiss="modal">&times;</a>
-		<h3>Choose a file</h3>
-	</div>
-	<div class="modal-body">
-		<div id="filechooser">
-		</div>
-	</div>
-	<div class="modal-footer">
-	</div>
-</div>
+    <div class="modal-header">
+        <a href="#" class="close" data-dismiss="modal">&times;</a>
+        <h3>Choose a file</h3>
+    </div>
+    <div class="modal-body">
+        <div id="filechooser">
+        </div>
+    </div>
+    <div class="modal-footer">
+    </div>
 </div>
+
+
 <style>
-	#filechooser {
-		min-height:100px;
-		overflow-y:scroll;
-		margin-top:10px;
-	}
+    #filechooser {
+        min-height: 100px;
+        overflow-y: scroll;
+        margin-top: 10px;
+    }
+
+    .pathChooser {
+        border-radius: 3px 0 0 3px;
+        border-right: 0;
+        margin-right: 0;
+    }
+
+    .fileChooserBtn {
+        border-radius: 0 3px 3px 0;
+        margin-left: 0;
+    }
+
+    #fileWillBeMoved {
+        margin-top: 10px;
+    }
 </style>
 
 
 <script type="text/javascript" charset="utf-8">
-	$(document).ready(function(){
-		$("#pathChooser").click(function(){
-			var _destination = $(this).attr("data-filechooser-destination");
-			$("#filechooser").jHueFileChooser({
-				onFileChoose: function(filePath){
-					$("input[name='"+_destination+"']").val(filePath);
-					$("#chooseFile").modal("hide");
-				},
-				createFolder: false
-			});
-			$("#chooseFile").modal("show");
-		});
-
-	});
+    $(document).ready(function(){
+        $(".fileChooserBtn").click(function(e){
+            e.preventDefault();
+            var _destination = $(this).attr("data-filechooser-destination");
+            $("#filechooser").jHueFileChooser({
+                initialPath: $("input[name='"+_destination+"']").val(),
+                onFileChoose: function(filePath){
+                    $("input[name='"+_destination+"']").val(filePath);
+                    $("#chooseFile").modal("hide");
+                },
+                createFolder: false
+            });
+            $("#chooseFile").modal("show");
+        });
+        $("#id_do_import").change(function(){
+            if ($(this).is(":checked")){
+                $("#fileWillBeMoved").show();
+            }
+            else {
+                $("#fileWillBeMoved").hide();
+            }
+        });
+        $("#step2").click(function(e){
+            e.preventDefault();
+            $("input[name='submit_file']").click();
+        });
+    });
 </script>
 
-${commonfooter()}
+${commonfooter()}

+ 91 - 74
apps/beeswax/src/beeswax/templates/define_columns.mako

@@ -23,80 +23,97 @@ ${commonheader("Beeswax: Create table from file", "beeswax", "100px")}
 ${layout.menubar(section='tables')}
 
 <div class="container-fluid">
-	<h1>Create a new table from file</h1>
-	<div class="row-fluid">
-		<div class="span3">
-			<div class="well sidebar-nav">
-				<ul class="nav nav-list">
-					<li class="nav-header">Actions</li>
-					<li><a href="${ url('beeswax.create_table.import_wizard')}">Create a new table from file</a></li>
-					<li><a href="${ url('beeswax.create_table.create_table')}">Create a new table manually</a></li>
-				</ul>
-			</div>
-		</div>
-		<div class="span9">
-		<ul class="nav nav-pills">
-		  <li><a href="${ url('beeswax.create_table.import_wizard') }">Step 1: Choose File</a></li>
-		  <li><a href="${ url('beeswax.create_table.import_wizard') }">Step 2: Choose Delimiter</a></li>
-		  <li class="active"><a href="#">Step 3: Define Columns</a></li>
-		</ul>
-		<br/>
-		<form action="${action}" method="POST" class="form-stacked">
-			<div class="hidden">
-				${util.render_form(file_form)}
-		        ${util.render_form(delim_form)}
-		        ${unicode(column_formset.management_form) | n}
-			</div>
-			<%
-		        n_rows = len(fields_list)
-		        if n_rows > 2: n_rows = 2
-		      %>
-			<fieldset>
-				<legend>Define your columns</legend>
-				<div class="clearfix">
-
-					<div class="input">
-						<table class="table table-striped">
-							<tr>
-								<td>&nbsp;</td>
-								% for form in column_formset.forms:
-				                    <td>
-										${comps.label(form["column_name"])}
-										${comps.field(form["column_name"],
-											render_default=False,
-											attrs=dict(
-												placeholder="Column name"
-											)
-										)}
-										<br/><br/>
-										${comps.label(form["column_type"])}
-										${comps.field(form["column_type"],
-											render_default=True
-										)}
-										${unicode(form["_exists"]) | n}
-				                    </td>
-				                  %endfor
-							</tr>
-							% for i, row in enumerate(fields_list[:n_rows]):
-			                  <tr>
-			                    <td><em>Row #${i + 1}</em></td>
-			                    % for val in row:
-			                      <td>${val}</td>
-			                    % endfor
-			                  </tr>
-			                % endfor
-						</table>
-
-					</div>
-				</div>
-			</fieldset>
-			<div class="actions">
-				<input class="btn primary" type="submit" name="submit_create" value="Create Table" />
-			</div>
-		</form>
-		</div>
-	</div>
+    <h1>Create a new table from file</h1>
+    <div class="row-fluid">
+        <div class="span3">
+            <div class="well sidebar-nav">
+                <ul class="nav nav-list">
+                    <li class="nav-header">Actions</li>
+                    <li><a href="${ url('beeswax.create_table.import_wizard')}">Create a new table from file</a></li>
+                    <li><a href="${ url('beeswax.create_table.create_table')}">Create a new table manually</a></li>
+                </ul>
+            </div>
+        </div>
+        <div class="span9">
+            <ul class="nav nav-pills">
+                <li><a id="step1" href="#">Step 1: Choose File</a></li>
+                <li><a id="step2" href="#">Step 2: Choose Delimiter</a></li>
+                <li class="active"><a href="#">Step 3: Define Columns</a></li>
+            </ul>
+            <form action="${action}" method="POST" class="form-stacked">
+                <div class="hide">
+                    ${util.render_form(file_form)}
+                ${util.render_form(delim_form)}
+                ${unicode(column_formset.management_form) | n}
+                </div>
+                <%
+                    n_rows = len(fields_list)
+                    if n_rows > 2: n_rows = 2
+                %>
+                <fieldset>
+                    <div class="alert alert-info"><h3>Define your columns</h3></div>
+                    <div class="control-group">
+                        <div class="controls">
+                            <div class="scrollable">
+                                <table class="table table-striped">
+                                    <tr>
+                                        <td>&nbsp;</td>
+                                        % for form in column_formset.forms:
+                                                <td>
+                                                ${comps.label(form["column_name"])}
+                                                ${comps.field(form["column_name"],
+                                                render_default=False,
+                                                placeholder="Column name"
+                                                )}
+                                                    <br/><br/>
+                                                ${comps.label(form["column_type"])}
+                                                ${comps.field(form["column_type"],
+                                                render_default=True
+                                                )}
+                                                ${unicode(form["_exists"]) | n}
+                                                </td>
+                                        %endfor
+                                    </tr>
+                                    % for i, row in enumerate(fields_list[:n_rows]):
+                                        <tr>
+                                            <td><em>Row #${i + 1}</em></td>
+                                        % for val in row:
+                                                <td>${val}</td>
+                                        % endfor
+                                        </tr>
+                                    % endfor
+                                </table>
+                            </div>
+                        </div>
+                    </div>
+                </fieldset>
+                <div class="form-actions">
+                    <input class="btn" type="submit" name="cancel_create" value="Previous" />
+                    <input class="btn primary" type="submit" name="submit_create" value="Create Table" />
+                </div>
+            </form>
+        </div>
+    </div>
 </div>
 
+<style>
+    .scrollable {
+        width: 100%;
+        overflow-x: auto;
+    }
+</style>
 
-${commonfooter()}
+<script type="text/javascript" charset="utf-8">
+    $(document).ready(function(){
+        $(".scrollable").width($(".form-actions").width());
+        $("#step1").click(function(e){
+            e.preventDefault();
+            $("input[name='cancel_create']").attr("name","cancel_delim").click();
+        });
+        $("#step2").click(function(e){
+            e.preventDefault();
+            $("input[name='cancel_create']").click();
+        });
+    });
+</script>
+${commonfooter()}