diff --git a/budget/templates/budget/index.html b/budget/templates/budget/index.html index 4bcd3916a6f27ec3f9f1c4a7fe5004348642400b..079818016b8729aa463be58e4e2f8504d9ab9599 100644 --- a/budget/templates/budget/index.html +++ b/budget/templates/budget/index.html @@ -1,20 +1,232 @@ -{% load static %} - - - - - {{ json_ctx|json_script:"ctx-data" }} - - - + + + + + -
+
\ No newline at end of file diff --git a/budget/views.py b/budget/views.py index 567b95c0743eefde7782bb8dded67443a38bf530..5315950bfc6d0f7e93c476940891e0cd3ba881ed 100644 --- a/budget/views.py +++ b/budget/views.py @@ -1,7 +1,5 @@ -from django.conf import settings from django.shortcuts import render from django.contrib.auth.mixins import LoginRequiredMixin -from django.urls import reverse from django.views import View from django.http import HttpRequest, JsonResponse, HttpResponse from datetime import date, datetime, timedelta @@ -20,13 +18,7 @@ class IndexView(LoginRequiredMixin, View): def get(self, request: HttpRequest) -> HttpResponse: - return render(request, "budget/index.html", { - "json_ctx": { - "debug": settings.DEBUG, - "adminIndex": reverse("admin:index"), - "showAdmin": request.user.is_staff, - } - }) + return render(request, "budget/index.html") class EventsView(LoginRequiredMixin, View): diff --git a/mint/settings.py b/mint/settings.py index 69fdc4ef6cb498d9d5cced5a9a333b5d95419e8f..ae48cdb188e0ab93d2ccf62afec7302f6d082af5 100644 --- a/mint/settings.py +++ b/mint/settings.py @@ -11,7 +11,7 @@ SECRET_KEY = env.str("SECRET_KEY") DEBUG = env.bool("DEBUG", False) -ALLOWED_HOSTS = ["localhost", "dev.jolheiser.com"] +ALLOWED_HOSTS = [] INSTALLED_APPS = [ "django.contrib.admin", @@ -73,9 +73,6 @@ USE_TZ = True STATIC_URL = "static/" -STATICFILES_DIRS = [ - BASE_DIR / "static", -] DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" diff --git a/mint/urls.py b/mint/urls.py index 09744c736b23f5c2b659a663c485341520ca8d2b..e11f05a4192066bb9c2d5df3d92fe1ad140c8498 100644 --- a/mint/urls.py +++ b/mint/urls.py @@ -1,6 +1,5 @@ from django.conf import settings -from django.conf.urls.static import static from django.contrib import admin from django.urls import path, include from budget.views import IndexView, EventsView, TransactionView, CopyView @@ -13,7 +12,7 @@ path("copy/", CopyView.as_view(), name="copy"), path("admin/", admin.site.urls), path("oidc/", include("mozilla_django_oidc.urls")), -] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) +] if settings.DEBUG: try: diff --git a/static/css/mint.css b/static/css/mint.css deleted file mode 100644 index 48a17b5bf3b3d5a98846c48b0983145f72a76175..0000000000000000000000000000000000000000 --- a/static/css/mint.css +++ /dev/null @@ -1,10 +0,0 @@ -@media screen { - - .fc-view-harness { - height: 90vh !important; - } - - .fc-scrollgrid-sync-table { - height: 84vh !important; - } -} \ No newline at end of file diff --git a/static/js/mint.js b/static/js/mint.js deleted file mode 100644 index 66f8b5434011a2785f328be054488898270a5d40..0000000000000000000000000000000000000000 --- a/static/js/mint.js +++ /dev/null @@ -1,237 +0,0 @@ -let calendar; -const csrftoken = getCookie('csrftoken'); - -document.addEventListener('DOMContentLoaded', function () { - const calendarEl = document.getElementById('calendar'); - calendar = new FullCalendar.Calendar(calendarEl, { - initialView: 'dayGridMonth', - dayMaxEventRows: true, - selectable: true, - selectLongPressDelay: 500, - eventStartEditable: true, - navLinks: true, - events: "/events/", - customButtons: { - admin: { - text: "admin", - hint: "Django Admin", - click: () => location.href = ctxData.adminIndex, - }, - copy: { - text: "copy", - hint: "Copy budget month-to-month", - click: () => copy(), - } - }, - headerToolbar: { - left: `prev,next today copy${ctxData.showAdmin ? " admin" : ""}`, - center: 'title', - right: 'dayGridWeek,dayGridMonth,dayGridYear' - }, - navLinkDayClick: (date) => input(date), - select: (info) => select(info), - eventDrop: (info) => eventDrop(info), - eventClick: (info) => eventClick(info), - }); - calendar.render(); -}); - -function copy() { - Swal.fire({ - title: "Are you sure?", - html: "This will copy all budget items from last month.", - icon: "warning", - allowOutsideClick: false, - showDenyButton: true, - showCloseButton: true, - confirmButtonText: "Yes", - reverseButtons: true, - showLoaderOnConfirm: true, - preConfirm: () => { - fetch("/copy/", { - method: "POST", - headers: {"X-CSRFToken": csrftoken}, - body: JSON.stringify({"month": calendar.getDate().getMonth() + 1}), - }).then(() => calendar.refetchEvents()); - } - }); -} - -function select(info) { - if (ctxData.debug) console.log(info); - const events = calendar.getEvents().filter((event) => { - const start = event.start; - const end = event.end || event.start; - return start < info.end && end >= info.start - }); - if (ctxData.debug) console.log(events); - const sum = events.filter((event) => !event.extendedProps.income).reduce((acc, event) => acc + event.extendedProps.amount, 0); - toast(`\$${sum / 100}`, 'info') -} - -function eventDrop(info) { - const event = info.event; - const props = event.extendedProps; - fetch("/transaction/", { - method: "PATCH", - headers: {"X-CSRFToken": csrftoken}, - body: JSON.stringify({ - id: event.id, - start: event.start, - extendedProps: { - title: props.title, - amount: props.amount, - recurrence: props.recurrence, - week: props.week, - income: props.income - } - }) - }).then(() => calendar.refetchEvents()); -} - -function eventClick(info) { - const event = info.event; - const props = event.extendedProps; - input(event.start, event.id, props.title, props.amount / 100, props.recurrence, props.week, props.income); -} - -function input(time, id = '', title = 'New Transaction', amount = 0, recurrence = "M", week = 1, income = false) { - const weekDay = time.getDay(); - const monthDay = time.getDate(); - title = title.replace(/"/g, """) - Swal.fire({ - title: ``, - html: ` -
- - - - -
- -
- -
- -
- `, - allowOutsideClick: false, - showCloseButton: true, - reverseButtons: true, - confirmButtonText: 'Save', - showDenyButton: id !== '', - denyButtonText: 'Delete', - customClass: { - confirmButton: 'ml-[15rem]!' - }, - didRender: () => { - const $recurring = document.querySelector("[name='recurring']"); - const $opts = document.getElementById("recurringOptions"); - $recurring.addEventListener('change', () => { - $recurring.checked ? $opts.classList.remove('hidden') : $opts.classList.add('hidden'); - }); - }, - preDeny: () => { - const $popup = Swal.getPopup(); - return { - id: $popup.querySelector("[name='id']").value - }; - }, - preConfirm: () => { - const $popup = Swal.getPopup(); - return { - id: $popup.querySelector("[name='id']").value, - date: new Date($popup.querySelector("[name='date']").value), - title: $popup.querySelector("[name='title']").value, - amount: parseFloat($popup.querySelector("[name='amount']").value), - recurrence: $popup.querySelector("[name='recurring']").checked ? $popup.querySelector("[name='recurrence']:checked").value : '', - week: $popup.querySelector("[name='week']").value, - income: $popup.querySelector("[name='income']").checked - }; - }, - }).then((result) => { - const value = result.value; - if (result.isDenied) { - fetch("/transaction/", { - method: "DELETE", - headers: {"X-CSRFToken": csrftoken}, - body: JSON.stringify(value) - }).then(() => calendar.refetchEvents()); - return - } - if (!result.isConfirmed) return; - if (ctxData.debug) console.log(value); - const event = { - id: value.id, - title: `${value.title} (\$${value.amount})`, - start: new Date(value.date), - allDay: true, - extendedProps: { - title: value.title, - amount: value.amount * 100, - recurrence: value.recurrence, - week: value.week, - income: value.income - } - }; - if (event.id !== "") { - fetch("/transaction/", { - method: "PATCH", - headers: {"X-CSRFToken": csrftoken}, - body: JSON.stringify(event) - }).then(() => calendar.refetchEvents()); - if (ctxData.debug) console.log('Event Modified:', event); - return; - } - fetch("/transaction/", { - method: "POST", - headers: {"X-CSRFToken": csrftoken}, - body: JSON.stringify(event) - }).then(() => calendar.refetchEvents()); - if (ctxData.debug) console.log('Event Added:', event); - }); -} - -function toast(message, level = "error", duration = 5) { - const Toast = Swal.mixin({ - position: "top-end", - showConfirmButton: false, - timer: duration * 1000, - timerProgressBar: true, - didOpen: (toast) => { - toast.addEventListener('click', () => Swal.close()); - toast.onmouseenter = Swal.stopTimer; - toast.onmouseleave = Swal.resumeTimer; - } - }); - Toast.fire({ - icon: level, - title: message - }); -} - -function ordinal(i = 0) { - const suffixes = ["th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"]; - i %= 100; - if ([11, 12, 13].includes(i)) return `${i}th`; - return `${i}${suffixes[i % 10]}`; -} - -function dayOfWeek(i = 0) { - return ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"][i]; -} - -function getCookie(name) { - let cookieValue = null; - if (document.cookie && document.cookie !== '') { - const cookies = document.cookie.split(';'); - for (let i = 0; i < cookies.length; i++) { - const cookie = cookies[i].trim(); - if (cookie.substring(0, name.length + 1) === (name + '=')) { - cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); - break; - } - } - } - return cookieValue; -} \ No newline at end of file