diff --git a/opentech/apply/dashboard/__init__.py b/opentech/apply/dashboard/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/opentech/apply/dashboard/apps.py b/opentech/apply/dashboard/apps.py
new file mode 100644
index 0000000000000000000000000000000000000000..50878e766268481aa6732f075857b0f1736fbd2d
--- /dev/null
+++ b/opentech/apply/dashboard/apps.py
@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+
+class DashboardConfig(AppConfig):
+    name = 'dashboard'
diff --git a/opentech/apply/dashboard/migrations/__init__.py b/opentech/apply/dashboard/migrations/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/opentech/apply/dashboard/models.py b/opentech/apply/dashboard/models.py
new file mode 100644
index 0000000000000000000000000000000000000000..0b4331b362b98a4e5947c491612f95a3c2e56686
--- /dev/null
+++ b/opentech/apply/dashboard/models.py
@@ -0,0 +1,3 @@
+# from django.db import models
+
+# Create your models here.
diff --git a/opentech/apply/dashboard/tables.py b/opentech/apply/dashboard/tables.py
new file mode 100644
index 0000000000000000000000000000000000000000..4d3b529af3ecc1924ae3b1bc01a8f15ec40d64ba
--- /dev/null
+++ b/opentech/apply/dashboard/tables.py
@@ -0,0 +1,12 @@
+import django_tables2 as tables
+from opentech.apply.funds.models import ApplicationSubmission
+
+
+class DashboardTable(tables.Table):
+    submit_time = tables.DateColumn(verbose_name="Submitted")
+    page = tables.Column(verbose_name="Fund")
+
+    class Meta:
+        model = ApplicationSubmission
+        fields = ('title', 'page', 'submit_time')
+        template = "dashboard/tables/table.html"
diff --git a/opentech/apply/dashboard/templates/dashboard/dashboard.html b/opentech/apply/dashboard/templates/dashboard/dashboard.html
new file mode 100644
index 0000000000000000000000000000000000000000..fbe73a38453efe082e7fd9a60c0d1d56e1f536c5
--- /dev/null
+++ b/opentech/apply/dashboard/templates/dashboard/dashboard.html
@@ -0,0 +1,13 @@
+{% extends "base.html" %}
+{% load render_table from django_tables2 %}
+
+{% block extra_css %}
+    {# To remove after the demo and we have some style #}
+    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.9/semantic.min.css"/>
+{% endblock %}
+
+{% block content %}
+<h1>Received Content</h1>
+<h3>Track and explore recent submissions</h3>
+{% render_table object_list %}
+{% endblock %}
diff --git a/opentech/apply/dashboard/templates/dashboard/tables/table.html b/opentech/apply/dashboard/templates/dashboard/tables/table.html
new file mode 100644
index 0000000000000000000000000000000000000000..6ff5e60321ee8084988b7726392a5c40e4ab3dcf
--- /dev/null
+++ b/opentech/apply/dashboard/templates/dashboard/tables/table.html
@@ -0,0 +1,7 @@
+{% extends 'django_tables2/semantic.html' %}
+{# Change back to after demo: extends 'django_tables2/table.html' #}
+
+{# example of how to extend the table template #}
+{% block table.tbody.row %}
+    {{ block.super }}
+{% endblock %}
diff --git a/opentech/apply/dashboard/urls.py b/opentech/apply/dashboard/urls.py
new file mode 100644
index 0000000000000000000000000000000000000000..b5bfdcafa3957fc4753d43a4d1055be469ff971c
--- /dev/null
+++ b/opentech/apply/dashboard/urls.py
@@ -0,0 +1,8 @@
+from django.conf.urls import url
+
+from .views import DashboardView
+
+
+urlpatterns = [
+    url(r'^$', DashboardView.as_view(), name="dashboard")
+]
diff --git a/opentech/apply/dashboard/views.py b/opentech/apply/dashboard/views.py
new file mode 100644
index 0000000000000000000000000000000000000000..b86e281c466be79136ff303496990da608fd46c3
--- /dev/null
+++ b/opentech/apply/dashboard/views.py
@@ -0,0 +1,17 @@
+from django.views.generic import ListView
+from django_tables2 import RequestConfig
+
+from opentech.apply.funds.models import ApplicationSubmission
+
+from .tables import DashboardTable
+
+
+class DashboardView(ListView):
+    model = ApplicationSubmission
+    template_name = 'dashboard/dashboard.html'
+
+    def get_context_data(self, **kwargs):
+        context = super().get_context_data(**kwargs)
+        context['object_list'] = DashboardTable(context['object_list'])
+        RequestConfig(self.request).configure(context['object_list'])
+        return context
diff --git a/opentech/apply/funds/blocks.py b/opentech/apply/funds/blocks.py
index 06513827f13da391874045d3b3f3e0b8f6dc7815..d89b2c3187ca1632b87e37737bb70ddda45be479 100644
--- a/opentech/apply/funds/blocks.py
+++ b/opentech/apply/funds/blocks.py
@@ -1,9 +1,129 @@
+from collections import Counter
+
+from django import forms
+from django.core.exceptions import ValidationError
+from django.forms.utils import ErrorList
 from django.utils.translation import ugettext_lazy as _
+from django.utils.text import mark_safe
 
-from opentech.apply.stream_forms.blocks import FormFieldsBlock
+from wagtail.wagtailcore.blocks import StaticBlock
+from opentech.apply.stream_forms.blocks import FormFieldsBlock, FormFieldBlock
 
 from opentech.apply.categories.blocks import CategoryQuestionBlock
 
 
+def find_duplicates(items):
+    counted = Counter(items)
+    duplicates = [
+        name for name, count in counted.items() if count > 1
+    ]
+    return duplicates
+
+
 class CustomFormFieldsBlock(FormFieldsBlock):
     category = CategoryQuestionBlock(group=_('Custom'))
+
+    def __init__(self, *args, **kwargs):
+        child_blocks = [(block.name, block(group=_('Required'))) for block in MustIncludeFieldBlock.__subclasses__()]
+        super().__init__(child_blocks, *args, **kwargs)
+
+    def clean(self, value):
+        try:
+            value = super().clean(value)
+        except ValidationError as e:
+            error_dict = e.params
+        else:
+            error_dict = dict()
+
+        block_types = [block.block_type for block in value]
+        missing = set(REQUIRED_BLOCK_NAMES) - set(block_types)
+
+        duplicates = [
+            name for name in find_duplicates(block_types)
+            if name in REQUIRED_BLOCK_NAMES
+        ]
+
+        all_errors = list()
+        if missing:
+            all_errors.append(
+                'You are missing the following required fields: {}'.format(', '.join(missing).title())
+            )
+
+        if duplicates:
+            all_errors.append(
+                'You have duplicates of the following required fields: {}'.format(', '.join(duplicates).title())
+            )
+            for i, block_name in enumerate(block_types):
+                if block_name in duplicates:
+                    self.add_error_to_child(error_dict, i, 'info', 'Duplicate field')
+
+        if all_errors or error_dict:
+            error_dict['__all__'] = all_errors
+            raise ValidationError('Error', params=error_dict)
+
+        return value
+
+    def add_error_to_child(self, errors, child_number, field, message):
+        new_error = ErrorList([message])
+        try:
+            errors[child_number].data[0].params[field] = new_error
+        except KeyError:
+            errors[child_number] = ErrorList(
+                [ValidationError('Error', params={field: new_error})]
+            )
+
+
+class MustIncludeStatic(StaticBlock):
+    """Helper block which displays additional information about the must include block and
+    helps display the error in a noticeable way.
+    """
+    def __init__(self, *args, description='', **kwargs):
+        self.description = description
+        super().__init__(*args, **kwargs)
+
+    class Meta:
+        admin_text = 'Much be included in the form only once.'
+
+    def render_form(self, *args, **kwargs):
+        errors = kwargs.pop('errors')
+        if errors:
+            # Pretend the error is a readonly input so that we get nice formatting
+            # Issue discussed here: https://github.com/wagtail/wagtail/issues/4122
+            error_message = '<div class="error"><input readonly placeholder="{}"></div>'.format(errors[0])
+        else:
+            error_message = ''
+        form = super().render_form(*args, **kwargs)
+        form = '<br>'.join([self.description, form]) + error_message
+        return mark_safe(form)
+
+    def deconstruct(self):
+        return ('wagtail.wagtailcore.blocks.static_block.StaticBlock', (), {})
+
+
+class MustIncludeFieldBlock(FormFieldBlock):
+    """Any block inheriting from this will need to be included in the application forms
+    This data will also be available to query on the submission object
+    """
+    def __init__(self, *args, **kwargs):
+        info_name = f'{self.name.title()} Field'
+        child_blocks = [('info', MustIncludeStatic(label=info_name, description=self.description))]
+        super().__init__(child_blocks, *args, **kwargs)
+
+    def get_field_kwargs(self, struct_value):
+        kwargs = super().get_field_kwargs(struct_value)
+        kwargs['required'] = True
+        return kwargs
+
+
+class TitleBlock(MustIncludeFieldBlock):
+    name = 'title'
+    description = 'The title of the project'
+
+
+class ValueBlock(MustIncludeFieldBlock):
+    name = 'value'
+    description = 'The value of the project'
+    widget = forms.NumberInput
+
+
+REQUIRED_BLOCK_NAMES = [block.name for block in MustIncludeFieldBlock.__subclasses__()]
diff --git a/opentech/apply/funds/migrations/0005_applicationsubmission.py b/opentech/apply/funds/migrations/0005_applicationsubmission.py
new file mode 100644
index 0000000000000000000000000000000000000000..ac820aae9eaf15fbd2148a022f554c4c8de8ff4c
--- /dev/null
+++ b/opentech/apply/funds/migrations/0005_applicationsubmission.py
@@ -0,0 +1,31 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.8 on 2018-01-22 09:40
+from __future__ import unicode_literals
+
+import django.contrib.postgres.fields.jsonb
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('wagtailcore', '0040_page_draft_title'),
+        ('funds', '0004_categoryblock_add_required_option'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='ApplicationSubmission',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('form_data', django.contrib.postgres.fields.jsonb.JSONField()),
+                ('submit_time', models.DateTimeField(auto_now_add=True, verbose_name='submit time')),
+                ('page', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='wagtailcore.Page')),
+            ],
+            options={
+                'verbose_name': 'form submission',
+                'abstract': False,
+            },
+        ),
+    ]
diff --git a/opentech/apply/funds/migrations/0006_update_block_definitions.py b/opentech/apply/funds/migrations/0006_update_block_definitions.py
new file mode 100644
index 0000000000000000000000000000000000000000..92d9ef8540a8270e1146556571a9a3dcc7dd0bec
--- /dev/null
+++ b/opentech/apply/funds/migrations/0006_update_block_definitions.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.8 on 2018-01-23 15:18
+from __future__ import unicode_literals
+
+from django.db import migrations
+import opentech.apply.categories.blocks
+import wagtail.wagtailcore.blocks
+import wagtail.wagtailcore.fields
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('funds', '0005_applicationsubmission'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='applicationform',
+            name='form_fields',
+            field=wagtail.wagtailcore.fields.StreamField((('char', wagtail.wagtailcore.blocks.StructBlock((('field_label', wagtail.wagtailcore.blocks.CharBlock(label='Label')), ('help_text', wagtail.wagtailcore.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.wagtailcore.blocks.BooleanBlock(label='Required', required=False)), ('format', wagtail.wagtailcore.blocks.ChoiceBlock(choices=[('email', 'Email'), ('url', 'URL')], label='Format', required=False)), ('default_value', wagtail.wagtailcore.blocks.CharBlock(label='Default value', required=False))), group='Fields')), ('text', wagtail.wagtailcore.blocks.StructBlock((('field_label', wagtail.wagtailcore.blocks.CharBlock(label='Label')), ('help_text', wagtail.wagtailcore.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.wagtailcore.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.wagtailcore.blocks.TextBlock(label='Default value', required=False))), group='Fields')), ('number', wagtail.wagtailcore.blocks.StructBlock((('field_label', wagtail.wagtailcore.blocks.CharBlock(label='Label')), ('help_text', wagtail.wagtailcore.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.wagtailcore.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.wagtailcore.blocks.CharBlock(label='Default value', required=False))), group='Fields')), ('checkbox', wagtail.wagtailcore.blocks.StructBlock((('field_label', wagtail.wagtailcore.blocks.CharBlock(label='Label')), ('help_text', wagtail.wagtailcore.blocks.TextBlock(label='Help text', required=False)), ('default_value', wagtail.wagtailcore.blocks.BooleanBlock(required=False))), group='Fields')), ('radios', wagtail.wagtailcore.blocks.StructBlock((('field_label', wagtail.wagtailcore.blocks.CharBlock(label='Label')), ('help_text', wagtail.wagtailcore.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.wagtailcore.blocks.BooleanBlock(label='Required', required=False)), ('choices', wagtail.wagtailcore.blocks.ListBlock(wagtail.wagtailcore.blocks.CharBlock(label='Choice')))), group='Fields')), ('dropdown', wagtail.wagtailcore.blocks.StructBlock((('field_label', wagtail.wagtailcore.blocks.CharBlock(label='Label')), ('help_text', wagtail.wagtailcore.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.wagtailcore.blocks.BooleanBlock(label='Required', required=False)), ('choices', wagtail.wagtailcore.blocks.ListBlock(wagtail.wagtailcore.blocks.CharBlock(label='Choice')))), group='Fields')), ('checkboxes', wagtail.wagtailcore.blocks.StructBlock((('field_label', wagtail.wagtailcore.blocks.CharBlock(label='Label')), ('help_text', wagtail.wagtailcore.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.wagtailcore.blocks.BooleanBlock(label='Required', required=False)), ('checkboxes', wagtail.wagtailcore.blocks.ListBlock(wagtail.wagtailcore.blocks.CharBlock(label='Checkbox')))), group='Fields')), ('date', wagtail.wagtailcore.blocks.StructBlock((('field_label', wagtail.wagtailcore.blocks.CharBlock(label='Label')), ('help_text', wagtail.wagtailcore.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.wagtailcore.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.wagtailcore.blocks.DateBlock(required=False))), group='Fields')), ('time', wagtail.wagtailcore.blocks.StructBlock((('field_label', wagtail.wagtailcore.blocks.CharBlock(label='Label')), ('help_text', wagtail.wagtailcore.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.wagtailcore.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.wagtailcore.blocks.TimeBlock(required=False))), group='Fields')), ('datetime', wagtail.wagtailcore.blocks.StructBlock((('field_label', wagtail.wagtailcore.blocks.CharBlock(label='Label')), ('help_text', wagtail.wagtailcore.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.wagtailcore.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.wagtailcore.blocks.DateTimeBlock(required=False))), group='Fields')), ('image', wagtail.wagtailcore.blocks.StructBlock((('field_label', wagtail.wagtailcore.blocks.CharBlock(label='Label')), ('help_text', wagtail.wagtailcore.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.wagtailcore.blocks.BooleanBlock(label='Required', required=False))), group='Fields')), ('file', wagtail.wagtailcore.blocks.StructBlock((('field_label', wagtail.wagtailcore.blocks.CharBlock(label='Label')), ('help_text', wagtail.wagtailcore.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.wagtailcore.blocks.BooleanBlock(label='Required', required=False))), group='Fields')), ('text_markup', wagtail.wagtailcore.blocks.RichTextBlock(group='Other', label='Paragraph')), ('category', wagtail.wagtailcore.blocks.StructBlock((('field_label', wagtail.wagtailcore.blocks.CharBlock(help_text='Leave blank to use the default Category label', label='Label', required=False)), ('help_text', wagtail.wagtailcore.blocks.TextBlock(label='Leave blank to use the default Category help text', required=False)), ('required', wagtail.wagtailcore.blocks.BooleanBlock(label='Required', required=False)), ('category', opentech.apply.categories.blocks.ModelChooserBlock('categories.Category')), ('multi', wagtail.wagtailcore.blocks.BooleanBlock(label='Multi select', required=False))), group='Custom')), ('title', wagtail.wagtailcore.blocks.StructBlock((('field_label', wagtail.wagtailcore.blocks.CharBlock(label='Label')), ('help_text', wagtail.wagtailcore.blocks.TextBlock(label='Help text', required=False)), ('info', wagtail.wagtailcore.blocks.StaticBlock())), group='Required')), ('value', wagtail.wagtailcore.blocks.StructBlock((('field_label', wagtail.wagtailcore.blocks.CharBlock(label='Label')), ('help_text', wagtail.wagtailcore.blocks.TextBlock(label='Help text', required=False)), ('info', wagtail.wagtailcore.blocks.StaticBlock())), group='Required')))),
+        ),
+    ]
diff --git a/opentech/apply/funds/migrations/0005_round.py b/opentech/apply/funds/migrations/0007_round.py
similarity index 92%
rename from opentech/apply/funds/migrations/0005_round.py
rename to opentech/apply/funds/migrations/0007_round.py
index e839c88745c36b286bf86a7ab58809fe350d5fdc..14ed8c2dbdf4b303871d1c486fc0d25cc29404de 100644
--- a/opentech/apply/funds/migrations/0005_round.py
+++ b/opentech/apply/funds/migrations/0007_round.py
@@ -10,7 +10,7 @@ class Migration(migrations.Migration):
 
     dependencies = [
         ('wagtailcore', '0040_page_draft_title'),
-        ('funds', '0004_categoryblock_add_required_option'),
+        ('funds', '0006_update_block_definitions'),
     ]
 
     operations = [
diff --git a/opentech/apply/funds/migrations/0006_add_dates_to_round.py b/opentech/apply/funds/migrations/0008_add_date_to_round.py
similarity index 94%
rename from opentech/apply/funds/migrations/0006_add_dates_to_round.py
rename to opentech/apply/funds/migrations/0008_add_date_to_round.py
index 747ef134788e1689cc12dc9d1e47105361b116c4..b57db907c91167001264c42c3c74a2e479d885c3 100644
--- a/opentech/apply/funds/migrations/0006_add_dates_to_round.py
+++ b/opentech/apply/funds/migrations/0008_add_date_to_round.py
@@ -9,7 +9,7 @@ from django.db import migrations, models
 class Migration(migrations.Migration):
 
     dependencies = [
-        ('funds', '0005_round'),
+        ('funds', '0007_round'),
     ]
 
     operations = [
diff --git a/opentech/apply/funds/migrations/0007_update_date_fields.py b/opentech/apply/funds/migrations/0009_update_date_fields.py
similarity index 93%
rename from opentech/apply/funds/migrations/0007_update_date_fields.py
rename to opentech/apply/funds/migrations/0009_update_date_fields.py
index 7b6b2829535b0139d491a0be0589e4acad48430e..ed7261b0f1e6dc064eb9d4f2640e19d633b05365 100644
--- a/opentech/apply/funds/migrations/0007_update_date_fields.py
+++ b/opentech/apply/funds/migrations/0009_update_date_fields.py
@@ -9,7 +9,7 @@ from django.db import migrations, models
 class Migration(migrations.Migration):
 
     dependencies = [
-        ('funds', '0006_add_dates_to_round'),
+        ('funds', '0008_add_date_to_round'),
     ]
 
     operations = [
diff --git a/opentech/apply/funds/models.py b/opentech/apply/funds/models.py
index 9f575ea6a3e22748e9fd76da3268daa293cbf0c0..347470855cfbe410e192d6ecc26aae50e3ccf352 100644
--- a/opentech/apply/funds/models.py
+++ b/opentech/apply/funds/models.py
@@ -1,8 +1,10 @@
 from datetime import date
 
+from django.contrib.postgres.fields import JSONField
 from django.core.exceptions import ValidationError
 from django.db import models
 from django.db.models import Q
+from django.db.models.expressions import RawSQL, OrderBy
 from django.http import Http404
 from django.urls import reverse
 from django.utils.text import mark_safe
@@ -17,10 +19,11 @@ from wagtail.wagtailadmin.edit_handlers import (
 )
 from wagtail.wagtailcore.fields import StreamField
 from wagtail.wagtailcore.models import Orderable
+from wagtail.wagtailforms.models import AbstractFormSubmission
 
 from opentech.apply.stream_forms.models import AbstractStreamForm
 
-from .blocks import CustomFormFieldsBlock
+from .blocks import CustomFormFieldsBlock, MustIncludeFieldBlock, REQUIRED_BLOCK_NAMES
 from .forms import WorkflowFormAdminForm
 from .workflow import SingleStage, DoubleStage
 
@@ -52,6 +55,22 @@ class FundType(AbstractStreamForm):
         # Only return the first form, will need updating for when working with 2 stage WF
         return self.forms.all()[0].fields
 
+    def get_submission_class(self):
+        return ApplicationSubmission
+
+    def process_form_submission(self, form):
+        cleaned_data = form.cleaned_data
+        for field in self.get_defined_fields():
+            # Update the ids which are unique to use the unique name
+            if isinstance(field.block, MustIncludeFieldBlock):
+                response = cleaned_data.pop(field.id)
+                cleaned_data[field.block.name] = response
+
+        return self.get_submission_class().objects.create(
+            form_data=cleaned_data,
+            page=self,
+        )
+
     @property
     def workflow_class(self):
         return WORKFLOW_CLASS[self.get_workflow_display()]
@@ -182,3 +201,44 @@ class Round(AbstractStreamForm):
         # We hide the round as only the open round is used which is displayed through the
         # fund page
         raise Http404()
+
+
+class JSONOrderable(models.QuerySet):
+    def order_by(self, *field_names):
+        def build_json_order_by(field):
+            if field.replace('-', '') not in REQUIRED_BLOCK_NAMES:
+                return field
+
+            if field[0] == '-':
+                descending = True
+                field = field[1:]
+            else:
+                descending = False
+            return OrderBy(RawSQL("LOWER(form_data->>%s)", (field,)), descending=descending)
+
+        field_ordering = [build_json_order_by(field) for field in field_names]
+        return super().order_by(*field_ordering)
+
+
+class ApplicationSubmission(AbstractFormSubmission):
+    form_data = JSONField()
+
+    objects = JSONOrderable.as_manager()
+
+    def get_data(self):
+        # Updated for JSONField
+        form_data = self.form_data
+        form_data.update({
+            'submit_time': self.submit_time,
+        })
+
+        return form_data
+
+    def __getattr__(self, item):
+        # fall back to values defined on the data
+        if item in REQUIRED_BLOCK_NAMES:
+            return self.get_data()[item]
+        return super().__getattr__(item)
+
+    def __str__(self):
+        return str(super().__str__())
diff --git a/opentech/apply/stream_forms/blocks.py b/opentech/apply/stream_forms/blocks.py
index 8340e7433d801929dbc5e590294968ce5124725d..f3b0906e6770e5246c6487cdcb9ac760c6ae7e43 100644
--- a/opentech/apply/stream_forms/blocks.py
+++ b/opentech/apply/stream_forms/blocks.py
@@ -8,7 +8,7 @@ from django.utils.translation import ugettext_lazy as _
 from unidecode import unidecode
 from wagtail.wagtailcore.blocks import (
     StructBlock, TextBlock, CharBlock, BooleanBlock, ListBlock, StreamBlock,
-    DateBlock, TimeBlock, DateTimeBlock, ChoiceBlock, RichTextBlock,
+    DateBlock, TimeBlock, DateTimeBlock, ChoiceBlock, RichTextBlock
 )
 
 
diff --git a/opentech/apply/stream_forms/models.py b/opentech/apply/stream_forms/models.py
index 3c34df543635e91fc8bb07d586038a85cb4edf24..699714c23c685ef8cece9c03534b82fac7ad98fb 100644
--- a/opentech/apply/stream_forms/models.py
+++ b/opentech/apply/stream_forms/models.py
@@ -8,6 +8,8 @@ from .forms import BlockFieldWrapper, StreamBaseForm
 
 
 class AbstractStreamForm(AbstractForm):
+    page_form_class = StreamBaseForm
+
     class Meta:
         abstract = True
 
@@ -21,11 +23,10 @@ class AbstractStreamForm(AbstractForm):
             block = struct_child.block
             struct_value = struct_child.value
             if isinstance(block, FormFieldBlock):
-                field_name = block.get_slug(struct_value)
-                form_fields[field_name] = block.get_field(struct_value)
+                form_fields[struct_child.id] = block.get_field(struct_value)
             else:
                 form_fields[struct_child.id] = BlockFieldWrapper(struct_child)
         return form_fields
 
     def get_form_class(self):
-        return type('WagtailStreamForm', (StreamBaseForm,), self.get_form_fields())
+        return type('WagtailStreamForm', (self.page_form_class,), self.get_form_fields())
diff --git a/opentech/apply/urls.py b/opentech/apply/urls.py
index 4624990d07ee3a1c64946e9dc6ab25ea614d4217..802e5c0fcfd9eae92a8b82d091a7070abe82e867 100644
--- a/opentech/apply/urls.py
+++ b/opentech/apply/urls.py
@@ -2,8 +2,11 @@ from django.conf.urls import include, url
 
 from .funds import urls as funds_urls
 from .users import urls as users_urls
+from .dashboard import urls as dashboard_urls
+
 
 urlpatterns = [
     url(r'^apply/', include(funds_urls)),
     url(r'^account/', include(users_urls, namespace='users')),
+    url(r'^dashboard/', include(dashboard_urls, namespace='dashboard')),
 ]
diff --git a/opentech/settings/base.py b/opentech/settings/base.py
index 38c958902a2f43a3babbbe31749555e0dba3f657..7405abda9515029499f8bded1239684cda298dbe 100644
--- a/opentech/settings/base.py
+++ b/opentech/settings/base.py
@@ -16,6 +16,7 @@ INSTALLED_APPS = [
 
     'opentech.apply.categories',
     'opentech.apply.funds',
+    'opentech.apply.dashboard',
     'opentech.apply.home',
     'opentech.apply.users',
     'opentech.apply.stream_forms',
@@ -55,6 +56,7 @@ INSTALLED_APPS = [
     'django_extensions',
     'captcha',
     'wagtailcaptcha',
+    'django_tables2',
 
     'django.contrib.admin',
     'django.contrib.auth',
diff --git a/requirements.txt b/requirements.txt
index 83d06ba2b9b890ff1af1af148881eadcb7987443..5399a07ac8b3ca01941552afeb116640eba3cff2 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -13,6 +13,7 @@ wagtail_factories==0.3.0
 flake8
 
 social_auth_app_django==2.1.0
+django-tables2==1.17.1
 
 # Production dependencies
 dj-database-url==0.4.1