Browse Source

"My Projects" page.

pull/217/head
Pēteris Caune 6 years ago
parent
commit
6b0d566922
No known key found for this signature in database GPG Key ID: E28D7679E9A9EDE2
21 changed files with 205 additions and 153 deletions
  1. +1
    -1
      hc/accounts/admin.py
  2. +15
    -7
      hc/accounts/tests/test_check_token.py
  3. +8
    -4
      hc/accounts/tests/test_login.py
  4. +0
    -50
      hc/accounts/tests/test_switch_project.py
  5. +0
    -3
      hc/accounts/urls.py
  6. +2
    -29
      hc/accounts/views.py
  7. +12
    -12
      hc/front/tests/test_add_check.py
  8. +28
    -10
      hc/front/tests/test_my_checks.py
  9. +3
    -2
      hc/front/tests/test_pause.py
  10. +3
    -2
      hc/front/tests/test_remove_check.py
  11. +3
    -2
      hc/front/tests/test_update_name.py
  12. +4
    -3
      hc/front/tests/test_update_timeout.py
  13. +2
    -2
      hc/front/urls.py
  14. +43
    -19
      hc/front/views.py
  15. +15
    -0
      static/css/projects.css
  16. +1
    -1
      templates/accounts/profile.html
  17. +1
    -1
      templates/admin/profile_list_projects.html
  18. +4
    -3
      templates/base.html
  19. +1
    -1
      templates/front/log.html
  20. +1
    -1
      templates/front/my_checks.html
  21. +58
    -0
      templates/front/projects.html

+ 1
- 1
hc/accounts/admin.py View File

@ -163,7 +163,7 @@ class ProjectAdmin(admin.ModelAdmin):
@mark_safe
def switch(self, obj):
url = reverse("hc-switch-project", args=[obj.code])
url = reverse("hc-checks", args=[obj.code])
return "<a href='%s'>Show Checks</a>" % url


+ 15
- 7
hc/accounts/tests/test_check_token.py View File

@ -9,13 +9,17 @@ class CheckTokenTestCase(BaseTestCase):
self.profile.token = make_password("secret-token", "login")
self.profile.save()
self.checks_url = "/projects/%s/checks/" % self.project.code
def test_it_shows_form(self):
r = self.client.get("/accounts/check_token/alice/secret-token/")
self.assertContains(r, "You are about to log in")
def test_it_redirects(self):
r = self.client.post("/accounts/check_token/alice/secret-token/")
self.assertRedirects(r, "/checks/")
r = self.client.post("/accounts/check_token/alice/secret-token/",
follow=True)
self.assertRedirects(r, self.checks_url)
# After login, token should be blank
self.profile.refresh_from_db()
@ -26,8 +30,10 @@ class CheckTokenTestCase(BaseTestCase):
self.client.login(username="[email protected]", password="password")
# Login again, when already authenticated
r = self.client.post("/accounts/check_token/alice/secret-token/")
self.assertRedirects(r, "/checks/")
r = self.client.post("/accounts/check_token/alice/secret-token/",
follow=True)
self.assertRedirects(r, self.checks_url)
def test_it_redirects_bad_login(self):
# Login with a bad token
@ -37,9 +43,11 @@ class CheckTokenTestCase(BaseTestCase):
self.assertContains(r, "incorrect or expired")
def test_it_handles_next_parameter(self):
r = self.client.post("/accounts/check_token/alice/secret-token/?next=/integrations/add_slack/")
url = "/accounts/check_token/alice/secret-token/?next=/integrations/add_slack/"
r = self.client.post(url)
self.assertRedirects(r, "/integrations/add_slack/")
def test_it_ignores_bad_next_parameter(self):
r = self.client.post("/accounts/check_token/alice/secret-token/?next=/evil/")
self.assertRedirects(r, "/checks/")
url = "/accounts/check_token/alice/secret-token/?next=/evil/"
r = self.client.post(url, follow=True)
self.assertRedirects(r, self.checks_url)

+ 8
- 4
hc/accounts/tests/test_login.py View File

@ -6,6 +6,10 @@ from hc.test import BaseTestCase
class LoginTestCase(BaseTestCase):
def setUp(self):
super(LoginTestCase, self).setUp()
self.checks_url = "/projects/%s/checks/" % self.project.code
def test_it_sends_link(self):
form = {"identity": "[email protected]"}
@ -49,8 +53,8 @@ class LoginTestCase(BaseTestCase):
"password": "password"
}
r = self.client.post("/accounts/login/", form)
self.assertRedirects(r, "/checks/")
r = self.client.post("/accounts/login/", form, follow=True)
self.assertRedirects(r, self.checks_url)
def test_it_handles_password_login_with_redirect(self):
check = Check.objects.create(project=self.project)
@ -77,8 +81,8 @@ class LoginTestCase(BaseTestCase):
"password": "password"
}
r = self.client.post("/accounts/login/?next=/evil/", form)
self.assertRedirects(r, "/checks/")
r = self.client.post("/accounts/login/?next=/evil/", form, follow=True)
self.assertRedirects(r, self.checks_url)
def test_it_handles_wrong_password(self):
form = {


+ 0
- 50
hc/accounts/tests/test_switch_project.py View File

@ -1,50 +0,0 @@
from hc.test import BaseTestCase
from hc.api.models import Check
class SwitchTeamTestCase(BaseTestCase):
def setUp(self):
super(SwitchTeamTestCase, self).setUp()
self.url = "/accounts/switch_project/%s/" % self.project.code
def test_it_switches(self):
self.bobs_profile.current_project = None
self.bobs_profile.save()
c = Check(project=self.project, name="This belongs to Alice")
c.save()
self.client.login(username="[email protected]", password="password")
r = self.client.get(self.url, follow=True)
self.assertContains(r, "This belongs to Alice")
self.bobs_profile.refresh_from_db()
self.assertEqual(self.bobs_profile.current_project, self.project)
def test_it_checks_team_membership(self):
self.client.login(username="[email protected]", password="password")
r = self.client.get(self.url)
self.assertEqual(r.status_code, 403)
def test_it_switches_to_own_team(self):
self.client.login(username="[email protected]", password="password")
r = self.client.get(self.url, follow=True)
self.assertEqual(r.status_code, 200)
def test_it_handles_invalid_project_code(self):
self.client.login(username="[email protected]", password="password")
url = "/accounts/switch_project/6837d6ec-fc08-4da5-a67f-08a9ed1ccf62/"
r = self.client.get(url)
self.assertEqual(r.status_code, 404)
def test_it_requires_login(self):
r = self.client.get(self.url)
expected_url = "/accounts/login/?next=" + self.url
self.assertRedirects(r, expected_url)

+ 0
- 3
hc/accounts/urls.py View File

@ -31,7 +31,4 @@ urlpatterns = [
path('change_email/<slug:token>/',
views.change_email, name="hc-change-email"),
path('switch_project/<uuid:code>/',
views.switch_project, name="hc-switch-project"),
]

+ 2
- 29
hc/accounts/views.py View File

@ -16,7 +16,6 @@ from django.utils.timezone import now
from django.urls import resolve, Resolver404
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
from django.shortcuts import get_object_or_404
from hc.accounts.forms import (ChangeEmailForm, EmailPasswordForm,
InviteTeamMemberForm, RemoveTeamMemberForm,
ReportSettingsForm, SetPasswordForm,
@ -81,7 +80,7 @@ def _redirect_after_login(request):
if _is_whitelisted(redirect_url):
return redirect(redirect_url)
return redirect("hc-checks")
return redirect("hc-index")
def login(request):
@ -228,7 +227,7 @@ def add_project(request):
project.name = form.cleaned_data["name"]
project.save()
return redirect("hc-switch-project", project.code)
return redirect("hc-checks", project.code)
@login_required
@ -453,32 +452,6 @@ def unsubscribe_reports(request, username):
return render(request, "accounts/unsubscribed.html")
@login_required
def switch_project(request, code):
project = get_object_or_404(Project, code=code)
# The rules:
# Superuser can switch to any team.
access_ok = request.user.is_superuser
# Users can switch to their own projects.
if not access_ok and project.owner_id == request.user.id:
access_ok = True
# Users can switch to projects they are members of.
if not access_ok:
q = project.member_set.filter(user=request.user)
access_ok = q.exists()
if not access_ok:
return HttpResponseForbidden()
request.profile.current_project = project
request.profile.save()
return redirect("hc-checks")
@require_POST
@login_required
def close(request):


+ 12
- 12
hc/front/tests/test_add_check.py View File

@ -3,12 +3,16 @@ from hc.test import BaseTestCase
class AddCheckTestCase(BaseTestCase):
def setUp(self):
super(AddCheckTestCase, self).setUp()
self.url = "/projects/%s/checks/add/" % self.project.code
self.redirect_url = "/projects/%s/checks/" % self.project.code
def test_it_works(self):
url = "/checks/add/"
self.client.login(username="[email protected]", password="password")
r = self.client.post(url)
self.assertRedirects(r, "/checks/")
r = self.client.post(self.url)
self.assertRedirects(r, self.redirect_url)
check = Check.objects.get()
self.assertEqual(check.project, self.project)
@ -16,33 +20,29 @@ class AddCheckTestCase(BaseTestCase):
self.profile.current_project = None
self.profile.save()
url = "/checks/add/"
self.client.login(username="[email protected]", password="password")
r = self.client.post(url)
self.assertRedirects(r, "/checks/")
r = self.client.post(self.url)
self.assertRedirects(r, self.redirect_url)
check = Check.objects.get()
self.assertEqual(check.project, self.project)
def test_team_access_works(self):
url = "/checks/add/"
self.client.login(username="[email protected]", password="password")
self.client.post(url)
self.client.post(self.url)
check = Check.objects.get()
# Added by bob, but should belong to alice (bob has team access)
self.assertEqual(check.project, self.project)
def test_it_rejects_get(self):
url = "/checks/add/"
self.client.login(username="[email protected]", password="password")
r = self.client.get(url)
r = self.client.get(self.url)
self.assertEqual(r.status_code, 405)
def test_it_obeys_check_limit(self):
self.profile.check_limit = 0
self.profile.save()
url = "/checks/add/"
self.client.login(username="[email protected]", password="password")
r = self.client.post(url)
r = self.client.post(self.url)
self.assertEqual(r.status_code, 400)

+ 28
- 10
hc/front/tests/test_my_checks.py View File

@ -11,19 +11,37 @@ class MyChecksTestCase(BaseTestCase):
self.check = Check(project=self.project, name="Alice Was Here")
self.check.save()
self.url = "/projects/%s/checks/" % self.project.code
def test_it_works(self):
for email in ("[email protected]", "[email protected]"):
self.client.login(username=email, password="password")
r = self.client.get("/checks/")
r = self.client.get(self.url)
self.assertContains(r, "Alice Was Here", status_code=200)
def test_it_updates_current_project(self):
self.profile.current_project = None
self.profile.save()
self.client.login(username="[email protected]", password="password")
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
self.profile.refresh_from_db()
self.assertEqual(self.profile.current_project, self.project)
def test_it_checks_access(self):
self.client.login(username="[email protected]", password="password")
r = self.client.get(self.url)
self.assertEqual(r.status_code, 404)
def test_it_shows_green_check(self):
self.check.last_ping = timezone.now()
self.check.status = "up"
self.check.save()
self.client.login(username="[email protected]", password="password")
r = self.client.get("/checks/")
r = self.client.get(self.url)
self.assertContains(r, "icon-up")
def test_it_shows_red_check(self):
@ -32,7 +50,7 @@ class MyChecksTestCase(BaseTestCase):
self.check.save()
self.client.login(username="[email protected]", password="password")
r = self.client.get("/checks/")
r = self.client.get(self.url)
self.assertContains(r, "icon-down")
def test_it_shows_amber_check(self):
@ -41,7 +59,7 @@ class MyChecksTestCase(BaseTestCase):
self.check.save()
self.client.login(username="[email protected]", password="password")
r = self.client.get("/checks/")
r = self.client.get(self.url)
self.assertContains(r, "icon-grace")
def test_it_hides_add_check_button(self):
@ -49,19 +67,19 @@ class MyChecksTestCase(BaseTestCase):
self.profile.save()
self.client.login(username="[email protected]", password="password")
r = self.client.get("/checks/")
r = self.client.get(self.url)
self.assertContains(r, "Check limit reached", status_code=200)
def test_it_saves_sort_field(self):
self.client.login(username="[email protected]", password="password")
self.client.get("/checks/?sort=name")
self.client.get(self.url + "?sort=name")
self.profile.refresh_from_db()
self.assertEqual(self.profile.sort, "name")
def test_it_ignores_bad_sort_value(self):
self.client.login(username="[email protected]", password="password")
self.client.get("/checks/?sort=invalid")
self.client.get(self.url + "?sort=invalid")
self.profile.refresh_from_db()
self.assertEqual(self.profile.sort, "created")
@ -73,7 +91,7 @@ class MyChecksTestCase(BaseTestCase):
self.check.save()
self.client.login(username="[email protected]", password="password")
r = self.client.get("/checks/")
r = self.client.get(self.url)
self.assertContains(r, """<div class="btn btn-xs down ">foo</div>""")
def test_it_shows_grace_badge(self):
@ -83,7 +101,7 @@ class MyChecksTestCase(BaseTestCase):
self.check.save()
self.client.login(username="[email protected]", password="password")
r = self.client.get("/checks/")
r = self.client.get(self.url)
self.assertContains(r, """<div class="btn btn-xs grace ">foo</div>""")
def test_it_shows_grace_started_badge(self):
@ -94,5 +112,5 @@ class MyChecksTestCase(BaseTestCase):
self.check.save()
self.client.login(username="[email protected]", password="password")
r = self.client.get("/checks/")
r = self.client.get(self.url)
self.assertContains(r, """<div class="btn btn-xs grace ">foo</div>""")

+ 3
- 2
hc/front/tests/test_pause.py View File

@ -11,11 +11,12 @@ class PauseTestCase(BaseTestCase):
super(PauseTestCase, self).setUp()
self.check = Check.objects.create(project=self.project, status="up")
self.url = "/checks/%s/pause/" % self.check.code
self.redirect_url = "/projects/%s/checks/" % self.project.code
def test_it_pauses(self):
self.client.login(username="[email protected]", password="password")
r = self.client.post(self.url)
self.assertRedirects(r, "/checks/")
self.assertRedirects(r, self.redirect_url)
self.check.refresh_from_db()
self.assertEqual(self.check.status, "paused")
@ -31,7 +32,7 @@ class PauseTestCase(BaseTestCase):
self.client.login(username="[email protected]", password="password")
r = self.client.post(self.url)
self.assertRedirects(r, "/checks/")
self.assertRedirects(r, self.redirect_url)
def test_it_clears_last_start_alert_after(self):
self.check.last_start = now()


+ 3
- 2
hc/front/tests/test_remove_check.py View File

@ -8,11 +8,12 @@ class RemoveCheckTestCase(BaseTestCase):
super(RemoveCheckTestCase, self).setUp()
self.check = Check.objects.create(project=self.project)
self.remove_url = "/checks/%s/remove/" % self.check.code
self.redirect_url = "/projects/%s/checks/" % self.project.code
def test_it_works(self):
self.client.login(username="[email protected]", password="password")
r = self.client.post(self.remove_url)
self.assertRedirects(r, "/checks/")
self.assertRedirects(r, self.redirect_url)
self.assertEqual(Check.objects.count(), 0)
@ -53,4 +54,4 @@ class RemoveCheckTestCase(BaseTestCase):
self.client.login(username="[email protected]", password="password")
r = self.client.post(self.remove_url)
self.assertRedirects(r, "/checks/")
self.assertRedirects(r, self.redirect_url)

+ 3
- 2
hc/front/tests/test_update_name.py View File

@ -9,11 +9,12 @@ class UpdateNameTestCase(BaseTestCase):
self.check = Check.objects.create(project=self.project)
self.url = "/checks/%s/name/" % self.check.code
self.redirect_url = "/projects/%s/checks/" % self.project.code
def test_it_works(self):
self.client.login(username="[email protected]", password="password")
r = self.client.post(self.url, data={"name": "Alice Was Here"})
self.assertRedirects(r, "/checks/")
self.assertRedirects(r, self.redirect_url)
self.check.refresh_from_db()
self.assertEqual(self.check.name, "Alice Was Here")
@ -37,7 +38,7 @@ class UpdateNameTestCase(BaseTestCase):
# But this should still work:
self.client.login(username="[email protected]", password="password")
r = self.client.post(self.url, data={"name": "Bob Was Here"})
self.assertRedirects(r, "/checks/")
self.assertRedirects(r, self.redirect_url)
def test_it_checks_ownership(self):
payload = {"name": "Charlie Sent This"}


+ 4
- 3
hc/front/tests/test_update_timeout.py View File

@ -14,13 +14,14 @@ class UpdateTimeoutTestCase(BaseTestCase):
self.check.save()
self.url = "/checks/%s/timeout/" % self.check.code
self.redirect_url = "/projects/%s/checks/" % self.project.code
def test_it_works(self):
payload = {"kind": "simple", "timeout": 3600, "grace": 60}
self.client.login(username="[email protected]", password="password")
r = self.client.post(self.url, data=payload)
self.assertRedirects(r, "/checks/")
self.assertRedirects(r, self.redirect_url)
self.check.refresh_from_db()
self.assertEqual(self.check.kind, "simple")
@ -55,7 +56,7 @@ class UpdateTimeoutTestCase(BaseTestCase):
self.client.login(username="[email protected]", password="password")
r = self.client.post(self.url, data=payload)
self.assertRedirects(r, "/checks/")
self.assertRedirects(r, self.redirect_url)
self.check.refresh_from_db()
self.assertEqual(self.check.kind, "cron")
@ -184,4 +185,4 @@ class UpdateTimeoutTestCase(BaseTestCase):
self.client.login(username="[email protected]", password="password")
r = self.client.post(self.url, data=payload)
self.assertRedirects(r, "/checks/")
self.assertRedirects(r, self.redirect_url)

+ 2
- 2
hc/front/urls.py View File

@ -48,8 +48,8 @@ channel_urls = [
urlpatterns = [
path('', views.index, name="hc-index"),
path('checks/', views.my_checks, name="hc-checks"),
path('checks/add/', views.add_check, name="hc-add-check"),
path('projects/<uuid:code>/checks/', views.my_checks, name="hc-checks"),
path('projects/<uuid:code>/checks/add/', views.add_check, name="hc-add-check"),
path('checks/cron_preview/', views.cron_preview),
path('projects/<uuid:code>/checks/status/', views.status, name="hc-status"),
path('checks/<uuid:code>/', include(check_urls)),


+ 43
- 19
hc/front/views.py View File

@ -17,6 +17,7 @@ from django.utils import timezone
from django.utils.crypto import get_random_string
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
from hc.accounts.models import Project
from hc.api.models import (DEFAULT_GRACE, DEFAULT_TIMEOUT, Channel, Check,
Ping, Notification)
from hc.api.transports import Telegram
@ -78,30 +79,41 @@ def _get_check_for_user(request, code):
raise Http404("not found")
def _has_access(request, project_code):
def _get_project_for_user(request, project_code):
""" Return true if current user has access to the specified account. """
if request.user.is_superuser:
return True
q = Project.objects.all
else:
q = request.profile.projects()
projects = request.profile.projects()
return projects.filter(code=project_code).exists()
try:
return q.get(code=project_code)
except Project.DoesNotExist:
raise Http404("not found")
@login_required
def my_checks(request):
def my_checks(request, code):
project = _get_project_for_user(request, code)
if request.GET.get("sort") in VALID_SORT_VALUES:
request.profile.sort = request.GET["sort"]
request.profile.save()
checks = list(Check.objects.filter(project=request.project).prefetch_related("channel_set"))
if request.profile.current_project_id != project.id:
request.profile.current_project = project
request.profile.save()
q = Check.objects.filter(project=project)
checks = list(q.prefetch_related("channel_set"))
sortchecks(checks, request.profile.sort)
tags_statuses, num_down = _tags_statuses(checks)
pairs = list(tags_statuses.items())
pairs.sort(key=lambda pair: pair[0].lower())
channels = Channel.objects.filter(project=request.project)
channels = Channel.objects.filter(project=project)
channels = list(channels.order_by("created"))
hidden_checks = set()
@ -129,7 +141,8 @@ def my_checks(request):
"tags": pairs,
"ping_endpoint": settings.PING_ENDPOINT,
"timezones": pytz.all_timezones,
"num_available": request.project.num_checks_available(),
"project": project,
"num_available": project.num_checks_available(),
"sort": request.profile.sort,
"selected_tags": selected_tags,
"show_search": True,
@ -142,8 +155,7 @@ def my_checks(request):
@login_required
def status(request, code):
if not _has_access(request, code):
raise Http404("not found")
_get_project_for_user(request, code)
checks = list(Check.objects.filter(project__code=code))
@ -183,7 +195,17 @@ def switch_channel(request, code, channel_code):
def index(request):
if request.user.is_authenticated:
return redirect("hc-checks")
projects = list(request.profile.projects())
if len(projects) == 1:
return redirect("hc-checks", projects[0].code)
ctx = {
"page": "projects",
"show_plans": settings.USE_PAYMENTS,
"projects": projects
}
return render(request, "front/projects.html", ctx)
check = Check()
@ -242,16 +264,17 @@ def docs_resources(request):
@require_POST
@login_required
def add_check(request):
if request.project.num_checks_available() <= 0:
def add_check(request, code):
project = _get_project_for_user(request, code)
if project.num_checks_available() <= 0:
return HttpResponseBadRequest()
check = Check(project=request.project)
check = Check(project=project)
check.save()
check.assign_all_channels()
return redirect("hc-checks")
return redirect("hc-checks", code)
@require_POST
@ -268,7 +291,7 @@ def update_name(request, code):
if "/details/" in request.META.get("HTTP_REFERER", ""):
return redirect("hc-details", code)
return redirect("hc-checks")
return redirect("hc-checks", check.project.code)
@require_POST
@ -313,7 +336,7 @@ def update_timeout(request, code):
if "/details/" in request.META.get("HTTP_REFERER", ""):
return redirect("hc-details", code)
return redirect("hc-checks")
return redirect("hc-checks", check.project.code)
@require_POST
@ -369,15 +392,16 @@ def pause(request, code):
if "/details/" in request.META.get("HTTP_REFERER", ""):
return redirect("hc-details", code)
return redirect("hc-checks")
return redirect("hc-checks", check.project.code)
@require_POST
@login_required
def remove_check(request, code):
check = _get_check_for_user(request, code)
project = check.project
check.delete()
return redirect("hc-checks")
return redirect("hc-checks", project.code)
def _get_events(check, limit):


+ 15
- 0
static/css/projects.css View File

@ -0,0 +1,15 @@
#my-projects a {
display: block;
color: #333;
}
#my-projects a:hover {
text-decoration: none;
}
#my-projects a:hover .panel {
border-color: #0091EA;
}

+ 1
- 1
templates/accounts/profile.html View File

@ -79,7 +79,7 @@
<div class="name">{{ project }}</div>
</td>
<td>
<a class="num-checks" href="{% url 'hc-switch-project' project.code %}">
<a class="num-checks" href="{% url 'hc-checks' project.code %}">
{% with project.check_set.count as n %}
{{ n }} check{{ n|pluralize }}
{% endwith %}


+ 1
- 1
templates/admin/profile_list_projects.html View File

@ -1,5 +1,5 @@
<ul>
{% for project in profile.user.project_set.all %}
<li><a href="{% url 'hc-switch-project' project.code %}">{{ project }}</a></li>
<li><a href="{% url 'hc-checks' project.code %}">{{ project }}</a></li>
{% endfor %}
</ul>

+ 4
- 3
templates/base.html View File

@ -43,6 +43,7 @@
<link rel="stylesheet" href="{% static 'css/radio.css' %}" type="text/css">
<link rel="stylesheet" href="{% static 'css/billing.css' %}" type="text/css">
<link rel="stylesheet" href="{% static 'css/login.css' %}" type="text/css">
<link rel="stylesheet" href="{% static 'css/projects.css' %}" type="text/css">
{% endcompress %}
</head>
<body class="page-{{ page }}">
@ -87,9 +88,9 @@
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul id="nav-main-sections" class="nav navbar-nav">
{% if request.user.is_authenticated %}
{% if request.user.is_authenticated and request.project %}
<li {% if page == 'checks' %} class="active" {% endif %}>
<a href="{% url 'hc-checks' %}">Checks</a>
<a href="{% url 'hc-checks' request.project.code %}">Checks</a>
</li>
<li {% if page == 'channels' %} class="active" {% endif %}>
@ -125,7 +126,7 @@
{% for project in request.profile.projects.all %}
<li class="dropdown-header">{{ project }}</li>
<li>
<a href="{% url 'hc-switch-project' project.code %}">Checks</a>
<a href="{% url 'hc-checks' project.code %}">Checks</a>
</li>
{% if project.owner == request.user %}
<li>


+ 1
- 1
templates/front/log.html View File

@ -9,7 +9,7 @@
<div class="row">
<div class="col-sm-12">
<ol class="breadcrumb">
<li><a href="{% url 'hc-checks' %}">Checks</a></li>
<li><a href="{% url 'hc-checks' check.project.code %}">Checks</a></li>
<li>
<a href="{% url 'hc-details' check.code %}">
{{ check.name_then_code }}


+ 1
- 1
templates/front/my_checks.html View File

@ -26,7 +26,7 @@
<div class="row">
<div class="col-sm-12">
{% if num_available > 0 %}
<form method="post" action="{% url 'hc-add-check' %}" class="text-center">
<form method="post" action="{% url 'hc-add-check' project.code %}" class="text-center">
{% csrf_token %}
<input type="submit" class="btn btn-primary btn-lg" value="Add Check">
</form>


+ 58
- 0
templates/front/projects.html View File

@ -0,0 +1,58 @@
{% extends "base.html" %}
{% load compress static hc_extras %}
{% block title %}Project Settings - {{ project }}{% endblock %}
{% block content %}
<div class="row">
<div class="col-sm-12">
<h1 class="settings-title">My Projects</h1>
{% for message in messages %}
<p class="alert alert-{{ message.tags }}">{{ message }}</p>
{% endfor %}
<div id="my-projects" class="row">
{% for project in projects%}
<a href="{% url 'hc-checks' project.code %}">
<div class="col-sm-6 col-md-4">
<div class="panel panel-default project-panel">
<div class="panel-body">
<h4>{{ project }}</h4>
<div>
{% with project.check_set.count as n %}
{{ n }} check{{ n|pluralize }},
{% endwith %}
{% with project.channel_set.count as n %}
{{ n }} integration{{ n|pluralize }}
{% endwith %}
</div>
{% if show_plans %}
<div class="text-muted">
{% if project.owner.subscription %}
Plan: {{ project.owner.subscription.plan_name }}
{% else %}
Plan: Hobbyist
{% endif %}
</div>
{% endif %}
</div>
</div>
</div>
</a>
{% endfor %}
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
{% compress js %}
<script src="{% static 'js/jquery-2.1.4.min.js' %}"></script>
<script src="{% static 'js/bootstrap.min.js' %}"></script>
<script src="{% static 'js/project.js' %}"></script>
{% endcompress %}
{% endblock %}

Loading…
Cancel
Save