From 7bb417bbf66eb2af9c70ecd3f67d0923de998525 Mon Sep 17 00:00:00 2001
From: sks444 <krishnasingh.ss30@gmail.com>
Date: Mon, 14 Sep 2020 02:55:54 +0530
Subject: [PATCH] Add investments table and partners page

---
 hypha/public/partner/__init__.py              |   0
 hypha/public/partner/admin.py                 |  21 +++
 hypha/public/partner/apps.py                  |   5 +
 ...add_investments_table_and_partners_page.py |  75 +++++++++++
 hypha/public/partner/migrations/__init__.py   |   0
 hypha/public/partner/models.py                | 120 ++++++++++++++++++
 hypha/public/partner/tables.py                |  21 +++
 .../partner/base_investments_table.html       |  10 ++
 .../templates/partner/investments.html        |  20 +++
 .../templates/partner/partner_page.html       |  27 ++++
 .../partner/templates/partner/table.html      |  45 +++++++
 hypha/public/partner/views.py                 |  15 +++
 .../people/templates/people/person_page.html  |   9 --
 hypha/public/urls.py                          |   4 +-
 hypha/settings/base.py                        |   1 +
 .../src/sass/public/abstracts/_mixins.scss    |  67 ++++++++++
 .../src/sass/public/abstracts/_variables.scss |   3 +
 .../src/sass/public/components/_table.scss    | 100 +++++++++++++++
 hypha/static_src/src/sass/public/main.scss    |   1 +
 19 files changed, 534 insertions(+), 10 deletions(-)
 create mode 100644 hypha/public/partner/__init__.py
 create mode 100644 hypha/public/partner/admin.py
 create mode 100644 hypha/public/partner/apps.py
 create mode 100644 hypha/public/partner/migrations/0001_add_investments_table_and_partners_page.py
 create mode 100644 hypha/public/partner/migrations/__init__.py
 create mode 100644 hypha/public/partner/models.py
 create mode 100644 hypha/public/partner/tables.py
 create mode 100644 hypha/public/partner/templates/partner/base_investments_table.html
 create mode 100644 hypha/public/partner/templates/partner/investments.html
 create mode 100644 hypha/public/partner/templates/partner/partner_page.html
 create mode 100644 hypha/public/partner/templates/partner/table.html
 create mode 100644 hypha/public/partner/views.py
 create mode 100644 hypha/static_src/src/sass/public/components/_table.scss

diff --git a/hypha/public/partner/__init__.py b/hypha/public/partner/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/hypha/public/partner/admin.py b/hypha/public/partner/admin.py
new file mode 100644
index 000000000..7f74539a7
--- /dev/null
+++ b/hypha/public/partner/admin.py
@@ -0,0 +1,21 @@
+from wagtail.contrib.modeladmin.options import (
+    ModelAdmin,
+    modeladmin_register,
+)
+from .models import Investment
+
+
+class InvestmentAdmin(ModelAdmin):
+    model = Investment
+    form_fields_exclude = ('application', )
+    menu_label = 'Investments'
+    menu_icon = 'placeholder'
+    menu_order = 290
+    add_to_settings_menu = False
+    exclude_from_explorer = False
+    list_display = ('partner', 'name', 'amount_committed', 'year')
+    list_filter = ('partner__name', 'year', 'amount_committed')
+    search_fields = ('name', 'year')
+
+
+modeladmin_register(InvestmentAdmin)
diff --git a/hypha/public/partner/apps.py b/hypha/public/partner/apps.py
new file mode 100644
index 000000000..01010fa86
--- /dev/null
+++ b/hypha/public/partner/apps.py
@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+
+class PartnerConfig(AppConfig):
+    name = 'partner'
diff --git a/hypha/public/partner/migrations/0001_add_investments_table_and_partners_page.py b/hypha/public/partner/migrations/0001_add_investments_table_and_partners_page.py
new file mode 100644
index 000000000..c8ac3b68d
--- /dev/null
+++ b/hypha/public/partner/migrations/0001_add_investments_table_and_partners_page.py
@@ -0,0 +1,75 @@
+# Generated by Django 2.2.16 on 2020-09-13 22:41
+
+import django.core.validators
+from django.db import migrations, models
+import django.db.models.deletion
+import hypha.public.partner.models
+import wagtail.core.fields
+import wagtailcache.cache
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+        ('images', '0003_customimage_drupal_id'),
+        ('wagtailcore', '0045_assign_unlock_grouppagepermission'),
+        ('funds', '0078_add_heading_block_to_form_fields_block'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='PartnerPage',
+            fields=[
+                ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.Page')),
+                ('social_text', models.CharField(blank=True, max_length=255)),
+                ('listing_title', models.CharField(blank=True, help_text='Override the page title used when this page appears in listings', max_length=255)),
+                ('listing_summary', models.CharField(blank=True, help_text="The text summary used when this page appears in listings. It's also used as the description for search engines if the 'Search description' field above is not defined.", max_length=255)),
+                ('name', models.CharField(max_length=100)),
+                ('status', models.CharField(choices=[('active', 'Active'), ('inactive', 'Inactive')], default='current_partner', max_length=20)),
+                ('public', models.BooleanField(default=True)),
+                ('description', wagtail.core.fields.RichTextField(blank=True)),
+                ('web_url', models.URLField(blank=True)),
+                ('header_image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='images.CustomImage')),
+                ('listing_image', models.ForeignKey(blank=True, help_text='Choose the image you wish to be displayed when this page appears in listings', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='images.CustomImage')),
+                ('logo', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='images.CustomImage')),
+                ('social_image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='images.CustomImage')),
+            ],
+            options={
+                'verbose_name': 'Partner Page',
+            },
+            bases=(wagtailcache.cache.WagtailCacheMixin, 'wagtailcore.page', models.Model),
+        ),
+        migrations.CreateModel(
+            name='PartnerIndexPage',
+            fields=[
+                ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.Page')),
+                ('social_text', models.CharField(blank=True, max_length=255)),
+                ('listing_title', models.CharField(blank=True, help_text='Override the page title used when this page appears in listings', max_length=255)),
+                ('listing_summary', models.CharField(blank=True, help_text="The text summary used when this page appears in listings. It's also used as the description for search engines if the 'Search description' field above is not defined.", max_length=255)),
+                ('introduction', models.TextField(blank=True)),
+                ('header_image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='images.CustomImage')),
+                ('listing_image', models.ForeignKey(blank=True, help_text='Choose the image you wish to be displayed when this page appears in listings', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='images.CustomImage')),
+                ('social_image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='images.CustomImage')),
+            ],
+            options={
+                'abstract': False,
+            },
+            bases=(wagtailcache.cache.WagtailCacheMixin, 'wagtailcore.page', models.Model),
+        ),
+        migrations.CreateModel(
+            name='Investment',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=50)),
+                ('year', models.IntegerField(default=hypha.public.partner.models.current_year, help_text='Use format: <YYYY>', validators=[django.core.validators.MinValueValidator(1984), hypha.public.partner.models.max_value_current_year])),
+                ('amount_committed', models.DecimalField(decimal_places=2, default=0, max_digits=11, verbose_name='Ammount Commited US$')),
+                ('description', models.TextField()),
+                ('created_at', models.DateTimeField(auto_now_add=True)),
+                ('updated_at', models.DateTimeField(auto_now_add=True)),
+                ('application', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='funds.ApplicationSubmission')),
+                ('partner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='investments', to='partner.PartnerPage')),
+            ],
+        ),
+    ]
diff --git a/hypha/public/partner/migrations/__init__.py b/hypha/public/partner/migrations/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/hypha/public/partner/models.py b/hypha/public/partner/models.py
new file mode 100644
index 000000000..b6457e4aa
--- /dev/null
+++ b/hypha/public/partner/models.py
@@ -0,0 +1,120 @@
+import datetime
+
+from django.db import models
+from django.core.validators import MaxValueValidator, MinValueValidator
+from django.shortcuts import redirect
+
+from pagedown.widgets import PagedownWidget
+
+from wagtail.core.models import Page
+from wagtail.core.fields import RichTextField
+from wagtail.admin.edit_handlers import FieldPanel
+from wagtail.search import index
+from wagtail.images.edit_handlers import ImageChooserPanel
+
+from hypha.public.utils.models import BasePage
+from hypha.apply.funds.models import ApplicationSubmission
+
+
+class PartnerIndexPage(BasePage):
+    parent_page_types = ['standardpages.IndexPage']
+    subpage_types = ['partner.PartnerPage']
+
+    introduction = models.TextField(blank=True)
+
+    content_panels = BasePage.content_panels + [
+        FieldPanel('introduction', widget=PagedownWidget()),
+    ]
+
+    search_fields = BasePage.search_fields + [
+        index.SearchField('introduction'),
+    ]
+
+    def serve(self, request, *args, **kwargs):
+        # import ipdb; ipdb.set_trace()
+        return redirect('investments')
+        # return super().serve(request, *args, **kwargs)
+
+
+class PartnerPage(BasePage):
+    STATUS = [
+        ('active', 'Active'),
+        ('inactive', 'Inactive')
+    ]
+
+    class Meta:
+        verbose_name = "Partner Page"
+
+    parent_page_types = ['partner.PartnerIndexPage']
+    subpage_types = []
+
+    name = models.CharField(max_length=100)
+    status = models.CharField(
+        choices=STATUS, default='current_partner', max_length=20
+    )
+    public = models.BooleanField(default=True)
+    description = RichTextField(blank=True)
+    web_url = models.URLField(blank=True)
+    logo = models.OneToOneField(
+        'images.CustomImage',
+        null=True,
+        blank=True,
+        related_name='+',
+        on_delete=models.SET_NULL
+    )
+
+    content_panels = Page.content_panels + [
+        FieldPanel('name'),
+        FieldPanel('status'),
+        FieldPanel('public'),
+        FieldPanel('description'),
+        FieldPanel('web_url'),
+        ImageChooserPanel('logo'),
+    ]
+
+    def __str__(self):
+        return self.name
+
+    def get_absolute_url(self):
+        return self.url
+
+
+def current_year():
+    return datetime.date.today().year
+
+
+def max_value_current_year(value):
+    return MaxValueValidator(current_year())(value)
+
+
+class Investment(models.Model):
+
+    partner = models.ForeignKey(
+        PartnerPage,
+        on_delete=models.CASCADE,
+        related_name='investments'
+    )
+    name = models.CharField(max_length=50)
+    year = models.IntegerField(
+        default=current_year,
+        validators=[MinValueValidator(1984), max_value_current_year],
+        help_text='Use format: <YYYY>'
+    )
+    amount_committed = models.DecimalField(
+        decimal_places=2,
+        default=0,
+        max_digits=11,
+        verbose_name='Ammount Commited US$'
+    )
+    description = models.TextField()
+    application = models.OneToOneField(
+        ApplicationSubmission,
+        on_delete=models.SET_NULL,
+        blank=True, null=True
+    )
+
+    created_at = models.DateTimeField(auto_now_add=True)
+    updated_at = models.DateTimeField(auto_now_add=True)
+
+    def __str__(self):
+        return self.name
diff --git a/hypha/public/partner/tables.py b/hypha/public/partner/tables.py
new file mode 100644
index 000000000..58c04acd5
--- /dev/null
+++ b/hypha/public/partner/tables.py
@@ -0,0 +1,21 @@
+import django_tables2 as tables
+from django.utils.translation import gettext_lazy as _
+
+from .models import Investment
+
+
+class InvestmentTable(tables.Table):
+    """Table for listing investments."""
+    partner = tables.Column(verbose_name='Partner name', linkify=True)
+    name = tables.Column(verbose_name='Investment')
+    year = tables.Column(verbose_name='Year')
+    status = tables.Column(accessor='partner__status', verbose_name='Status')
+    amount_committed = tables.Column(verbose_name='Amount committed (US$)')
+
+    class Meta:
+        model = Investment
+        order_by = ('-updated_at',)
+        fields = ('partner', 'name', 'year', 'status', 'amount_committed')
+        template_name = 'partner/table.html'
+        attrs = {'class': 'all-investments-table'}
+        empty_text = _('No investments available')
diff --git a/hypha/public/partner/templates/partner/base_investments_table.html b/hypha/public/partner/templates/partner/base_investments_table.html
new file mode 100644
index 000000000..71d970299
--- /dev/null
+++ b/hypha/public/partner/templates/partner/base_investments_table.html
@@ -0,0 +1,10 @@
+{% extends "base.html" %}
+
+{% load static %}
+{% load render_table from django_tables2 %}
+
+{% block content %}
+    {% block table %}
+        {% render_table table %}
+    {% endblock %}
+{% endblock %}
diff --git a/hypha/public/partner/templates/partner/investments.html b/hypha/public/partner/templates/partner/investments.html
new file mode 100644
index 000000000..0b1afe943
--- /dev/null
+++ b/hypha/public/partner/templates/partner/investments.html
@@ -0,0 +1,20 @@
+{% extends "partner/base_investments_table.html" %}
+{% block title %}Investments{% endblock %}
+
+{% block content %}
+<div class="admin-bar">
+    <div class="admin-bar__inner wrapper--search">
+        {% block page_header %}
+            <div>
+                <h1 class="gamma heading heading--no-margin heading--bold">Investments</h1>
+            </div>
+        {% endblock %}
+    </div>
+</div>
+
+<div class="wrapper wrapper--large wrapper--inner-space-medium">
+    {% block table %}
+        {{ block.super }}
+    {% endblock %}
+</div>
+{% endblock %}
diff --git a/hypha/public/partner/templates/partner/partner_page.html b/hypha/public/partner/templates/partner/partner_page.html
new file mode 100644
index 000000000..7d81860bd
--- /dev/null
+++ b/hypha/public/partner/templates/partner/partner_page.html
@@ -0,0 +1,27 @@
+{% extends "base.html" %}
+
+{% load wagtailcore_tags wagtailimages_tags %}
+
+{% block content %}
+<section class="wrapper wrapper--small wrapper--inner-space-large wrapper--sidebar">
+    <div class="wrapper--sidebar--inner">
+        <h4 class="heading">{{ page.name }}</h4>
+
+        {{ page.description|richtext }}
+
+        <p class="list list--contact"><span>Status: </span>{{ page.get_status_display }}</p>
+
+        <p class="list list--contact"><span>Public: </span>{{ page.public|yesno:"Yes,No" }}</p>
+
+        {% if page.web_url %}
+            <p class="list list--contact"><span>Website:</span> <a href="{{ page.web_url }}">{{ page.web_url }}</a></p>
+        {% endif %}
+    </div>
+
+    <div>
+        {% if page.logo %}
+            {% image page.logo fill-210x235 class="image image--headshot-desktop" %}
+        {% endif %}
+    </div>
+</section>
+{% endblock %}
diff --git a/hypha/public/partner/templates/partner/table.html b/hypha/public/partner/templates/partner/table.html
new file mode 100644
index 000000000..8780f70a6
--- /dev/null
+++ b/hypha/public/partner/templates/partner/table.html
@@ -0,0 +1,45 @@
+{% extends 'django_tables2/table.html' %}
+{% load django_tables2 table_tags review_tags wagtailimages_tags i18n %}
+
+{% block table.tbody.row %}
+    <tr {{ row.attrs.as_html }}>
+        {% for column, cell in row.items %}
+            <td {{ column.attrs.td.as_html }}>
+                {% if column.localize == None %}{{ cell }}{% else %}{% if column.localize %}{{ cell|localize }}{% else %}{{ cell|unlocalize }}{% endif %}{% endif %}
+            </td>
+        {% endfor %}
+    </tr>
+
+{% endblock %}
+
+{% block table.tbody.empty_text %}
+<tr class="all-investments-table__empty"><td colspan="{{ table.columns|length }}">{{ table.empty_text }}</td></tr>
+{% endblock table.tbody.empty_text %}
+
+{% block pagination %}
+    {% if table.page and table.paginator.num_pages > 1 %}
+    <ul class="pagination">
+        {% if table.page.has_previous %}
+            <li class="previous">
+                <a href="{% querystring table.prefixed_page_field=table.page.previous_page_number %}">
+                    {% trans 'previous' %}
+                </a>
+            </li>
+        {% endif %}
+        {% if table.page.has_previous or table.page.has_next %}
+            <li class="cardinality">
+                <p>
+                    Page {{ table.page.number }}
+                </p>
+            </li>
+        {% endif %}
+        {% if table.page.has_next %}
+            <li class="next">
+                <a href="{% querystring table.prefixed_page_field=table.page.next_page_number %}">
+                    {% trans 'next' %}
+                </a>
+            </li>
+        {% endif %}
+    </ul>
+    {% endif %}
+{% endblock pagination %}
diff --git a/hypha/public/partner/views.py b/hypha/public/partner/views.py
new file mode 100644
index 000000000..745764922
--- /dev/null
+++ b/hypha/public/partner/views.py
@@ -0,0 +1,15 @@
+import django_tables2 as tables
+from django_tables2.views import SingleTableMixin
+from django_filters.views import FilterView
+from django.views.generic import ListView
+from django_tables2 import SingleTableView
+
+from .tables import InvestmentTable
+from .models import Investment
+
+
+class InvestmentTableView(SingleTableView):
+    model = Investment
+    table_class = InvestmentTable
+    table_pagination = {'per_page': 5}
+    template_name = 'partner/investments.html'
diff --git a/hypha/public/people/templates/people/person_page.html b/hypha/public/people/templates/people/person_page.html
index f6f605655..7ffbd3e8e 100644
--- a/hypha/public/people/templates/people/person_page.html
+++ b/hypha/public/people/templates/people/person_page.html
@@ -45,15 +45,6 @@
         {% if page.photo %}
             {% image page.photo fill-210x235 class="image image--headshot-desktop" %}
         {% endif %}
-
-        {% if page.social_media_profile.all %}
-            <h5>Follow {{ page.first_name }}</h5>
-            {% for item in page.social_media_profile.all %}
-                <a aria-label="Social media link" href="{{ item.profile_url }}">
-                    <svg class="icon icon--social-share icon--{{item.service}}-share"><use xlink:href="#{{item.service}}"></use></svg>
-                </a>
-            {% endfor %}
-        {% endif %}
     </div>
 </section>
 {% endblock %}
diff --git a/hypha/public/urls.py b/hypha/public/urls.py
index a1868e486..6e38187cf 100644
--- a/hypha/public/urls.py
+++ b/hypha/public/urls.py
@@ -3,10 +3,12 @@ from django.urls import include, path
 from .mailchimp import urls as newsletter_urls
 from .news import feeds as news_feeds
 from .search import views as search_views
+from .partner import views as partner_views
 
 urlpatterns = [
     path('search/', search_views.search, name='search'),
     path('news/feed/', news_feeds.NewsFeed(), name='news_feed'),
     path('news/<int:news_type>/feed/', news_feeds.NewsTypeFeed(), name='news_type_feed'),
-    path('newsletter/', include(newsletter_urls))
+    path('newsletter/', include(newsletter_urls)),
+    path('investments/', partner_views.InvestmentTableView.as_view(), name='investments')
 ]
diff --git a/hypha/settings/base.py b/hypha/settings/base.py
index 207161c95..6d178d402 100644
--- a/hypha/settings/base.py
+++ b/hypha/settings/base.py
@@ -93,6 +93,7 @@ INSTALLED_APPS = [
     'hypha.public.standardpages',
     'hypha.public.forms',
     'hypha.public.utils',
+    'hypha.public.partner',
 
     'social_django',
 
diff --git a/hypha/static_src/src/sass/public/abstracts/_mixins.scss b/hypha/static_src/src/sass/public/abstracts/_mixins.scss
index 1e47f7eff..405db5f95 100644
--- a/hypha/static_src/src/sass/public/abstracts/_mixins.scss
+++ b/hypha/static_src/src/sass/public/abstracts/_mixins.scss
@@ -176,3 +176,70 @@
         border-bottom: $perpendicular-borders;
     }
 }
+
+@mixin table-ordering-styles {
+    thead {
+        th {
+            // ordering
+            &.desc,
+            &.asc {
+                position: relative;
+                color: $color--dark-grey;
+
+                &::after {
+                    position: absolute;
+                    top: 50%;
+                    margin-left: 3px;
+                }
+
+                a {
+                    color: inherit;
+                }
+            }
+
+            &.desc {
+                &::after {
+                    @include triangle(top, $color--default, 5px);
+                }
+            }
+
+            &.asc {
+                &::after {
+                    @include triangle(bottom, $color--default, 5px);
+                }
+            }
+        }
+    }
+}
+
+@mixin column-scrolling {
+    @include media-query(tablet-landscape) {
+        height: calc(100vh - var(--header-admin-height) - #{$listing-header-height});
+        overflow-y: scroll;
+    }
+
+    @include media-query(laptop-short) {
+        // allow for vertical scrolling on laptops
+        height: calc(100vh -  #{$listing-header-height});
+    }
+}
+
+@mixin table-checkbox {
+    input[type='checkbox'] {
+        margin: 0 auto;
+        display: block;
+        width: 20px;
+        height: 20px;
+        border: 1px solid $color--mid-grey;
+        -webkit-appearance: none; // sass-lint:disable-line no-vendor-prefixes
+        -moz-appearance: none; // sass-lint:disable-line no-vendor-prefixes
+        appearance: none;
+        background-color: $color--white;
+
+        &:checked {
+            background: url('./../../images/tick.svg') $color--dark-blue center no-repeat;
+            background-size: 12px;
+            border: 1px solid $color--dark-blue;
+        }
+    }
+}
diff --git a/hypha/static_src/src/sass/public/abstracts/_variables.scss b/hypha/static_src/src/sass/public/abstracts/_variables.scss
index 931be93ec..4eba93625 100644
--- a/hypha/static_src/src/sass/public/abstracts/_variables.scss
+++ b/hypha/static_src/src/sass/public/abstracts/_variables.scss
@@ -128,3 +128,6 @@ $table-breakpoint: 'tablet-landscape';
 
 // Dropdown height
 $dropdown-height: 45px;
+
+// Table breakpoint
+$table-breakpoint: 'tablet-landscape';
diff --git a/hypha/static_src/src/sass/public/components/_table.scss b/hypha/static_src/src/sass/public/components/_table.scss
new file mode 100644
index 000000000..5bb173ed3
--- /dev/null
+++ b/hypha/static_src/src/sass/public/components/_table.scss
@@ -0,0 +1,100 @@
+// base table styles - specific ones in their own scss partial
+table {
+    width: 100%;
+    background-color: $color--white;
+    border-collapse: collapse;
+    table-layout: fixed;
+
+    // table head
+    th {
+        padding: 20px 15px;
+        font-size: 15px;
+        font-weight: 600;
+        text-align: left;
+
+        a {
+            color: $color--black-60;
+            transition: color .25s ease-out;
+        }
+    }
+
+    // table rows
+    tr {
+        border: 1px solid $color--light-mid-grey;
+        transition: box-shadow .15s ease;
+
+        @include media-query($table-breakpoint) {
+            border-top: 0;
+            border-right: 0;
+            border-bottom: 2px solid $color--light-grey;
+            border-left: 0;
+
+            &.is-expanded {
+                border-bottom: 1px solid $color--light-mid-grey;
+
+                .lead {
+                    span {
+                        background-color: $color--mist;
+                    }
+                }
+            }
+
+            &:hover {
+                box-shadow: 0 6px 35px -13px $color--black-50;
+            }
+
+            &.reviews-summary__tr {
+                box-shadow: none;
+            }
+        }
+
+        // responsive table styles
+        > td {
+            display: block;
+            width: 100%;
+
+            @include media-query($table-breakpoint) {
+                display: table-cell;
+                width: initial;
+            }
+
+            &.lead {
+                span {
+                    @include media-query($table-breakpoint) {
+                        position: relative;
+                        z-index: 1;
+                        display: block;
+                        padding-right: 5px;
+                        overflow: hidden;
+                        text-overflow: ellipsis;
+                        background: $color--white;
+
+                        &:hover {
+                            display: inline-block;
+                            overflow: visible;
+                        }
+                    }
+                }
+            }
+
+            &.title {
+                a {
+                    font-weight: $weight--bold;
+                }
+            }
+        }
+    }
+
+    td,
+    th {
+        padding: 5px 20px;
+
+        @include media-query($table-breakpoint) {
+            padding: 15px 10px;
+        }
+
+        &.title {
+            padding-left: 20px;
+        }
+    }
+}
diff --git a/hypha/static_src/src/sass/public/main.scss b/hypha/static_src/src/sass/public/main.scss
index d97cf885f..4c56f2abf 100644
--- a/hypha/static_src/src/sass/public/main.scss
+++ b/hypha/static_src/src/sass/public/main.scss
@@ -37,6 +37,7 @@
 @import 'components/section';
 @import 'components/select2';
 @import 'components/show-more';
+@import 'components/table';
 @import 'components/wrapper';
 
 // Layout
-- 
GitLab