Skip to content
Closed

main #22

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 58 additions & 3 deletions twisted/twisted_site/templates/admin/user.html
Original file line number Diff line number Diff line change
Expand Up @@ -202,9 +202,64 @@ <h1 class="text-2xl font-black">Admin
<h1 class="text-lg font-bold">Not an admin!</h1>
<p>This user is not an admin yet!</p>
{% if request.user.profile.staff_permissions.superuser %}
<c-ui.button class="mt-2">
Make admin
</c-ui.button>
<c-admin.dialog title="Are you sure?" class="mt-2">
<c-slot name="trigger">
<c-ui.button>Make admin</c-ui.button>
</c-slot>
<div class="w-full">
<div class="w-full flex gap-2">
<div class="flex-1">
<c-ui.input id="textCopyInput" disabled value="Yes, I am sure, make @{{ user.profile.slack_username }} an admin." />
</div>
<c-ui.button onclick="copyAction(this)">
<img class="size-4" src="https://icons.hackclub.com/api/icons/white/copy">
</c-ui.button>
<script>
function copyAction(elem) {
copyText = document.getElementById('textCopyInput').value
navigator.clipboard.writeText(copyText)

currentHTML = elem.innerHTML
elem.innerHTML = "Copied!"
elem.classList.add('bg-success/50!')
setTimeout(() => {
elem.innerHTML = currentHTML
elem.classList.remove('bg-success/50!')
}, 500)
}
</script>
</div>
<div class="mt-2">Type the text in the input above to confirm this action</div>
<c-ui.input oninput="makeAdminInputChange(this)" />
<script>
function makeAdminInputChange(elem) {
text = elem.value
copyText = document.getElementById('textCopyInput').value
enabledButton = document.getElementById('enabledButton')
disabledButton = document.getElementById('disabledButton')
if (text == copyText) {
enabledButton.classList.remove('hidden')
disabledButton.classList.add('hidden')
} else {
enabledButton.classList.add('hidden')
disabledButton.classList.remove('hidden')
}
}
</script>
<div class="mt-4 flex justify-end">
<c-ui.button id="disabledButton">
Confirm
</c-ui.button>
<form method="post">
{% csrf_token %}
<input type="hidden" name="action" value="make_admin">
<c-ui.button type="submit" id="enabledButton" class="hidden" variant="primary">
Confirm
</c-ui.button>
</form>
</div>
</div>
</c-admin.dialog>
{% endif %}
</c-ui.card>
{% endif %}
Expand Down
2 changes: 2 additions & 0 deletions twisted/twisted_site/templates/client/projects/detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,13 @@ <h1 class="wrap-break-word text-xl font-bold">{{ project.project_name }}</h1>
<div class="mb-3 border border-warning bg-warning/10 p-3">
<div class="font-bold text-warning">Log more time!</div>
<div class="mt-1 text-sm text-muted-foreground">Log atleast 60 minutes before shipping your project.</div>
{% if project.user == user %}
<button type="button"
onclick="createWindow('journal: ' + document.getElementById('project-title').value, '{% url 'fr.projects.journals.new.hackatime' project.pk %}')"
class="mt-4 border border-border bg-primary px-4 py-1.5 text-sm font-bold text-primary-foreground hover:bg-primary/80">
+ New journal
</button>
{% endif %}
</div>
{% endif %}
{% endif %}
Expand Down
39 changes: 39 additions & 0 deletions twisted/twisted_site/templates/cotton/admin/dialog.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<div x-data="{ open: false }" {{ attrs }}>
<div @click="open = true">
{{ trigger }}
</div>
<div
x-show="open"
@keydown.escape.window="open = false"
class="fixed inset-0 z-50 flex items-center justify-center overflow-y-auto"
x-cloak
>
<!-- Backdrop Overlay -->
<div
x-show="open"
x-transition.opacity.duration.300ms
class="fixed inset-0 bg-black/60 bg-opacity-50"
></div>

<!-- 3. Dialog Panel Container -->
<div
x-show="open"
x-transition:enter="transition ease-out duration-300"
x-transition:enter-start="opacity-0 scale-95"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-200"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-95"
@click.outside="open = false"
class="relative w-full max-w-md p-6 mx-4 bg-surface rounded-lg shadow-xl z-10"
>
<!-- Title -->
<h3 class="text-lg font-bold">{{ title }}</h3>

<!-- Content -->
<div class="mt-2 text-sm text-gray-500">
{{ slot }}
</div>
</div>
</div>
</div>
116 changes: 74 additions & 42 deletions twisted/twisted_site/views/admin/admin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from dataclasses import dataclass
from typing import Any, override

from django.contrib import messages
from django.http import HttpRequest, HttpResponseBase
from django.shortcuts import redirect, resolve_url
from django.views import View
Expand All @@ -19,58 +20,84 @@ class SidebarLink:
# Create your views here.
class AdminView(View):
audit_log: AuditLog # pyright: ignore[reportUninitializedInstanceVariable]
perms: ProfileStaffPermissions | None # pyright: ignore[reportUninitializedInstanceVariable]
perms: ProfileStaffPermissions # pyright: ignore[reportUninitializedInstanceVariable]
allowed = False

def get_context_data(self, page: str, subpage: str | None = None) -> dict[str, Any]: # pyrefly: ignore[explicit-any]
context: dict[str, Any] = {} # pyrefly: ignore[explicit-any]
context["page"] = page
context["subpage"] = subpage
context["sidebar_links"] = [
sidebar_links = [
SidebarLink(
name="dashboard",
icon="analytics",
text="Analytics",
href=resolve_url("admin.dash"),
),
SidebarLink(
name="users",
icon="profile",
text="Users",
href=resolve_url("admin.users"),
),
SidebarLink(
name="pathways",
icon="controls",
text="Pathways",
href=resolve_url("admin.pathways"),
),
SidebarLink(
name="fulfillment",
icon="list",
text="Fulfillment",
href=resolve_url("admin.fulfillment"),
),
SidebarLink(name="shop", icon="bag-add", text="Shop", href=resolve_url("admin.shop")),
SidebarLink(
name="review",
icon="message-new",
text="Review",
href=resolve_url("admin.review"),
),
SidebarLink(
name="announcements",
icon="important",
text="Announcements",
href=resolve_url("admin.announcements"),
),
SidebarLink(
name="logs",
icon="view",
text="Audit Logs",
href=resolve_url("admin.logs") + "?page=1",
),
]
context["profile"] = self.request.user.profile # ty:ignore[unresolved-attribute] # pyright: ignore[reportAttributeAccessIssue] # pyrefly: ignore[missing-attribute]
if self.perms.view_users:
sidebar_links.append(
SidebarLink(
name="users",
icon="profile",
text="Users",
href=resolve_url("admin.users"),
),
)
if self.perms.view_pathways:
sidebar_links.append(
SidebarLink(
name="pathways",
icon="controls",
text="Pathways",
href=resolve_url("admin.pathways"),
),
)
if self.perms.manage_fulfillments:
sidebar_links.append(
SidebarLink(
name="fulfillment",
icon="list",
text="Fulfillment",
href=resolve_url("admin.fulfillment"),
),
)
if self.perms.manage_shop:
sidebar_links.append(
SidebarLink(
name="shop", icon="bag-add", text="Shop", href=resolve_url("admin.shop"),
),
)
if self.perms.view_review:
sidebar_links.append(
SidebarLink(
name="review",
icon="message-new",
text="Review",
href=resolve_url("admin.review"),
),
)
if self.perms.manage_announcements:
sidebar_links.append(
SidebarLink(
name="announcements",
icon="important",
text="Announcements",
href=resolve_url("admin.announcements"),
),
)
if self.perms.view_auditlogs:
sidebar_links.append(
SidebarLink(
name="logs",
icon="view",
text="Audit Logs",
href=resolve_url("admin.logs") + "?page=1",
),
)

context["sidebar_links"] = sidebar_links
context["profile"] = self.request.user.profile # pyright: ignore[reportAttributeAccessIssue] # pyrefly: ignore[missing-attribute]
return context

@override
Expand All @@ -86,13 +113,18 @@ def dispatch(self, request: HttpRequest, *args: object, **kwargs: object) -> Htt
additional_context={},
)

perms = self.request.user.profile.staff_permissions # ty:ignore[unresolved-attribute] # pyright: ignore[reportAttributeAccessIssue] # pyrefly: ignore[missing-attribute]
perms = self.request.user.profile.staff_permissions # pyright: ignore[reportAttributeAccessIssue] # pyrefly: ignore[missing-attribute]
if perms is None:
profile = self.request.user.profile # ty:ignore[unresolved-attribute] # pyright: ignore[reportAttributeAccessIssue] # pyrefly: ignore[missing-attribute]
profile = self.request.user.profile # pyright: ignore[reportAttributeAccessIssue] # pyrefly: ignore[missing-attribute]
profile.staff_permissions = ProfileStaffPermissions.objects.create()
profile.save()

self.perms = perms
response = super().dispatch(request, *args, **kwargs)

if not self.allowed:
messages.error(request, "You arent allowed to visit this page!")
return redirect("admin.dash")

self.audit_log.save()
return response
6 changes: 6 additions & 0 deletions twisted/twisted_site/views/admin/announcements.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@
# Create your views here.
class AnnouncementsView(AdminView):
def get(self, request: HttpRequest) -> HttpResponse:
if self.perms.manage_announcements:
self.allowed = True
else:
return HttpResponse("err")

context = self.get_context_data(page="announcements")

if self.request.user.is_anonymous:
return redirect("homepage")
return TemplateResponse(request, "admin/announcements.html", context=context)
1 change: 1 addition & 0 deletions twisted/twisted_site/views/admin/dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

# Create your views here.
class DashboardView(AdminView):
allowed = True
def get(self, request: HttpRequest) -> HttpResponse:
context = self.get_context_data(page="dashboard")
if self.request.user.is_anonymous:
Expand Down
5 changes: 5 additions & 0 deletions twisted/twisted_site/views/admin/fulfillment.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,10 @@
# Create your views here.
class FulfillmentView(AdminView):
def get(self, request: HttpRequest) -> HttpResponse:
if self.perms.manage_fulfillments:
self.allowed = True
else:
return HttpResponse("err")

context = self.get_context_data(page="fulfillment")
return render(request, "admin/fulfillment.html", context=context)
21 changes: 21 additions & 0 deletions twisted/twisted_site/views/admin/pathways.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
# Create your views here.
class PathwayListView(AdminView):
def get(self, request: HttpRequest) -> HttpResponse:
if self.perms.view_pathways:
self.allowed = True
else:
return HttpResponse("err")


context = self.get_context_data(page="pathways")
context["pathways"] = Pathway.objects.all().order_by("start")

Expand Down Expand Up @@ -46,6 +52,11 @@ def get(
error: str | None = None,
extracontext: dict[str, Any] | None = None, # pyrefly: ignore[explicit-any]
) -> HttpResponse:
if self.perms.manage_pathways:
self.allowed = True
else:
return HttpResponse("err")

if extracontext is None:
extracontext = {}

Expand All @@ -58,6 +69,11 @@ def get(
return render(request, "admin/pathways/create.html", context=context)

def post(self, request: HttpRequest) -> HttpResponse:
if self.perms.manage_pathways:
self.allowed = True
else:
return HttpResponse("err")

pathway_name: str | None = request.POST.get("name")

start_date: str | None = request.POST.get("startDate")
Expand Down Expand Up @@ -113,6 +129,11 @@ def post(self, request: HttpRequest) -> HttpResponse:

class PathwayDetailView(AdminView):
def get(self, request: HttpRequest, pathway_id: int) -> HttpResponse:
if self.perms.view_pathways:
self.allowed = True
else:
return HttpResponse("err")

context = self.get_context_data(page="pathways", subpage="detail")
pathway = get_object_or_404(Pathway, id=pathway_id)
context["pathway"] = pathway
Expand Down
10 changes: 10 additions & 0 deletions twisted/twisted_site/views/admin/review.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,23 @@
# Create your views here.
class ReviewView(AdminView):
def get(self, request: HttpRequest) -> HttpResponse:
if self.perms.view_review:
self.allowed = True
else:
return HttpResponse("err")

if settings.DEBUG_REVIEW:
return self.debug_get(request)

context = self.get_context_data(page="review")
return render(request, "admin/review.html", context=context)

def post(self, request: HttpRequest) -> HttpResponse:
if self.perms.manage_review:
self.allowed = True
else:
return HttpResponse("err")

if settings.DEBUG_REVIEW:
return self.debug_post(request)

Expand Down
5 changes: 5 additions & 0 deletions twisted/twisted_site/views/admin/shop.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,10 @@
# Create your views here.
class ShopView(AdminView):
def get(self, request: HttpRequest) -> HttpResponse:
if self.perms.manage_shop:
self.allowed = True
else:
return HttpResponse("err")

context = self.get_context_data(page="shop")
return render(request, "admin/shop.html", context=context)
Loading
Loading