diff --git a/.gitignore b/.gitignore index 45899c8b..a8ddc42b 100644 --- a/.gitignore +++ b/.gitignore @@ -113,6 +113,5 @@ screenshots/ nginx/certs/ nginx/certbot/ nginx/*.conf -!nginx/default.dev.conf *.pem *.key diff --git a/Dockerfile b/Dockerfile index 87811728..03d9dabf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,67 +1,101 @@ -# Stage 1: build frontend assets (multi-arch: official node image) +# syntax=docker/dockerfile:1 +# +# Targets: +# prod (default, last stage) gunicorn image with assets and static files baked in +# dev dependencies only; the source is bind-mounted by compose.override.yml +# +# Stages are ordered so that editing application code only invalidates the cheap final layers: +# apt packages, Python dependencies and node modules are all cached separately. + +# ---- Frontend assets (multi-arch: official node image) ---- FROM node:24-slim AS assets WORKDIR /app COPY package.json package-lock.json ./ -RUN npm ci +RUN --mount=type=cache,target=/root/.npm \ + npm ci COPY gulpfile.js ./ COPY pipeline/source_assets ./pipeline/source_assets RUN npm run build -# Stage 2: build the Python environment (multi-arch: official python image) -FROM python:3.14-slim-trixie AS builder + +# ---- Shared Python base: runtime system libraries only ---- +FROM python:3.14-slim-trixie AS python-base + +# Keep downloaded packages between builds (the default Docker config for Debian images deletes them) +RUN rm -f /etc/apt/apt.conf.d/docker-clean \ + && echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,sharing=locked \ + apt-get update \ + && apt-get install -y --no-install-recommends libcairo2 + +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 \ + UV_PROJECT_ENVIRONMENT=/opt/venv \ + UV_LINK_MODE=copy \ + PATH="/opt/venv/bin:$PATH" + + +# ---- Python dependencies (only rebuilt when pyproject.toml / uv.lock change) ---- +FROM python-base AS deps-base COPY --from=ghcr.io/astral-sh/uv:0.12.21 /uv /uvx /bin/ # pycairo (via z3c.rml) has no wheels and is compiled against cairo -RUN apt-get update \ - && apt-get install -y --no-install-recommends build-essential pkg-config libcairo2-dev \ - && rm -rf /var/lib/apt/lists/* +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,sharing=locked \ + apt-get update \ + && apt-get install -y --no-install-recommends build-essential pkg-config libcairo2-dev WORKDIR /app -# Set up py environment -# DEBUG must never be on in a built image; enable it explicitly via the environment if needed -ENV DEBUG=false \ - PYTHONDONTWRITEBYTECODE=1 \ - PYTHONUNBUFFERED=1 \ - UV_COMPILE_BYTECODE=1 \ - UV_LINK_MODE=copy - -# Copy uv project files first (for better caching) -COPY pyproject.toml uv.lock ./ - -# Install the project's dependencies using the lockfile and settings +FROM deps-base AS deps-prod +ENV UV_COMPILE_BYTECODE=1 RUN --mount=type=cache,target=/root/.cache/uv \ --mount=type=bind,source=uv.lock,target=uv.lock \ --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ uv sync --frozen --no-install-project --no-dev -# Then, add the rest of the project source code and install it -# Installing separately from its dependencies allows optimal layer caching +FROM deps-base AS deps-dev +RUN --mount=type=cache,target=/root/.cache/uv \ + --mount=type=bind,source=uv.lock,target=uv.lock \ + --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ + uv sync --frozen --no-install-project + + +# ---- Development: dependencies only, source and static assets are mounted at runtime ---- +FROM python-base AS dev +COPY --from=deps-dev /opt/venv /opt/venv +WORKDIR /app +EXPOSE 8000 +CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"] + + +# ---- Static files: depends on the source, so this is the first stage that reruns on a code change ---- +FROM python-base AS static +COPY --from=deps-prod /opt/venv /opt/venv +WORKDIR /app COPY . /app COPY --from=assets /app/pipeline/built_assets /app/pipeline/built_assets -RUN --mount=type=cache,target=/root/.cache/uv \ - uv sync --frozen --no-dev - # Placeholder values only satisfy settings that are mandatory when DEBUG is off; they are not kept in the image -RUN EMAIL_HOST=build EMAIL_HOST_USER=build EMAIL_HOST_PASSWORD=build EMAIL_FROM=build@example.com \ - uv run python manage.py collectstatic --noinput +RUN DEBUG=false EMAIL_HOST=build EMAIL_HOST_USER=build EMAIL_HOST_PASSWORD=build EMAIL_FROM=build@example.com \ + python manage.py collectstatic --noinput -FROM python:3.14-slim-trixie -RUN apt-get update \ - && apt-get install -y --no-install-recommends libcairo2 \ - && rm -rf /var/lib/apt/lists/* + +# ---- Production image ---- +FROM python-base AS prod RUN addgroup --system app \ - && adduser --system --group --home /home/app app \ - && mkdir -p /home/app \ - && chown app:app /home/app -COPY --from=builder --chown=app:app /app /app + && adduser --system --group --home /home/app app + +COPY --from=deps-prod /opt/venv /opt/venv WORKDIR /app -ENV DEBUG=false \ - PYTHONDONTWRITEBYTECODE=1 \ - PYTHONUNBUFFERED=1 -ENV PATH="/app/.venv/bin:$PATH" +COPY --chown=app:app . /app +COPY --from=static --chown=app:app /app/static /app/static +COPY --from=assets --chown=app:app /app/pipeline/built_assets /app/pipeline/built_assets + +# DEBUG must never be on in a built image; enable it explicitly via the environment if needed +ENV DEBUG=false USER app EXPOSE 8000 diff --git a/PyRIGS/urls.py b/PyRIGS/urls.py index 93b6a513..0577b777 100644 --- a/PyRIGS/urls.py +++ b/PyRIGS/urls.py @@ -27,7 +27,3 @@ urlpatterns = [ if settings.DEBUG: urlpatterns += staticfiles_urlpatterns() - - urlpatterns += [ - path("bootstrap/", TemplateView.as_view(template_name="bootstrap.html")), - ] diff --git a/README.md b/README.md index ca91749d..0f14d458 100644 --- a/README.md +++ b/README.md @@ -16,14 +16,23 @@ For setup information and other such helpful stuff check the [Wiki](https://gith - users: Our custom logic for registration and profiles. Semi-modular. # Running locally -The compose stack runs the app behind a plain-HTTP nginx (`nginx/default.dev.conf`) on port 80. +> [!WARNING] +> `compose.yml` is for **development only** and must not be run in production. It mounts the source into the container, runs Django's development server with `DEBUG` on by default, and uses placeholder captcha keys. Production is deployed from the image built by the `prod` target of the `Dockerfile`. + +The compose stack runs Postgres, the Django development server and a gulp watcher that rebuilds the CSS/JS. Changes to Python code and templates reload the app automatically, and changes to `pipeline/source_assets` are rebuilt into `pipeline/built_assets` (refresh the browser to pick them up). 1. Copy `.env.example` to `.env` and fill it in. For local use set at least: + ``` + DEBUG=true + DJANGO_ALLOWED_HOSTS=localhost + ``` + The sample data commands refuse to run unless `DEBUG` (or `STAGING`) is true. 2. Start the stack (migrations run automatically on start): ``` docker compose up -d --build ``` -3. Open . + The first start installs the node modules, so give the assets a minute to appear. After changing `pyproject.toml`, `uv.lock` or the `Dockerfile`, rebuild with `docker compose up -d --build` (or run `docker compose watch`). +3. Open . ## Creating a user ``` diff --git a/RIGS/forms.py b/RIGS/forms.py index a9780c8c..862e8f77 100644 --- a/RIGS/forms.py +++ b/RIGS/forms.py @@ -201,7 +201,7 @@ class EventRiskAssessmentForm(forms.ModelForm): field.widget = forms.CheckboxInput() elif field.__class__ == forms.BooleanField: field.widget = forms.RadioSelect( - choices=[(True, "Yes"), (False, "No")], attrs={"class": "custom-control-input", "required": "true"} + choices=[(True, "Yes"), (False, "No")], attrs={"class": "form-check-input", "required": "true"} ) def clean(self): diff --git a/RIGS/templates/base_rigs.html b/RIGS/templates/base_rigs.html index d7556a4e..98df83c8 100644 --- a/RIGS/templates/base_rigs.html +++ b/RIGS/templates/base_rigs.html @@ -14,7 +14,7 @@ {% if user.is_authenticated %} @@ -74,11 +74,4 @@ {% block js %} {{ block.super }} - - - {% endblock %} diff --git a/RIGS/templates/calendar.html b/RIGS/templates/calendar.html index e0a8116f..d35a40ac 100644 --- a/RIGS/templates/calendar.html +++ b/RIGS/templates/calendar.html @@ -27,7 +27,7 @@ calendar = new FullCalendar.Calendar(calendarEl, { firstDay: 1, - themeSystem: 'bootstrap', + themeSystem: 'standard', aspectRatio: 1.5, eventTimeFormat: { 'hour': '2-digit', @@ -38,14 +38,13 @@ editable: false, dayMaxEventRows: true, // allow "more" link when too many events events: function(fetchInfo, successCallback, failureCallback) { - $.ajax({ - url: '/api/event', - dataType: 'json', - data: { - start: moment(fetchInfo.startStr).format("YYYY-MM-DD[T]HH:mm:ss"), - end: moment(fetchInfo.endStr).format("YYYY-MM-DD[T]HH:mm:ss") - }, - success: function(doc) { + var params = new URLSearchParams({ + start: moment(fetchInfo.startStr).format("YYYY-MM-DD[T]HH:mm:ss"), + end: moment(fetchInfo.endStr).format("YYYY-MM-DD[T]HH:mm:ss") + }); + fetch('/api/event?' + params, {credentials: 'same-origin'}) + .then(function (response) { return response.json(); }) + .then(function (doc) { var events = []; colours = { 'Provisional': '#FFE89B', @@ -54,8 +53,8 @@ 'Cancelled': 'grey' , 'non-rig': '#25AAE2' }; - $(doc).each(function() { - end = $(this).attr('latest') + doc.forEach(function (item) { + end = item.latest allDay = false if(end.indexOf("T") < 0){ //If latest does not contain a time end = moment(end + " 23:59").format("YYYY-MM-DD[T]HH:mm:ss") @@ -63,84 +62,64 @@ } thisEvent = { - 'start': $(this).attr('earliest'), + 'start': item.earliest, 'end': end, 'className': 'modal-href', - 'title': $(this).attr('title'), - 'url': $(this).attr('url'), + 'title': item.title, + 'url': item.url, 'allDay': allDay } - if($(this).attr('is_rig')===true || $(this).attr('status') === "Cancelled"){ - thisEvent['color'] = colours[$(this).attr('status')]; + if(item.is_rig===true || item.status === "Cancelled"){ + thisEvent['color'] = colours[item.status]; }else{ thisEvent['color'] = colours['non-rig']; } events.push(thisEvent); }); successCallback(events); - } - }); + }) + .catch(failureCallback); }, datesSet: function(info) { var view = info.view; // Set the title of the view - $('#calendar-header').text(view.title); + document.getElementById('calendar-header').textContent = view.title; // Enable/Disable "Today" button as required - let $today = $('#today-button'); - if(moment().isBetween(view.currentStart, view.currentEnd)){ - //Today is within the current view - $today.prop('disabled', true); - }else{ - $today.prop('disabled', false); - } + //Today is within the current view + document.getElementById('today-button').disabled = moment().isBetween(view.currentStart, view.currentEnd); // Set active view select button - let $month = $('#month-button'); - let $week = $('#week-button'); - let $day = $('#day-button'); - switch(view.type){ - case 'dayGridMonth': - $month.addClass('active'); - $week.removeClass('active'); - $day.removeClass('active'); - break; - - case 'timeGridWeek': - $month.removeClass('active'); - $week.addClass('active'); - $day.removeClass('active'); - break; - - case 'timeGridDay': - $month.removeClass('active'); - $week.removeClass('active'); - $day.addClass('active'); - break; - } + var buttons = { + dayGridMonth: document.getElementById('month-button'), + timeGridWeek: document.getElementById('week-button'), + timeGridDay: document.getElementById('day-button') + }; + Object.keys(buttons).forEach(function (type) { + buttons[type].classList.toggle('active', type === view.type); + }); history.replaceState(null,null,"{% url 'web_calendar' %}"+viewToUrl[view.type]+'/'+moment(view.currentStart).format('YYYY-MM-DD')+'/'); } }); calendar.render(); }); - $(document).ready(function() { + ready(function() { // set some button listeners - $('#next-button').click(function(){ calendar.next(); }); - $('#prev-button').click(function(){ calendar.prev(); }); - $('#today-button').click(function(){ calendar.today(); }); - $('#month-button').click(function(){ calendar.changeView('dayGridMonth'); }); - $('#week-button').click(function(){ calendar.changeView('timeGridWeek'); }); - $('#day-button').click(function(){ calendar.changeView('timeGridDay'); }); - $('#go-to-date-input').change(function(){ - if(moment($('#go-to-date-input').val()).isValid()){ - $('#go-to-date-button').prop('disabled', false); - } else{ - $('#go-to-date-button').prop('disabled', true); - } + var click = function (id, fn) { document.getElementById(id).addEventListener('click', fn); }; + click('next-button', function(){ calendar.next(); }); + click('prev-button', function(){ calendar.prev(); }); + click('today-button', function(){ calendar.today(); }); + click('month-button', function(){ calendar.changeView('dayGridMonth'); }); + click('week-button', function(){ calendar.changeView('timeGridWeek'); }); + click('day-button', function(){ calendar.changeView('timeGridDay'); }); + var dateInput = document.getElementById('go-to-date-input'); + var dateButton = document.getElementById('go-to-date-button'); + dateInput.addEventListener('change', function(){ + dateButton.disabled = !moment(dateInput.value).isValid(); }); - $('#go-to-date-button').click(function(){ - day = moment($('#go-to-date-input').val()); + dateButton.addEventListener('click', function(){ + day = moment(dateInput.value); if(day.isValid()){ calendar.gotoDate(day.format("YYYY-MM-DD")); } else{ @@ -164,25 +143,23 @@ {% block content %}
-
-
+
+
-
-
+
+
- - - +
-
+
-
+
-
+
diff --git a/RIGS/templates/event_archive.html b/RIGS/templates/event_archive.html index 41e2f27a..170bd9a2 100644 --- a/RIGS/templates/event_archive.html +++ b/RIGS/templates/event_archive.html @@ -17,26 +17,20 @@ {% block content %}
-
+
-
- Start -
+ Start
-
- End -
+ End
-
- Keyword -
+ Keyword
- {% for status in statuses %} {% endfor %} diff --git a/RIGS/templates/event_detail.html b/RIGS/templates/event_detail.html index 4013df65..a0749e12 100644 --- a/RIGS/templates/event_detail.html +++ b/RIGS/templates/event_detail.html @@ -7,10 +7,10 @@ {{ block.super }} @@ -20,7 +20,7 @@
{% if not is_ajax %} {% if perms.RIGS.view_event %} -
+
{% include 'partials/event_detail_buttons.html' %}
{% endif %} @@ -50,7 +50,7 @@ {% endif %} {% endif %} {% if not is_ajax and perms.RIGS.view_event %} -
+
{% include 'partials/event_detail_buttons.html' %}
{% endif %} @@ -73,13 +73,13 @@ {% include 'partials/crew_list.html' %} {% if not is_ajax and perms.RIGS.view_event %} -
+
{% include 'partials/event_detail_buttons.html' %}
{% endif %} {% endif %} {% if not is_ajax and perms.RIGS.view_event %} -
+
{% include 'partials/last_edited.html' with target="event_history" %}
{% endif %} diff --git a/RIGS/templates/event_embed.html b/RIGS/templates/event_embed.html index df2a69b2..df42800c 100644 --- a/RIGS/templates/event_embed.html +++ b/RIGS/templates/event_embed.html @@ -7,7 +7,7 @@ {% endblock %} {% block content %} - + {% if object.mic %}
@@ -39,8 +39,8 @@ {% include 'partials/event_status.html' %} -
-
+
+

{% if object.is_rig %} Client: {{ object.person.name }} @@ -61,7 +61,7 @@ {% endif %}

-
+
{% if object.meet_at %}

Crew meet: diff --git a/RIGS/templates/event_form.html b/RIGS/templates/event_form.html index 69d28455..2838c39b 100644 --- a/RIGS/templates/event_form.html +++ b/RIGS/templates/event_form.html @@ -19,64 +19,64 @@ {% block js %} {{ block.super }} - - {% endblock %} @@ -98,11 +98,11 @@

- - @@ -116,13 +116,13 @@
Contact Details
-
+
- {% if person %} {% endif %} @@ -131,10 +131,10 @@
-
+
- {% if organisation %} {% endif %} @@ -158,10 +158,10 @@
@@ -174,7 +174,7 @@
Event Description
-
+
@@ -192,7 +192,7 @@
Event Details
-
+
@@ -200,14 +200,14 @@ {% render_field form.name class+="form-control" %}
-
+
- {% if venue %} {% endif %} @@ -216,10 +216,10 @@
-
+
-
+
{% render_field form.start_date class+="form-control" %}
-
+
{% render_field form.start_time class+="form-control" step="60" %}
-
+
-
+
{% render_field form.end_date class+="form-control" %}
-
+
{% render_field form.end_time class+="form-control" step="60" %}
@@ -260,14 +260,14 @@ {# Rig only information #}
-
+
{% render_field form.access_at class+="form-control" step="60" %}
-
+
@@ -275,15 +275,15 @@ {% render_field form.meet_at class+="form-control" step="60" %}
-
+
-
-
+
-
@@ -292,7 +292,7 @@
{# Status is needed on all events types and it looks good here in the form #} -
+
@@ -302,12 +302,12 @@
-
+
- {% if mic %} {% endif %} @@ -316,12 +316,12 @@
{% if object.dry_hire %} -
+
- {% if checked_in_by %} {% endif %} @@ -330,7 +330,7 @@
{% endif %} -
+
@@ -339,7 +339,7 @@
-
+
@@ -348,7 +348,7 @@
-
+
@@ -357,7 +357,7 @@ {% if object.pk %}

or

- + Create Forum Thread {% endif %}
@@ -372,7 +372,7 @@
-
+
{% render_field form.notes class+="form-control md-enabled" %}
@@ -381,7 +381,7 @@
-
+
{% button 'submit' %}
diff --git a/RIGS/templates/eventauthorisation.html b/RIGS/templates/eventauthorisation.html index f464f72d..e9a6d0b5 100644 --- a/RIGS/templates/eventauthorisation.html +++ b/RIGS/templates/eventauthorisation.html @@ -3,15 +3,13 @@ {% load static %} {% block js %} - diff --git a/RIGS/templates/eventauthorisation_form.html b/RIGS/templates/eventauthorisation_form.html index 771ddeaa..0390653d 100644 --- a/RIGS/templates/eventauthorisation_form.html +++ b/RIGS/templates/eventauthorisation_form.html @@ -23,7 +23,7 @@
-
@@ -33,7 +33,7 @@
-
@@ -44,7 +44,7 @@
-
@@ -53,15 +53,13 @@
-
-
- £ -
+ £ {% render_field form.amount class+="form-control" %}
@@ -69,8 +67,8 @@
-
-
+
-
+
- +
diff --git a/RIGS/templates/eventauthorisation_request.html b/RIGS/templates/eventauthorisation_request.html index a8adc86f..5c883f07 100644 --- a/RIGS/templates/eventauthorisation_request.html +++ b/RIGS/templates/eventauthorisation_request.html @@ -18,11 +18,11 @@
{% if object.person.email %}
Person Email
-
{{ object.person.email }} {% button 'copy' id='#person-email' %}
+
{{ object.person.email }} {% button 'copy' id='#person-email' %}
{% endif %} {% if object.organisation.email %}
Organisation Email
-
{{ object.organisation.email }} {% button 'copy' id='#org-email' %}
+
{{ object.organisation.email }} {% button 'copy' id='#org-email' %}
{% endif %}
{% else %} @@ -32,7 +32,7 @@ {% csrf_token %} {% include 'form_errors.html' %} -
+
@@ -42,25 +42,24 @@
- - {% endblock %} {% block footer %} -
+
Preview @@ -67,9 +65,9 @@
-
+
-
+
@@ -80,7 +78,7 @@
{% if edit or manual %} -
+
@@ -90,7 +88,7 @@ {% endif %} {% if not is_ajax %}
-
+
{% button 'submit' %}
@@ -100,7 +98,7 @@ {% endblock %} {% block footer %} -
+
diff --git a/RIGS/templates/hs/hs_list.html b/RIGS/templates/hs/hs_list.html index f57f6e29..3d573432 100644 --- a/RIGS/templates/hs/hs_list.html +++ b/RIGS/templates/hs/hs_list.html @@ -42,7 +42,7 @@
{% endfor %} + class="d-none d-sm-inline">Create {% empty %} diff --git a/RIGS/templates/hs/power_detail.html b/RIGS/templates/hs/power_detail.html index 2fd123f6..5a5a3e39 100644 --- a/RIGS/templates/hs/power_detail.html +++ b/RIGS/templates/hs/power_detail.html @@ -6,7 +6,7 @@ {% block content %}
-
+
{% button 'edit' url='pt_edit' pk=object.pk %} {% button 'view' url='event_detail' pk=object.event.pk text="Event" %} {% include 'partials/review_status.html' with perm=perms.RIGS.review_power review='pt_review' %} @@ -164,13 +164,13 @@ {% endif %}
-
+
{% button 'print' 'pt_print' object.pk %} {% button 'edit' url='pt_edit' pk=object.pk %} {% button 'view' url='event_detail' pk=object.event.pk text="Event" %} {% include 'partials/review_status.html' with perm=perms.RIGS.review_power review='pt_review' %}
-
+
{% include 'partials/last_edited.html' with target="powertestrecord_history" %}
{% endblock %} diff --git a/RIGS/templates/hs/power_form.html b/RIGS/templates/hs/power_form.html index e07877e0..f9bcc1b5 100644 --- a/RIGS/templates/hs/power_form.html +++ b/RIGS/templates/hs/power_form.html @@ -17,8 +17,6 @@ {% block js %} {{ block.super }} - - {% endblock %} {% block content %} @@ -48,19 +46,19 @@
{% include 'partials/event_size.html' with object=event.riskassessment %}

-
+
- {% if power_mic %} {% endif %}
-
+
- {% if venue %} {% endif %} @@ -190,7 +188,7 @@
{% endif %}
-
+
{% button 'submit' %}
diff --git a/RIGS/templates/hs/risk_assessment_detail.html b/RIGS/templates/hs/risk_assessment_detail.html index 146d2ddc..1e84ab69 100644 --- a/RIGS/templates/hs/risk_assessment_detail.html +++ b/RIGS/templates/hs/risk_assessment_detail.html @@ -165,7 +165,7 @@
-
+
{% button 'print' 'ra_print' object.pk %} Edit diff --git a/RIGS/templates/hs/risk_assessment_form.html b/RIGS/templates/hs/risk_assessment_form.html index cccef9bd..0d677e3c 100644 --- a/RIGS/templates/hs/risk_assessment_form.html +++ b/RIGS/templates/hs/risk_assessment_form.html @@ -13,27 +13,35 @@ {% endblock %} {% block js %} - - {% endblock %} @@ -42,13 +50,13 @@
{% if form.errors %}
- +
{% with form|nice_errors as qq %} {% for error_name,desc in qq.items %} {% if error_name == 'General form errors' %} {{ form.non_field_errors.0|safe }} -
+
{% render_field form.supervisor_consulted class+="form-check-input" form="form" %}
@@ -98,7 +106,7 @@
- {% if power_mic %} {% endif %} @@ -174,7 +182,7 @@
-
+
@@ -65,11 +64,11 @@ £ {% if edit %} - +
-
+
{% include 'partials/form_field.html' with field=form.date_sold col="col-6" %}
-
+
{% include 'partials/form_field.html' with field=form.replacement_cost col="col-6" prepend="£" %}

-
+
{% render_field form.is_cable|attr:'onchange=checkIfCableHidden()' %}
-
+
{% include 'partials/form_field.html' with field=form.cable_type col="col-6" %}
-
+
{% include 'partials/form_field.html' with field=form.length append=form.length.help_text col="col-6" %}
@@ -71,7 +71,7 @@
-
+
{% include 'partials/form_field.html' with field=form.csa append=form.csa.help_text title='CSA' col="col-6" %}
@@ -80,7 +80,7 @@
{% if not is_ajax %} -
+
{% endif %} @@ -88,7 +88,7 @@ {% endblock %} {% block footer %} -
- +
+
{% endblock %} diff --git a/assets/templates/asset_audit_list.html b/assets/templates/asset_audit_list.html index 73711dbb..1c9f67b0 100644 --- a/assets/templates/asset_audit_list.html +++ b/assets/templates/asset_audit_list.html @@ -5,59 +5,53 @@ {% block js %} {% endblock %} {% block content %} -
+

Audit Asset:

{% render_field form.q|add_class:'form-control' placeholder='Enter Asset ID' autofocus="true" %} -
- + Search -
diff --git a/assets/templates/asset_form.html b/assets/templates/asset_form.html index b7c96e7b..0229a464 100644 --- a/assets/templates/asset_form.html +++ b/assets/templates/asset_form.html @@ -16,30 +16,18 @@ {% block js %} {{ block.super }} - - - - {% endblock %} {% block content %} diff --git a/assets/templates/asset_list.html b/assets/templates/asset_list.html index e00dcd04..31191d46 100644 --- a/assets/templates/asset_list.html +++ b/assets/templates/asset_list.html @@ -62,26 +62,26 @@
-
+
{% render_field form.q|add_class:'form-control' placeholder='Enter Asset ID/Desc/Serial' %} - - {% button 'search' id="id_search" %} + + {% button 'search' id="id_search" %}
-
+
-
- - {% render_field form.category|attr:'multiple'|add_class:'selectpicker col-sm' data-none-selected-text="Categories" data-header="Categories" data-actions-box="true" %} +
+ + {% render_field form.category|attr:'multiple'|add_class:'ts-select col-sm' data-none-selected-text="Categories" data-header="Categories" data-actions-box="true" %}
-
- - {% render_field form.status|attr:'multiple'|add_class:'selectpicker col-sm' data-none-selected-text="Statuses" data-header="Statuses" data-actions-box="true" %} +
+ + {% render_field form.status|attr:'multiple'|add_class:'ts-select col-sm' data-none-selected-text="Statuses" data-header="Statuses" data-actions-box="true" %}
@@ -91,12 +91,12 @@
-
+
{% render_field form.date_acquired|add_class:'form-control mx-2' %}
-
+
@@ -104,7 +104,7 @@
-
+
{% button 'new' 'asset_create' style="width: 6em" %} {% if object_list %} Generate Labels @@ -114,9 +114,9 @@
{# TODO Gotta be a cleaner way to do this... #} -

Active Filters: {% for filter in category_filters %}{{filter}}{%endfor%}{% for filter in status_filters %}{{filter}}{%endfor%}{% for filter in status_filters %}{{filter}}{%endfor%}

diff --git a/assets/templates/asset_update_search_results.html b/assets/templates/asset_update_search_results.html index 46fcc57c..3175253b 100644 --- a/assets/templates/asset_update_search_results.html +++ b/assets/templates/asset_update_search_results.html @@ -9,8 +9,7 @@ diff --git a/assets/templates/base_assets.html b/assets/templates/base_assets.html index 9d0a491d..6fc46d2c 100644 --- a/assets/templates/base_assets.html +++ b/assets/templates/base_assets.html @@ -11,7 +11,7 @@ {% block titleelements %}