diff --git a/.gitignore b/.gitignore index 8267ccc..fa3cab2 100644 --- a/.gitignore +++ b/.gitignore @@ -102,6 +102,7 @@ venv.bak/ # Static CACHE /static/CACHE/ +/staticfiles/CACHE/ # sftp configuration file sftp-config.json diff --git a/Pipfile b/Pipfile index 94af41c..58220d7 100644 --- a/Pipfile +++ b/Pipfile @@ -10,6 +10,7 @@ psycopg2-binary = "*" django-anymail = {extras = ["mailgun"], version = "*"} celery = {extras = ["redis"], version = "*"} django-celery-beat = "*" +django-compressor = "*" [dev-packages] diff --git a/Pipfile.lock b/Pipfile.lock index 40545b0..ffe7cea 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "ffe96f34ca738a35a283e9ac98b5f1a74609c67619f18e8eb7ba62967247766f" + "sha256": "f5ee1dc635277e81758920aeb79d3788dd518bde2e01af520b76659587f7c96c" }, "pipfile-spec": 6, "requires": { @@ -59,11 +59,11 @@ }, "charset-normalizer": { "hashes": [ - "sha256:88fce3fa5b1a84fdcb3f603d889f723d1dd89b26059d0123ca435570e848d5e1", - "sha256:c46c3ace2d744cfbdebceaa3c19ae691f53ae621b39fd7570f59d14fb7f2fd12" + "sha256:0c8911edd15d19223366a194a513099a302055a962bca2cec0f54b8b63175d8b", + "sha256:f23667ebe1084be45f6ae0538e4a5a865206544097e4e8bbcacf42cd02a348f3" ], "markers": "python_version >= '3'", - "version": "==2.0.3" + "version": "==2.0.4" }, "click": { "hashes": [ @@ -95,11 +95,11 @@ }, "django": { "hashes": [ - "sha256:3da05fea54fdec2315b54a563d5b59f3b4e2b1e69c3a5841dda35019c01855cd", - "sha256:c58b5f19c5ae0afe6d75cbdd7df561e6eb929339985dbbda2565e1cabb19a62e" + "sha256:7f92413529aa0e291f3be78ab19be31aefb1e1c9a52cd59e130f505f27a51f13", + "sha256:f27f8544c9d4c383bbe007c57e3235918e258364577373d4920e9162837be022" ], "index": "pypi", - "version": "==3.2.5" + "version": "==3.2.6" }, "django-anymail": { "extras": [ @@ -112,6 +112,13 @@ "index": "pypi", "version": "==8.4" }, + "django-appconf": { + "hashes": [ + "sha256:1b1d0e1069c843ebe8ae5aa48ec52403b1440402b320c3e3a206a0907e97bb06", + "sha256:be58deb54a43d77d2e1621fe59f787681376d3cd0b8bd8e4758ef6c3a6453380" + ], + "version": "==1.0.4" + }, "django-celery-beat": { "hashes": [ "sha256:97ae5eb309541551bdb07bf60cc57cadacf42a74287560ced2d2c06298620234", @@ -120,6 +127,14 @@ "index": "pypi", "version": "==2.2.1" }, + "django-compressor": { + "hashes": [ + "sha256:3358077605c146fdcca5f9eaffb50aa5dbe15f238f8854679115ebf31c0415e0", + "sha256:f8313f59d5e65712fc28787d084fe834997c9dfa92d064a1a3ec3d3366594d04" + ], + "index": "pypi", + "version": "==2.4.1" + }, "django-timezone-field": { "hashes": [ "sha256:6dc782e31036a58da35b553bd00c70f112d794700025270d8a6a4c1d2e5b26c6", @@ -128,6 +143,14 @@ "markers": "python_version >= '3.5'", "version": "==4.2.1" }, + "gunicorn": { + "hashes": [ + "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e", + "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8" + ], + "index": "pypi", + "version": "==20.1.0" + }, "idna": { "hashes": [ "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a", @@ -146,11 +169,46 @@ }, "prompt-toolkit": { "hashes": [ - "sha256:08360ee3a3148bdb5163621709ee322ec34fc4375099afa4bbf751e9b7b7fa4f", - "sha256:7089d8d2938043508aa9420ec18ce0922885304cddae87fb96eebca942299f88" + "sha256:6076e46efae19b1e0ca1ec003ed37a933dc94b4d20f486235d436e64771dcd5c", + "sha256:eb71d5a6b72ce6db177af4a7d4d7085b99756bf656d98ffcc4fecd36850eea6c" ], - "markers": "python_full_version >= '3.6.1'", - "version": "==3.0.19" + "markers": "python_full_version >= '3.6.2'", + "version": "==3.0.20" + }, + "psycopg2-binary": { + "hashes": [ + "sha256:0b7dae87f0b729922e06f85f667de7bf16455d411971b2043bbd9577af9d1975", + "sha256:0f2e04bd2a2ab54fa44ee67fe2d002bb90cee1c0f1cc0ebc3148af7b02034cbd", + "sha256:123c3fb684e9abfc47218d3784c7b4c47c8587951ea4dd5bc38b6636ac57f616", + "sha256:1473c0215b0613dd938db54a653f68251a45a78b05f6fc21af4326f40e8360a2", + "sha256:14db1752acdd2187d99cb2ca0a1a6dfe57fc65c3281e0f20e597aac8d2a5bd90", + "sha256:1e3a362790edc0a365385b1ac4cc0acc429a0c0d662d829a50b6ce743ae61b5a", + "sha256:1e85b74cbbb3056e3656f1cc4781294df03383127a8114cbc6531e8b8367bf1e", + "sha256:20f1ab44d8c352074e2d7ca67dc00843067788791be373e67a0911998787ce7d", + "sha256:2f62c207d1740b0bde5c4e949f857b044818f734a3d57f1d0d0edc65050532ed", + "sha256:3242b9619de955ab44581a03a64bdd7d5e470cc4183e8fcadd85ab9d3756ce7a", + "sha256:35c4310f8febe41f442d3c65066ca93cccefd75013df3d8c736c5b93ec288140", + "sha256:4235f9d5ddcab0b8dbd723dca56ea2922b485ea00e1dafacf33b0c7e840b3d32", + "sha256:5ced67f1e34e1a450cdb48eb53ca73b60aa0af21c46b9b35ac3e581cf9f00e31", + "sha256:7360647ea04db2e7dff1648d1da825c8cf68dc5fbd80b8fb5b3ee9f068dcd21a", + "sha256:8c13d72ed6af7fd2c8acbd95661cf9477f94e381fce0792c04981a8283b52917", + "sha256:988b47ac70d204aed01589ed342303da7c4d84b56c2f4c4b8b00deda123372bf", + "sha256:995fc41ebda5a7a663a254a1dcac52638c3e847f48307b5416ee373da15075d7", + "sha256:a36c7eb6152ba5467fb264d73844877be8b0847874d4822b7cf2d3c0cb8cdcb0", + "sha256:aed4a9a7e3221b3e252c39d0bf794c438dc5453bc2963e8befe9d4cd324dff72", + "sha256:aef9aee84ec78af51107181d02fe8773b100b01c5dfde351184ad9223eab3698", + "sha256:b0221ca5a9837e040ebf61f48899926b5783668b7807419e4adae8175a31f773", + "sha256:b4d7679a08fea64573c969f6994a2631908bb2c0e69a7235648642f3d2e39a68", + "sha256:c250a7ec489b652c892e4f0a5d122cc14c3780f9f643e1a326754aedf82d9a76", + "sha256:ca86db5b561b894f9e5f115d6a159fff2a2570a652e07889d8a383b5fae66eb4", + "sha256:cfc523edecddaef56f6740d7de1ce24a2fdf94fd5e704091856a201872e37f9f", + "sha256:da113b70f6ec40e7d81b43d1b139b9db6a05727ab8be1ee559f3a69854a69d34", + "sha256:f6fac64a38f6768e7bc7b035b9e10d8a538a9fadce06b983fb3e6fa55ac5f5ce", + "sha256:f8559617b1fcf59a9aedba2c9838b5b6aa211ffedecabca412b92a1ff75aac1a", + "sha256:fbb42a541b1093385a2d8c7eec94d26d30437d0e77c1d25dae1dcc46741a385e" + ], + "index": "pypi", + "version": "==2.9.1" }, "python-crontab": { "hashes": [ @@ -173,6 +231,12 @@ ], "version": "==2021.1" }, + "rcssmin": { + "hashes": [ + "sha256:ca87b695d3d7864157773a61263e5abb96006e9ff0e021eff90cbe0e1ba18270" + ], + "version": "==1.0.6" + }, "redis": { "hashes": [ "sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2", @@ -188,6 +252,24 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", "version": "==2.26.0" }, + "rjsmin": { + "hashes": [ + "sha256:0ab825839125eaca57cc59581d72e596e58a7a56fbc0839996b7528f0343a0a8", + "sha256:211c2fe8298951663bbc02acdffbf714f6793df54bfc50e1c6c9e71b3f2559a3", + "sha256:466fe70cc5647c7c51b3260c7e2e323a98b2b173564247f9c89e977720a0645f", + "sha256:585e75a84d9199b68056fd4a083d9a61e2a92dfd10ff6d4ce5bdb04bc3bdbfaf", + "sha256:6044ca86e917cd5bb2f95e6679a4192cef812122f28ee08c677513de019629b3", + "sha256:714329db774a90947e0e2086cdddb80d5e8c4ac1c70c9f92436378dedb8ae345", + "sha256:799890bd07a048892d8d3deb9042dbc20b7f5d0eb7da91e9483c561033b23ce2", + "sha256:975b69754d6a76be47c0bead12367a1ca9220d08e5393f80bab0230d4625d1f4", + "sha256:b15dc75c71f65d9493a8c7fa233fdcec823e3f1b88ad84a843ffef49b338ac32", + "sha256:dd0f4819df4243ffe4c964995794c79ca43943b5b756de84be92b445a652fb86", + "sha256:e3908b21ebb584ce74a6ac233bdb5f29485752c9d3be5e50c5484ed74169232c", + "sha256:e487a7783ac4339e79ec610b98228eb9ac72178973e3dee16eba0e3feef25924", + "sha256:ecd29f1b3e66a4c0753105baec262b331bcbceefc22fbe6f7e8bcd2067bcb4d7" + ], + "version": "==1.1.0" + }, "six": { "hashes": [ "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", diff --git a/accounts/templates/accounts/profile.html b/accounts/templates/accounts/profile.html index 800a087..9259e0b 100644 --- a/accounts/templates/accounts/profile.html +++ b/accounts/templates/accounts/profile.html @@ -52,8 +52,8 @@

To-do's

+ {% if overdue_todos %}
-

Overdue

{% regroup overdue_todos by employee as overdue_list %} @@ -110,63 +110,64 @@ {% endfor %}
+ {% endif %}

Due today

{% regroup todos by employee as todo_list %} {% for employee in todo_list %}

{{employee.grouper}}

{% for todo in employee.list %} -
  • -
    - - {{todo.description}} - {% if todo.due_date %}{{todo.due_date}}{% endif %} -
    - {% if not todo.employee.archived %} - - - {% endif %} -
    +
  • +
    + + {{todo.description}} + {% if todo.due_date %}{{todo.due_date}}{% endif %} +
    + {% if not todo.employee.archived %} + + + {% endif %}
    -
    + + {% csrf_token %} + - {% csrf_token %} - - - -
    - - cancel -
    -
    -
  • - {% endfor %} + data-todo-target="checkbox" + class="todo__checkbox_input" + name="completed" + type="checkbox" + {% if todo.completed %}checked{% endif %} + {% if todo.employee.archived %}disabled{% endif %} + > + + +
    + + cancel +
    + + + {% endfor %} + {% empty %} +

    No to-do's for today today.

    {% endfor %}
    -
    - -
    +

    Recent Activity

    {% regroup latest_activity by created_at.date as latest_activity_re %} diff --git a/board/templates/board/employee_detail.html b/board/templates/board/employee_detail.html index c1e3972..ec1d22e 100644 --- a/board/templates/board/employee_detail.html +++ b/board/templates/board/employee_detail.html @@ -1,5 +1,6 @@ {% extends "base.html" %} {% load static %} +{% load compress %} {% block head %} @@ -41,6 +42,8 @@ {{event.time|time:"TIME_FORMAT"}} Edit + {% empty %} +

    No events for employee.

    {% endfor %}
    diff --git a/onboard/settings.py b/onboard/settings.py index 1fc67d7..f798c34 100644 --- a/onboard/settings.py +++ b/onboard/settings.py @@ -41,6 +41,7 @@ INSTALLED_APPS = [ 'django.contrib.staticfiles', 'django.contrib.sites', 'django.contrib.flatpages', + 'compressor', 'accounts.apps.AccountsConfig', 'board.apps.BoardConfig', ] @@ -132,6 +133,13 @@ STATICFILES_DIRS = [ ] STATIC_URL = '/static/' STATIC_ROOT = '/var/www/onboard.windmillapps.org/static/' +COMPRESS_ENABLED = True + +STATICFILES_FINDERS = ( + 'django.contrib.staticfiles.finders.FileSystemFinder', + 'django.contrib.staticfiles.finders.AppDirectoriesFinder', + 'compressor.finders.CompressorFinder', +) # Default primary key field type diff --git a/onboard/settings_dev.py b/onboard/settings_dev.py index 1f84911..56bc8d4 100644 --- a/onboard/settings_dev.py +++ b/onboard/settings_dev.py @@ -39,6 +39,7 @@ INSTALLED_APPS = [ 'django.contrib.staticfiles', 'django.contrib.sites', 'django.contrib.flatpages', + 'compressor', 'django_celery_beat', 'accounts.apps.AccountsConfig', 'board.apps.BoardConfig', @@ -124,8 +125,15 @@ USE_TZ = True # https://docs.djangoproject.com/en/3.2/howto/static-files/ STATIC_URL = '/static/' +STATIC_ROOT = BASE_DIR / 'staticfiles' STATICFILES_DIRS = [BASE_DIR / 'static'] +COMPRESS_ENABLED = True +STATICFILES_FINDERS = ( + 'django.contrib.staticfiles.finders.FileSystemFinder', + 'django.contrib.staticfiles.finders.AppDirectoriesFinder', + 'compressor.finders.CompressorFinder', +) # Default primary key field type # https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field diff --git a/static/scripts/form.js b/static/scripts/form.js deleted file mode 100644 index f8aa2e4..0000000 --- a/static/scripts/form.js +++ /dev/null @@ -1,155 +0,0 @@ -import getCookie from "./get_cookie.js"; - -export default class Form { - constructor(form, isNew=false) { - if (typeof form === "string") { - this.form = document.querySelector(`#${form}`) - } else { - this.form = form; - } - this.url = this.form.attributes.action.value; - this.destroyButton = null - if (isNew) { - this.load() - } else { - if (this.form.querySelector("[name=destroy]")) { - this.destroyButton = this.form.querySelector("[name=destroy]") - this.destroyButton.addEventListener("click", this.destroy.bind(this)) - } - if (this.form.querySelector("[name=edit]")) { - this.editButton = this.form.querySelector("[name=edit]") - this.editButton.addEventListener("click", this.edit.bind(this)) - } - } - this.form.addEventListener("submit", this.post.bind(this)) - this.form.addEventListener("change", this.change.bind(this)) - } - - load() { - fetch(`${this.url}`) - .then((response) => response.text()) - .then((html) => { - this.form.innerHTML = html; - if (this.form.querySelector("[name=destroy]")) { - this.destroyButton = this.form.querySelector("[name=destroy]") - this.destroyButton.addEventListener("click", this.destroy.bind(this)) - } - if (this.form.querySelector("[name=edit]")) { - this.editButton = this.form.querySelector("[name=edit]") - this.editButton.addEventListener("click", this.edit.bind(this)) - } - }) - } - - validate() { - const inputs = new Set() - this.form.querySelectorAll("input").forEach(input => { - inputs.add(input.checkValidity()) - }) - - let valid = (inputs.has(false)) ? false : true - - return valid - } - - change(event) { - if (event.target.type === "checkbox") { - this.post(event) - } - } - - edit(event) { - if (event) { - event.preventDefault() - } - this.form.querySelector("[name=description]").type = 'text' - let display = this.form.querySelector(".todo__description_display") - display.classList.add('--hidden') - } - - - post(event) { - if (event) { - event.preventDefault() - } - - // construct a new FormData object from the html form - const formData = new FormData(this.form) - - // get the csrftoken - const csrftoken = getCookie("csrftoken") - - const options = { - method: "POST", - body: new URLSearchParams(formData), - mode: "same-origin", - }; - - // construct a new Request passing in the csrftoken - const request = new Request(`${this.url}`, { - headers: { "X-CSRFToken": csrftoken }, - }) - - // finally make the post request and wait for the server to respond - fetch(request, options) - .then((response) => response.text()) - .then((html) => { - var doc = new DOMParser().parseFromString(html, "text/html") - this.url = doc.forms[0].attributes.action.value; - this.form.innerHTML = html; - - if (this.form.querySelector("[name=destroy]")) { - this.destroyButton = this.form.querySelector("[name=destroy]") - this.destroyButton.addEventListener( - "click", - this.destroy.bind(this) - ) - } - if (this.form.querySelector("[name=edit]")) { - this.editButton = this.form.querySelector("[name=edit]") - this.editButton.addEventListener("click", this.edit.bind(this)) - } - }) - .catch((error) => { - return error; - }) - } - - destroy(event) { - if (event) { - event.preventDefault() - } - - const confirmation = confirm( - "Are you sure you would like to delete this entry?" - ) - - if (confirmation) { - // get the csrftoken - const csrftoken = getCookie("csrftoken") - - const options = { - method: "POST", - mode: "same-origin", - }; - - // construct a new Request passing in the csrftoken - const request = new Request(`${this.destroyButton.dataset.url}`, { - headers: { "X-CSRFToken": csrftoken }, - }) - - // finally make the post request and wait for the server to respond - fetch(request, options) - .then((response) => response.text()) - .then((html) => { - this.form.innerHTML = html; - setTimeout(() => { - this.form.remove() - }, 3000) - }) - .catch((error) => { - return error; - }) - } - } -} diff --git a/static/scripts/index.js b/static/scripts/index.js index 8ffd283..50a23a1 100644 --- a/static/scripts/index.js +++ b/static/scripts/index.js @@ -1,20 +1,4 @@ -// import Form from "./form.js"; -// import View from "./view.js"; - -// import TodoListController from "./controllers/todo_list_controller.js" import TodoController from "./controllers/todo_controller.js" - -// constructor(element, forms, templateName, addButton, destination) - -// const todoListView = new View( -// document.querySelector("#todos"), -// ".todo_form", -// "#todo__list" -// ) - - - const application = Stimulus.Application.start() -// application.register("todo-list", TodoListController) application.register("todo", TodoController) diff --git a/static/scripts/view.js b/static/scripts/view.js deleted file mode 100644 index b12270f..0000000 --- a/static/scripts/view.js +++ /dev/null @@ -1,52 +0,0 @@ -import Form from "./form.js"; - -export default class View { - constructor(element, forms, list) { - this.element = element - this.forms = this.element.querySelectorAll(forms) - this.list = this.element.querySelector(list) - this.observer = null - - this.connect() - - for (const form of this.forms) { - new Form(form); - } - - } - - connect() { - this.observe(this.list) - } - - observe(list, config = { attributes: true, childList: true, subtree: true }) { - const callback = function (mutationList, observer) { - mutationList.forEach(mutation => { - switch(mutation.type) { - case 'childList': - if (mutation.target === list) { - mutation.addedNodes.forEach(node => { - if (node.children) { - let potentialForm = node.children[0].children[0] - if (potentialForm.nodeName === "FORM") { - new Form(potentialForm, true); - } - } - }) - } - break; - case 'attributes': - /* An attribute value changed on the element in - mutation.target. - The attribute name is in mutation.attributeName, and - its previous value is in mutation.oldValue. */ - break; - } - }); - }; - - this.observer = new MutationObserver(callback); - this.observer.observe(this.list, config); - } - -} diff --git a/static/styles/main.css b/static/styles/main.css index 0dfadf6..a190558 100644 --- a/static/styles/main.css +++ b/static/styles/main.css @@ -1,7 +1,7 @@ :root { --white: #f8f8fb; --black: #393d3f; - --grey: #a7b4bb; + --gray: #a7b4bb; --blue: #10638c; --light-blue: #74c0e6; --red: #8c1016; @@ -74,7 +74,7 @@ article { max-width: 64rem; margin: 0 auto 2rem; padding: 1rem; - border: 0.2rem solid var(--grey); + border: 0.2rem solid var(--gray); } @media all and (max-width: 64rem) { @@ -170,6 +170,9 @@ article { align-items: center; align-content: flex-start; } +.todo__item:hover { + background-color: #e3e3e3; +} .todo__item:hover .hidden_action { visibility: visible; } @@ -204,6 +207,9 @@ article { .todos__overdue h4 { margin-top: 0; } +h2 + h4 { + margin-top: 0; +} .todo__checkbox_button { display: inline-block; width: 1.4em; @@ -284,7 +290,7 @@ input[type=time], input[type=password], input[type=search], textarea, select { - border: 0.2rem solid var(--grey); + border: 0.2rem solid var(--gray); padding: 0.3rem; font-family: inherit; outline: none; @@ -408,7 +414,7 @@ hgroup { column-gap: 1rem; margin-bottom: 2rem; padding-bottom: 0.5rem; - border-bottom: 0.046rem solid var(--grey); + border-bottom: 0.046rem solid var(--gray); } diff --git a/templates/base.html b/templates/base.html index 8447248..22af296 100644 --- a/templates/base.html +++ b/templates/base.html @@ -1,4 +1,5 @@ {% load static %} +{% load compress %} @@ -12,8 +13,10 @@ + {% compress css %} + {% endcompress %} {% block head %} {% endblock %}