Compare commits
85 commits
Author | SHA1 | Date | |
---|---|---|---|
e150453801 | |||
e2582ffec5 | |||
a6508154cb | |||
7348eec671 | |||
4221fa50dd | |||
e0ebd43e7a | |||
c7ee9de9eb | |||
950db697a0 | |||
358b3b75a5 | |||
7e559a01b3 | |||
23bdde245a | |||
2c269b640b | |||
![]() |
f1c8ffedda | ||
![]() |
aace6033c5 | ||
![]() |
7171c7fb06 | ||
![]() |
993f066f2c | ||
![]() |
980031f524 | ||
![]() |
bcaf0d08bc | ||
![]() |
ac439c949b | ||
![]() |
20b3f87dbe | ||
![]() |
9e55717041 | ||
![]() |
772b6610cb | ||
![]() |
3f27f626df | ||
![]() |
29743e70b7 | ||
![]() |
54220601ed | ||
![]() |
9a6f8970ae | ||
![]() |
28f90ad6b5 | ||
![]() |
535317e4f7 | ||
![]() |
072db39233 | ||
![]() |
c6365f2631 | ||
![]() |
d520f64fee | ||
![]() |
2308d2e240 | ||
![]() |
0b4efae392 | ||
![]() |
0b646d2d18 | ||
![]() |
a4d81a6e3c | ||
![]() |
5e742eab17 | ||
![]() |
b9b8bbd2ea | ||
![]() |
8df52bf2a2 | ||
![]() |
55f45ae8a0 | ||
![]() |
2219cf8198 | ||
![]() |
9be2f63475 | ||
![]() |
812d8d2349 | ||
![]() |
20dcdd8b86 | ||
![]() |
bc399182ba | ||
![]() |
71d63f6b93 | ||
![]() |
0b6cfaa9c5 | ||
![]() |
b81914fbf5 | ||
![]() |
b30f066c41 | ||
![]() |
2e20fc5b75 | ||
![]() |
ca05e68890 | ||
![]() |
7a9d5772db | ||
![]() |
dffd951369 | ||
![]() |
d67eb2f1cc | ||
![]() |
3a9bf69aa7 | ||
![]() |
d1f4c0f150 | ||
![]() |
b7991b5dc6 | ||
![]() |
c1a2c9cc70 | ||
![]() |
37f760959d | ||
![]() |
cea3e4b927 | ||
![]() |
29531c83c4 | ||
![]() |
4c3e3aeb6a | ||
![]() |
c176d97870 | ||
![]() |
7d6f75bb05 | ||
![]() |
7b40c527c8 | ||
![]() |
f292850d05 | ||
![]() |
8d5427e92f | ||
![]() |
b8131c8393 | ||
![]() |
e52a83751e | ||
![]() |
ffa724ef37 | ||
![]() |
1d00fe994a | ||
![]() |
71abbe06da | ||
![]() |
f755460242 | ||
![]() |
2ffc067097 | ||
![]() |
b6a8e508bf | ||
![]() |
1def26a35b | ||
![]() |
07871188aa | ||
![]() |
c8dc60cb68 | ||
![]() |
526c84dfa6 | ||
![]() |
21f90f3f32 | ||
![]() |
83586ef90f | ||
![]() |
59bd58aca7 | ||
![]() |
1ec1eba496 | ||
![]() |
d29b840343 | ||
![]() |
b762a0782a | ||
![]() |
15ab0c9592 |
143 changed files with 3724 additions and 2606 deletions
181
CHANGELOG.md
181
CHANGELOG.md
|
@ -5,6 +5,187 @@ All notable changes to Tailbone will be documented in this file.
|
|||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## v0.22.7 (2025-02-19)
|
||||
|
||||
### Fix
|
||||
|
||||
- stop using old config for logo image url on login page
|
||||
- fix warning msg for deprecated Grid param
|
||||
|
||||
## v0.22.6 (2025-02-01)
|
||||
|
||||
### Fix
|
||||
|
||||
- register vue3 form component for products -> make batch
|
||||
|
||||
## v0.22.5 (2024-12-16)
|
||||
|
||||
### Fix
|
||||
|
||||
- whoops this is latest rattail
|
||||
- require newer rattail lib
|
||||
- require newer wuttaweb
|
||||
- let caller request safe HTML literal for rendered grid table
|
||||
|
||||
## v0.22.4 (2024-11-22)
|
||||
|
||||
### Fix
|
||||
|
||||
- avoid error in product search for duplicated key
|
||||
- use vmodel for confirm password widget input
|
||||
|
||||
## v0.22.3 (2024-11-19)
|
||||
|
||||
### Fix
|
||||
|
||||
- avoid error for trainwreck query when not a customer
|
||||
|
||||
## v0.22.2 (2024-11-18)
|
||||
|
||||
### Fix
|
||||
|
||||
- use local/custom enum for continuum operations
|
||||
- add basic master view for Product Costs
|
||||
- show continuum operation type when viewing version history
|
||||
- always define `app` attr for ViewSupplement
|
||||
- avoid deprecated import
|
||||
|
||||
## v0.22.1 (2024-11-02)
|
||||
|
||||
### Fix
|
||||
|
||||
- fix submit button for running problem report
|
||||
- avoid deprecated grid method
|
||||
|
||||
## v0.22.0 (2024-10-22)
|
||||
|
||||
### Feat
|
||||
|
||||
- add support for new ordering batch from parsed file
|
||||
|
||||
### Fix
|
||||
|
||||
- avoid deprecated method to suggest username
|
||||
|
||||
## v0.21.11 (2024-10-03)
|
||||
|
||||
### Fix
|
||||
|
||||
- custom method for adding grid action
|
||||
- become/stop root should redirect to previous url
|
||||
|
||||
## v0.21.10 (2024-09-15)
|
||||
|
||||
### Fix
|
||||
|
||||
- update project repo links, kallithea -> forgejo
|
||||
- use better icon for submit button on login page
|
||||
- wrap notes text for batch view
|
||||
- expose datasync consumer batch size via configure page
|
||||
|
||||
## v0.21.9 (2024-08-28)
|
||||
|
||||
### Fix
|
||||
|
||||
- render custom attrs in form component tag
|
||||
|
||||
## v0.21.8 (2024-08-28)
|
||||
|
||||
### Fix
|
||||
|
||||
- ignore session kwarg for `MasterView.make_row_grid()`
|
||||
|
||||
## v0.21.7 (2024-08-28)
|
||||
|
||||
### Fix
|
||||
|
||||
- avoid error when form value cannot be obtained
|
||||
|
||||
## v0.21.6 (2024-08-28)
|
||||
|
||||
### Fix
|
||||
|
||||
- avoid error when grid value cannot be obtained
|
||||
|
||||
## v0.21.5 (2024-08-28)
|
||||
|
||||
### Fix
|
||||
|
||||
- set empty string for "-new-" file configure option
|
||||
|
||||
## v0.21.4 (2024-08-26)
|
||||
|
||||
### Fix
|
||||
|
||||
- handle differing email profile keys for appinfo/configure
|
||||
|
||||
## v0.21.3 (2024-08-26)
|
||||
|
||||
### Fix
|
||||
|
||||
- show non-standard config values for app info configure email
|
||||
|
||||
## v0.21.2 (2024-08-26)
|
||||
|
||||
### Fix
|
||||
|
||||
- refactor waterpark base template to use wutta feedback component
|
||||
- fix input/output file upload feature for configure pages, per oruga
|
||||
- tweak how grid data translates to Vue template context
|
||||
- merge filters into main grid template
|
||||
- add basic wutta view for users
|
||||
- some fixes for wutta people view
|
||||
- various fixes for waterpark theme
|
||||
- avoid deprecated `component` form kwarg
|
||||
|
||||
## v0.21.1 (2024-08-22)
|
||||
|
||||
### Fix
|
||||
|
||||
- misc. bugfixes per recent changes
|
||||
|
||||
## v0.21.0 (2024-08-22)
|
||||
|
||||
### Feat
|
||||
|
||||
- move "most" filtering logic for grid class to wuttaweb
|
||||
- inherit from wuttaweb templates for home, login pages
|
||||
- inherit from wuttaweb for AppInfoView, appinfo/configure template
|
||||
- add "has output file templates" config option for master view
|
||||
|
||||
### Fix
|
||||
|
||||
- change grid reset-view param name to match wuttaweb
|
||||
- move "searchable columns" grid feature to wuttaweb
|
||||
- use wuttaweb to get/render csrf token
|
||||
- inherit from wuttaweb for appinfo/index template
|
||||
- prefer wuttaweb config for "home redirect to login" feature
|
||||
- fix master/index template rendering for waterpark theme
|
||||
- fix spacing for navbar logo/title in waterpark theme
|
||||
|
||||
## v0.20.1 (2024-08-20)
|
||||
|
||||
### Fix
|
||||
|
||||
- fix default filter verbs logic for workorder status
|
||||
|
||||
## v0.20.0 (2024-08-20)
|
||||
|
||||
### Feat
|
||||
|
||||
- add new 'waterpark' theme, based on wuttaweb w/ vue2 + buefy
|
||||
- refactor templates to simplify base/page/form structure
|
||||
|
||||
### Fix
|
||||
|
||||
- avoid deprecated reference to app db engine
|
||||
|
||||
## v0.19.3 (2024-08-19)
|
||||
|
||||
### Fix
|
||||
|
||||
- add pager stats to all grid vue data (fixes view history)
|
||||
|
||||
## v0.19.2 (2024-08-19)
|
||||
|
||||
### Fix
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
|
||||
Tailbone
|
||||
========
|
||||
# Tailbone
|
||||
|
||||
Tailbone is an extensible web application based on Rattail. It provides a
|
||||
"back-office network environment" (BONE) for use in managing retail data.
|
||||
|
||||
Please see Rattail's `home page`_ for more information.
|
||||
|
||||
.. _home page: http://rattailproject.org/
|
||||
Please see Rattail's [home page](http://rattailproject.org/) for more
|
||||
information.
|
|
@ -27,10 +27,10 @@ templates_path = ['_templates']
|
|||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
intersphinx_mapping = {
|
||||
'rattail': ('https://rattailproject.org/docs/rattail/', None),
|
||||
'rattail': ('https://docs.wuttaproject.org/rattail/', None),
|
||||
'webhelpers2': ('https://webhelpers2.readthedocs.io/en/latest/', None),
|
||||
'wuttaweb': ('https://rattailproject.org/docs/wuttaweb/', None),
|
||||
'wuttjamaican': ('https://rattailproject.org/docs/wuttjamaican/', None),
|
||||
'wuttaweb': ('https://docs.wuttaproject.org/wuttaweb/', None),
|
||||
'wuttjamaican': ('https://docs.wuttaproject.org/wuttjamaican/', None),
|
||||
}
|
||||
|
||||
# allow todo entries to show up
|
||||
|
|
|
@ -6,9 +6,9 @@ build-backend = "hatchling.build"
|
|||
|
||||
[project]
|
||||
name = "Tailbone"
|
||||
version = "0.19.2"
|
||||
version = "0.22.7"
|
||||
description = "Backoffice Web Application for Rattail"
|
||||
readme = "README.rst"
|
||||
readme = "README.md"
|
||||
authors = [{name = "Lance Edgar", email = "lance@edbob.org"}]
|
||||
license = {text = "GNU GPL v3+"}
|
||||
classifiers = [
|
||||
|
@ -53,13 +53,13 @@ dependencies = [
|
|||
"pyramid_mako",
|
||||
"pyramid_retry",
|
||||
"pyramid_tm",
|
||||
"rattail[db,bouncer]>=0.18.1",
|
||||
"rattail[db,bouncer]>=0.20.1",
|
||||
"sa-filters",
|
||||
"simplejson",
|
||||
"transaction",
|
||||
"waitress",
|
||||
"WebHelpers2",
|
||||
"WuttaWeb>=0.10.1",
|
||||
"WuttaWeb>=0.21.0",
|
||||
"zope.sqlalchemy>=1.5",
|
||||
]
|
||||
|
||||
|
@ -84,9 +84,9 @@ tailbone = "tailbone.config:ConfigExtension"
|
|||
|
||||
[project.urls]
|
||||
Homepage = "https://rattailproject.org"
|
||||
Repository = "https://kallithea.rattailproject.org/rattail-project/tailbone"
|
||||
Issues = "https://redmine.rattailproject.org/projects/tailbone/issues"
|
||||
Changelog = "https://kallithea.rattailproject.org/rattail-project/tailbone/files/master/CHANGELOG.md"
|
||||
Repository = "https://forgejo.wuttaproject.org/rattail/tailbone"
|
||||
Issues = "https://forgejo.wuttaproject.org/rattail/tailbone/issues"
|
||||
Changelog = "https://forgejo.wuttaproject.org/rattail/tailbone/src/branch/master/CHANGELOG.md"
|
||||
|
||||
|
||||
[tool.commitizen]
|
||||
|
|
|
@ -29,8 +29,7 @@ import logging
|
|||
import humanize
|
||||
import sqlalchemy as sa
|
||||
|
||||
from rattail.db import model
|
||||
from rattail.util import pretty_quantity
|
||||
from rattail.db.model import PurchaseBatch, PurchaseBatchRow
|
||||
|
||||
from cornice import Service
|
||||
from deform import widget as dfwidget
|
||||
|
@ -45,7 +44,7 @@ log = logging.getLogger(__name__)
|
|||
|
||||
class ReceivingBatchViews(APIBatchView):
|
||||
|
||||
model_class = model.PurchaseBatch
|
||||
model_class = PurchaseBatch
|
||||
default_handler_spec = 'rattail.batch.purchase:PurchaseBatchHandler'
|
||||
route_prefix = 'receivingbatchviews'
|
||||
permission_prefix = 'receiving'
|
||||
|
@ -55,7 +54,8 @@ class ReceivingBatchViews(APIBatchView):
|
|||
supports_execute = True
|
||||
|
||||
def base_query(self):
|
||||
query = super(ReceivingBatchViews, self).base_query()
|
||||
model = self.app.model
|
||||
query = super().base_query()
|
||||
query = query.filter(model.PurchaseBatch.mode == self.enum.PURCHASE_BATCH_MODE_RECEIVING)
|
||||
return query
|
||||
|
||||
|
@ -85,7 +85,7 @@ class ReceivingBatchViews(APIBatchView):
|
|||
|
||||
# assume "receive from PO" if given a PO key
|
||||
if data.get('purchase_key'):
|
||||
data['receiving_workflow'] = 'from_po'
|
||||
data['workflow'] = 'from_po'
|
||||
|
||||
return super().create_object(data)
|
||||
|
||||
|
@ -120,6 +120,7 @@ class ReceivingBatchViews(APIBatchView):
|
|||
return self._get(obj=batch)
|
||||
|
||||
def eligible_purchases(self):
|
||||
model = self.app.model
|
||||
uuid = self.request.params.get('vendor_uuid')
|
||||
vendor = self.Session.get(model.Vendor, uuid) if uuid else None
|
||||
if not vendor:
|
||||
|
@ -176,7 +177,7 @@ class ReceivingBatchViews(APIBatchView):
|
|||
|
||||
class ReceivingBatchRowViews(APIBatchRowView):
|
||||
|
||||
model_class = model.PurchaseBatchRow
|
||||
model_class = PurchaseBatchRow
|
||||
default_handler_spec = 'rattail.batch.purchase:PurchaseBatchHandler'
|
||||
route_prefix = 'receiving.rows'
|
||||
permission_prefix = 'receiving'
|
||||
|
@ -185,7 +186,8 @@ class ReceivingBatchRowViews(APIBatchRowView):
|
|||
supports_quick_entry = True
|
||||
|
||||
def make_filter_spec(self):
|
||||
filters = super(ReceivingBatchRowViews, self).make_filter_spec()
|
||||
model = self.app.model
|
||||
filters = super().make_filter_spec()
|
||||
if filters:
|
||||
|
||||
# must translate certain convenience filters
|
||||
|
@ -296,11 +298,11 @@ class ReceivingBatchRowViews(APIBatchRowView):
|
|||
return filters
|
||||
|
||||
def normalize(self, row):
|
||||
data = super(ReceivingBatchRowViews, self).normalize(row)
|
||||
data = super().normalize(row)
|
||||
model = self.app.model
|
||||
|
||||
batch = row.batch
|
||||
app = self.get_rattail_app()
|
||||
prodder = app.get_products_handler()
|
||||
prodder = self.app.get_products_handler()
|
||||
|
||||
data['product_uuid'] = row.product_uuid
|
||||
data['item_id'] = row.item_id
|
||||
|
@ -375,7 +377,7 @@ class ReceivingBatchRowViews(APIBatchRowView):
|
|||
if accounted_for:
|
||||
# some product accounted for; button should receive "remainder" only
|
||||
if remainder:
|
||||
remainder = pretty_quantity(remainder)
|
||||
remainder = self.app.render_quantity(remainder)
|
||||
data['quick_receive_quantity'] = remainder
|
||||
data['quick_receive_text'] = "Receive Remainder ({} {})".format(
|
||||
remainder, data['unit_uom'])
|
||||
|
@ -386,7 +388,7 @@ class ReceivingBatchRowViews(APIBatchRowView):
|
|||
else: # nothing yet accounted for, button should receive "all"
|
||||
if not remainder:
|
||||
log.warning("quick receive remainder is empty for row %s", row.uuid)
|
||||
remainder = pretty_quantity(remainder)
|
||||
remainder = self.app.render_quantity(remainder)
|
||||
data['quick_receive_quantity'] = remainder
|
||||
data['quick_receive_text'] = "Receive ALL ({} {})".format(
|
||||
remainder, data['unit_uom'])
|
||||
|
@ -414,7 +416,7 @@ class ReceivingBatchRowViews(APIBatchRowView):
|
|||
data['received_alert'] = None
|
||||
if self.batch_handler.get_units_confirmed(row):
|
||||
msg = "You have already received some of this product; last update was {}.".format(
|
||||
humanize.naturaltime(app.make_utc() - row.modified))
|
||||
humanize.naturaltime(self.app.make_utc() - row.modified))
|
||||
data['received_alert'] = msg
|
||||
|
||||
return data
|
||||
|
@ -423,6 +425,8 @@ class ReceivingBatchRowViews(APIBatchRowView):
|
|||
"""
|
||||
View which handles "receiving" against a particular batch row.
|
||||
"""
|
||||
model = self.app.model
|
||||
|
||||
# first do basic input validation
|
||||
schema = ReceiveRow().bind(session=self.Session())
|
||||
form = forms.Form(schema=schema, request=self.request)
|
||||
|
|
|
@ -26,7 +26,6 @@ Tailbone Web API - Master View
|
|||
|
||||
import json
|
||||
|
||||
from rattail.config import parse_bool
|
||||
from rattail.db.util import get_fieldnames
|
||||
|
||||
from cornice import resource, Service
|
||||
|
@ -185,7 +184,7 @@ class APIMasterView(APIView):
|
|||
if sortcol:
|
||||
spec = {
|
||||
'field': sortcol.field_name,
|
||||
'direction': 'asc' if parse_bool(self.request.params['ascending']) else 'desc',
|
||||
'direction': 'asc' if self.config.parse_bool(self.request.params['ascending']) else 'desc',
|
||||
}
|
||||
if sortcol.model_name:
|
||||
spec['model'] = sortcol.model_name
|
||||
|
|
|
@ -62,9 +62,20 @@ def make_rattail_config(settings):
|
|||
# nb. this is for compaibility with wuttaweb
|
||||
settings['wutta_config'] = rattail_config
|
||||
|
||||
# must import all sqlalchemy models before things get rolling,
|
||||
# otherwise can have errors about continuum TransactionMeta class
|
||||
# not yet mapped, when relevant pages are first requested...
|
||||
# cf. https://docs.pylonsproject.org/projects/pyramid_cookbook/en/latest/database/sqlalchemy.html#importing-all-sqlalchemy-models
|
||||
# hat tip to https://stackoverflow.com/a/59241485
|
||||
if getattr(rattail_config, 'tempmon_engine', None):
|
||||
from rattail_tempmon.db import model as tempmon_model, Session as TempmonSession
|
||||
tempmon_session = TempmonSession()
|
||||
tempmon_session.query(tempmon_model.Appliance).first()
|
||||
tempmon_session.close()
|
||||
|
||||
# configure database sessions
|
||||
if hasattr(rattail_config, 'rattail_engine'):
|
||||
tailbone.db.Session.configure(bind=rattail_config.rattail_engine)
|
||||
if hasattr(rattail_config, 'appdb_engine'):
|
||||
tailbone.db.Session.configure(bind=rattail_config.appdb_engine)
|
||||
if hasattr(rattail_config, 'trainwreck_engine'):
|
||||
tailbone.db.TrainwreckSession.configure(bind=rattail_config.trainwreck_engine)
|
||||
if hasattr(rattail_config, 'tempmon_engine'):
|
||||
|
@ -321,7 +332,8 @@ def main(global_config, **settings):
|
|||
"""
|
||||
This function returns a Pyramid WSGI application.
|
||||
"""
|
||||
settings.setdefault('mako.directories', ['tailbone:templates'])
|
||||
settings.setdefault('mako.directories', ['tailbone:templates',
|
||||
'wuttaweb:templates'])
|
||||
rattail_config = make_rattail_config(settings)
|
||||
pyramid_config = make_pyramid_config(settings)
|
||||
pyramid_config.include('tailbone')
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2023 Lance Edgar
|
||||
# Copyright © 2010-2024 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
|
@ -270,9 +270,21 @@ class VersionDiff(Diff):
|
|||
for field in self.fields:
|
||||
values[field] = {'before': self.render_old_value(field),
|
||||
'after': self.render_new_value(field)}
|
||||
|
||||
operation = None
|
||||
if self.version.operation_type == continuum.Operation.INSERT:
|
||||
operation = 'INSERT'
|
||||
elif self.version.operation_type == continuum.Operation.UPDATE:
|
||||
operation = 'UPDATE'
|
||||
elif self.version.operation_type == continuum.Operation.DELETE:
|
||||
operation = 'DELETE'
|
||||
else:
|
||||
operation = self.version.operation_type
|
||||
|
||||
return {
|
||||
'key': id(self.version),
|
||||
'model_title': self.title,
|
||||
'operation': operation,
|
||||
'diff_class': self.nature,
|
||||
'fields': self.fields,
|
||||
'values': values,
|
||||
|
|
|
@ -401,6 +401,8 @@ class Form(object):
|
|||
self.edit_help_url = edit_help_url
|
||||
self.route_prefix = route_prefix
|
||||
|
||||
self.button_icon_submit = kwargs.get('button_icon_submit', 'save')
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.fields)
|
||||
|
||||
|
@ -905,7 +907,8 @@ class Form(object):
|
|||
|
||||
def render_vue_template(self, template='/forms/deform.mako', **context):
|
||||
""" """
|
||||
return self.render_deform(template=template, **context)
|
||||
output = self.render_deform(template=template, **context)
|
||||
return HTML.literal(output)
|
||||
|
||||
def render_deform(self, dform=None, template=None, **kwargs):
|
||||
if not template:
|
||||
|
@ -1036,9 +1039,9 @@ class Form(object):
|
|||
|
||||
def render_vue_tag(self, **kwargs):
|
||||
""" """
|
||||
return self.render_vuejs_component()
|
||||
return self.render_vuejs_component(**kwargs)
|
||||
|
||||
def render_vuejs_component(self):
|
||||
def render_vuejs_component(self, **kwargs):
|
||||
"""
|
||||
Render the Vue.js component HTML for the form.
|
||||
|
||||
|
@ -1049,10 +1052,11 @@ class Form(object):
|
|||
<tailbone-form :configure-fields-help="configureFieldsHelp">
|
||||
</tailbone-form>
|
||||
"""
|
||||
kwargs = dict(self.vuejs_component_kwargs)
|
||||
kw = dict(self.vuejs_component_kwargs)
|
||||
kw.update(kwargs)
|
||||
if self.can_edit_help:
|
||||
kwargs.setdefault(':configure-fields-help', 'configureFieldsHelp')
|
||||
return HTML.tag(self.vue_tagname, **kwargs)
|
||||
kw.setdefault(':configure-fields-help', 'configureFieldsHelp')
|
||||
return HTML.tag(self.vue_tagname, **kw)
|
||||
|
||||
def set_json_data(self, key, value):
|
||||
"""
|
||||
|
@ -1220,6 +1224,18 @@ class Form(object):
|
|||
# TODO: again, why does serialize() not return literal?
|
||||
return HTML.literal(field.serialize())
|
||||
|
||||
# TODO: this was copied from wuttaweb; can remove when we align
|
||||
# Form class structure
|
||||
def render_vue_finalize(self):
|
||||
""" """
|
||||
set_data = f"{self.vue_component}.data = function() {{ return {self.vue_component}Data }}"
|
||||
make_component = f"Vue.component('{self.vue_tagname}', {self.vue_component})"
|
||||
return HTML.tag('script', c=['\n',
|
||||
HTML.literal(set_data),
|
||||
'\n',
|
||||
HTML.literal(make_component),
|
||||
'\n'])
|
||||
|
||||
def render_field_readonly(self, field_name, **kwargs):
|
||||
"""
|
||||
Render the given field completely, but in read-only fashion.
|
||||
|
@ -1367,7 +1383,11 @@ class Form(object):
|
|||
return getattr(record, field_name)
|
||||
except AttributeError:
|
||||
pass
|
||||
return record[field_name]
|
||||
|
||||
try:
|
||||
return record[field_name]
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
# TODO: is this always safe to do?
|
||||
elif self.defaults and field_name in self.defaults:
|
||||
|
|
|
@ -24,9 +24,10 @@
|
|||
Core Grid Classes
|
||||
"""
|
||||
|
||||
from urllib.parse import urlencode
|
||||
import warnings
|
||||
import inspect
|
||||
import logging
|
||||
import warnings
|
||||
from urllib.parse import urlencode
|
||||
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy import orm
|
||||
|
@ -196,11 +197,7 @@ class Grid(WuttaGrid):
|
|||
raw_renderers={},
|
||||
extra_row_class=None,
|
||||
url='#',
|
||||
joiners={},
|
||||
filterable=False,
|
||||
filters={},
|
||||
use_byte_string_filters=False,
|
||||
searchable={},
|
||||
checkboxes=False,
|
||||
checked=None,
|
||||
check_handler=None,
|
||||
|
@ -216,48 +213,59 @@ class Grid(WuttaGrid):
|
|||
expose_direct_link=False,
|
||||
**kwargs,
|
||||
):
|
||||
if kwargs.get('component'):
|
||||
if 'component' in kwargs:
|
||||
warnings.warn("component param is deprecated for Grid(); "
|
||||
"please use vue_tagname param instead",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
kwargs.setdefault('vue_tagname', kwargs.pop('component'))
|
||||
|
||||
if kwargs.get('default_sortkey'):
|
||||
if 'default_sortkey' in kwargs:
|
||||
warnings.warn("default_sortkey param is deprecated for Grid(); "
|
||||
"please use sort_defaults param instead",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
if kwargs.get('default_sortdir'):
|
||||
if 'default_sortdir' in kwargs:
|
||||
warnings.warn("default_sortdir param is deprecated for Grid(); "
|
||||
"please use sort_defaults param instead",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
if kwargs.get('default_sortkey') or kwargs.get('default_sortdir'):
|
||||
if 'default_sortkey' in kwargs or 'default_sortdir' in kwargs:
|
||||
sortkey = kwargs.pop('default_sortkey', None)
|
||||
sortdir = kwargs.pop('default_sortdir', 'asc')
|
||||
if sortkey:
|
||||
kwargs.setdefault('sort_defaults', [(sortkey, sortdir)])
|
||||
|
||||
if kwargs.get('pageable'):
|
||||
warnings.warn("component param is deprecated for Grid(); "
|
||||
"please use vue_tagname param instead",
|
||||
if 'pageable' in kwargs:
|
||||
warnings.warn("pageable param is deprecated for Grid(); "
|
||||
"please use paginated param instead",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
kwargs.setdefault('paginated', kwargs.pop('pageable'))
|
||||
|
||||
if kwargs.get('default_pagesize'):
|
||||
if 'default_pagesize' in kwargs:
|
||||
warnings.warn("default_pagesize param is deprecated for Grid(); "
|
||||
"please use pagesize param instead",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
kwargs.setdefault('pagesize', kwargs.pop('default_pagesize'))
|
||||
|
||||
if kwargs.get('default_page'):
|
||||
if 'default_page' in kwargs:
|
||||
warnings.warn("default_page param is deprecated for Grid(); "
|
||||
"please use page param instead",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
kwargs.setdefault('page', kwargs.pop('default_page'))
|
||||
|
||||
if 'searchable' in kwargs:
|
||||
warnings.warn("searchable param is deprecated for Grid(); "
|
||||
"please use searchable_columns param instead",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
kwargs.setdefault('searchable_columns', kwargs.pop('searchable'))
|
||||
|
||||
# TODO: this should not be needed once all templates correctly
|
||||
# reference grid.vue_component etc.
|
||||
kwargs.setdefault('vue_tagname', 'tailbone-grid')
|
||||
|
||||
# nb. these must be set before super init, as they are
|
||||
# referenced when constructing filters
|
||||
self.assume_local_times = assume_local_times
|
||||
self.use_byte_string_filters = use_byte_string_filters
|
||||
|
||||
kwargs['key'] = key
|
||||
kwargs['data'] = data
|
||||
super().__init__(request, **kwargs)
|
||||
|
@ -275,19 +283,11 @@ class Grid(WuttaGrid):
|
|||
|
||||
self.width = width
|
||||
self.enums = enums or {}
|
||||
self.assume_local_times = assume_local_times
|
||||
self.renderers = self.make_default_renderers(self.renderers)
|
||||
self.raw_renderers = raw_renderers or {}
|
||||
self.invisible = invisible or []
|
||||
self.extra_row_class = extra_row_class
|
||||
self.url = url
|
||||
self.joiners = joiners or {}
|
||||
|
||||
self.filterable = filterable
|
||||
self.use_byte_string_filters = use_byte_string_filters
|
||||
self.filters = self.make_filters(filters)
|
||||
|
||||
self.searchable = searchable or {}
|
||||
|
||||
self.checkboxes = checkboxes
|
||||
self.checked = checked
|
||||
|
@ -443,10 +443,14 @@ class Grid(WuttaGrid):
|
|||
self.remove(oldfield)
|
||||
|
||||
def set_joiner(self, key, joiner):
|
||||
""" """
|
||||
if joiner is None:
|
||||
self.joiners.pop(key, None)
|
||||
warnings.warn("specifying None is deprecated for Grid.set_joiner(); "
|
||||
"please use Grid.remove_joiner() instead",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
self.remove_joiner(key)
|
||||
else:
|
||||
self.joiners[key] = joiner
|
||||
super().set_joiner(key, joiner)
|
||||
|
||||
def set_sorter(self, key, *args, **kwargs):
|
||||
""" """
|
||||
|
@ -474,41 +478,19 @@ class Grid(WuttaGrid):
|
|||
self.sorters[key] = self.make_sorter(*args, **kwargs)
|
||||
|
||||
def set_filter(self, key, *args, **kwargs):
|
||||
if len(args) == 1 and args[0] is None:
|
||||
self.remove_filter(key)
|
||||
else:
|
||||
if 'label' not in kwargs and key in self.labels:
|
||||
kwargs['label'] = self.labels[key]
|
||||
self.filters[key] = self.make_filter(key, *args, **kwargs)
|
||||
""" """
|
||||
if len(args) == 1:
|
||||
if args[0] is None:
|
||||
warnings.warn("specifying None is deprecated for Grid.set_filter(); "
|
||||
"please use Grid.remove_filter() instead",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
self.remove_filter(key)
|
||||
return
|
||||
|
||||
def set_searchable(self, key, searchable=True):
|
||||
if searchable:
|
||||
self.searchable[key] = True
|
||||
else:
|
||||
self.searchable.pop(key, None)
|
||||
|
||||
def is_searchable(self, key):
|
||||
return self.searchable.get(key, False)
|
||||
|
||||
def remove_filter(self, key):
|
||||
self.filters.pop(key, None)
|
||||
|
||||
def set_label(self, key, label, column_only=False):
|
||||
"""
|
||||
Set/override the label for a column.
|
||||
|
||||
This overrides
|
||||
:meth:`~wuttaweb:wuttaweb.grids.base.Grid.set_label()` to add
|
||||
the following params:
|
||||
|
||||
:param column_only: Boolean indicating whether the label
|
||||
should be applied *only* to the column header (if
|
||||
``True``), vs. applying also to the filter (if ``False``).
|
||||
"""
|
||||
super().set_label(key, label)
|
||||
|
||||
if not column_only and key in self.filters:
|
||||
self.filters[key].label = label
|
||||
# TODO: our make_filter() signature differs from upstream,
|
||||
# so must call it explicitly instead of delegating to super
|
||||
kwargs.setdefault('label', self.get_label(key))
|
||||
self.filters[key] = self.make_filter(key, *args, **kwargs)
|
||||
|
||||
def set_click_handler(self, key, handler):
|
||||
if handler:
|
||||
|
@ -593,7 +575,11 @@ class Grid(WuttaGrid):
|
|||
return getattr(obj, column_name)
|
||||
except AttributeError:
|
||||
pass
|
||||
return obj[column_name]
|
||||
|
||||
try:
|
||||
return obj[column_name]
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
def render_currency(self, obj, column_name):
|
||||
value = self.obtain_value(obj, column_name)
|
||||
|
@ -708,6 +694,14 @@ class Grid(WuttaGrid):
|
|||
def actions_column_format(self, column_number, row_number, item):
|
||||
return HTML.td(self.render_actions(item, row_number), class_='actions')
|
||||
|
||||
# TODO: upstream should handle this..
|
||||
def make_backend_filters(self, filters=None):
|
||||
""" """
|
||||
final = self.get_default_filters()
|
||||
if filters:
|
||||
final.update(filters)
|
||||
return final
|
||||
|
||||
def get_default_filters(self):
|
||||
"""
|
||||
Returns the default set of filters provided by the grid.
|
||||
|
@ -732,16 +726,6 @@ class Grid(WuttaGrid):
|
|||
filters[prop.key] = self.make_filter(prop.key, column)
|
||||
return filters
|
||||
|
||||
def make_filters(self, filters=None):
|
||||
"""
|
||||
Returns an initial set of filters which will be available to the grid.
|
||||
The grid itself may or may not provide some default filters, and the
|
||||
``filters`` kwarg may contain additions and/or overrides.
|
||||
"""
|
||||
if filters:
|
||||
return filters
|
||||
return self.get_default_filters()
|
||||
|
||||
def make_filter(self, key, column, **kwargs):
|
||||
"""
|
||||
Make a filter suitable for use with the given column.
|
||||
|
@ -879,9 +863,13 @@ class Grid(WuttaGrid):
|
|||
settings['page'] = self.page
|
||||
if self.filterable:
|
||||
for filtr in self.iter_filters():
|
||||
settings['filter.{}.active'.format(filtr.key)] = filtr.default_active
|
||||
settings['filter.{}.verb'.format(filtr.key)] = filtr.default_verb
|
||||
settings['filter.{}.value'.format(filtr.key)] = filtr.default_value
|
||||
defaults = self.filter_defaults.get(filtr.key, {})
|
||||
settings[f'filter.{filtr.key}.active'] = defaults.get('active',
|
||||
filtr.default_active)
|
||||
settings[f'filter.{filtr.key}.verb'] = defaults.get('verb',
|
||||
filtr.default_verb)
|
||||
settings[f'filter.{filtr.key}.value'] = defaults.get('value',
|
||||
filtr.default_value)
|
||||
|
||||
# If user has default settings on file, apply those first.
|
||||
if self.user_has_defaults():
|
||||
|
@ -889,13 +877,13 @@ class Grid(WuttaGrid):
|
|||
|
||||
# If request contains instruction to reset to default filters, then we
|
||||
# can skip the rest of the request/session checks.
|
||||
if self.request.GET.get('reset-to-default-filters') == 'true':
|
||||
if self.request.GET.get('reset-view'):
|
||||
pass
|
||||
|
||||
# If request has filter settings, grab those, then grab sort/pager
|
||||
# settings from request or session.
|
||||
elif self.filterable and self.request_has_settings('filter'):
|
||||
self.update_filter_settings(settings, 'request')
|
||||
elif self.request_has_settings('filter'):
|
||||
self.update_filter_settings(settings, src='request')
|
||||
if self.request_has_settings('sort'):
|
||||
self.update_sort_settings(settings, src='request')
|
||||
else:
|
||||
|
@ -907,7 +895,7 @@ class Grid(WuttaGrid):
|
|||
# settings from request or session.
|
||||
elif self.request_has_settings('sort'):
|
||||
self.update_sort_settings(settings, src='request')
|
||||
self.update_filter_settings(settings, 'session')
|
||||
self.update_filter_settings(settings, src='session')
|
||||
self.update_page_settings(settings)
|
||||
|
||||
# NOTE: These next two are functionally equivalent, but are kept
|
||||
|
@ -917,12 +905,12 @@ class Grid(WuttaGrid):
|
|||
# grab those, then grab filter/sort settings from session.
|
||||
elif self.request_has_settings('page'):
|
||||
self.update_page_settings(settings)
|
||||
self.update_filter_settings(settings, 'session')
|
||||
self.update_filter_settings(settings, src='session')
|
||||
self.update_sort_settings(settings, src='session')
|
||||
|
||||
# If request has no settings, grab all from session.
|
||||
elif self.session_has_settings():
|
||||
self.update_filter_settings(settings, 'session')
|
||||
self.update_filter_settings(settings, src='session')
|
||||
self.update_sort_settings(settings, src='session')
|
||||
self.update_page_settings(settings)
|
||||
|
||||
|
@ -1062,18 +1050,11 @@ class Grid(WuttaGrid):
|
|||
merge('page', int)
|
||||
|
||||
def request_has_settings(self, type_):
|
||||
"""
|
||||
Determine if the current request (GET query string) contains any
|
||||
filter/sort settings for the grid.
|
||||
"""
|
||||
if type_ == 'filter':
|
||||
for filtr in self.iter_filters():
|
||||
if filtr.key in self.request.GET:
|
||||
return True
|
||||
if 'filter' in self.request.GET: # user may be applying empty filters
|
||||
return True
|
||||
""" """
|
||||
if super().request_has_settings(type_):
|
||||
return True
|
||||
|
||||
elif type_ == 'sort':
|
||||
if type_ == 'sort':
|
||||
|
||||
# TODO: remove this eventually, but some links in the wild
|
||||
# may still include these params, so leave it for now
|
||||
|
@ -1081,14 +1062,6 @@ class Grid(WuttaGrid):
|
|||
if key in self.request.GET:
|
||||
return True
|
||||
|
||||
if 'sort1key' in self.request.GET:
|
||||
return True
|
||||
|
||||
elif type_ == 'page':
|
||||
for key in ['pagesize', 'page']:
|
||||
if key in self.request.GET:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def session_has_settings(self):
|
||||
|
@ -1104,72 +1077,6 @@ class Grid(WuttaGrid):
|
|||
return any([key.startswith(f'{prefix}.filter')
|
||||
for key in self.request.session])
|
||||
|
||||
def update_filter_settings(self, settings, source):
|
||||
"""
|
||||
Updates a settings dictionary according to filter settings data found
|
||||
in either the GET query string, or session storage.
|
||||
|
||||
:param settings: Dictionary of initial settings, which is to be updated.
|
||||
|
||||
:param source: String identifying the source to consult for settings
|
||||
data. Must be one of: ``('request', 'session')``.
|
||||
"""
|
||||
if not self.filterable:
|
||||
return
|
||||
|
||||
for filtr in self.iter_filters():
|
||||
prefix = 'filter.{}'.format(filtr.key)
|
||||
|
||||
if source == 'request':
|
||||
# consider filter active if query string contains a value for it
|
||||
settings['{}.active'.format(prefix)] = filtr.key in self.request.GET
|
||||
settings['{}.verb'.format(prefix)] = self.get_setting(
|
||||
settings, f'{filtr.key}.verb', src='request', default='')
|
||||
settings['{}.value'.format(prefix)] = self.get_setting(
|
||||
settings, filtr.key, src='request', default='')
|
||||
|
||||
else: # source = session
|
||||
settings['{}.active'.format(prefix)] = self.get_setting(
|
||||
settings, f'{prefix}.active', src='session',
|
||||
normalize=lambda v: str(v).lower() == 'true', default=False)
|
||||
settings['{}.verb'.format(prefix)] = self.get_setting(
|
||||
settings, f'{prefix}.verb', src='session', default='')
|
||||
settings['{}.value'.format(prefix)] = self.get_setting(
|
||||
settings, f'{prefix}.value', src='session', default='')
|
||||
|
||||
def update_page_settings(self, settings):
|
||||
"""
|
||||
Updates a settings dictionary according to pager settings data found in
|
||||
either the GET query string, or session storage.
|
||||
|
||||
Note that due to how the actual pager functions, the effective settings
|
||||
will often come from *both* the request and session. This is so that
|
||||
e.g. the page size will remain constant (coming from the session) while
|
||||
the user jumps between pages (which only provides the single setting).
|
||||
|
||||
:param settings: Dictionary of initial settings, which is to be updated.
|
||||
"""
|
||||
if not self.paginated:
|
||||
return
|
||||
|
||||
pagesize = self.request.GET.get('pagesize')
|
||||
if pagesize is not None:
|
||||
if pagesize.isdigit():
|
||||
settings['pagesize'] = int(pagesize)
|
||||
else:
|
||||
pagesize = self.request.session.get('grid.{}.pagesize'.format(self.key))
|
||||
if pagesize is not None:
|
||||
settings['pagesize'] = pagesize
|
||||
|
||||
page = self.request.GET.get('page')
|
||||
if page is not None:
|
||||
if page.isdigit():
|
||||
settings['page'] = int(page)
|
||||
else:
|
||||
page = self.request.session.get('grid.{}.page'.format(self.key))
|
||||
if page is not None:
|
||||
settings['page'] = int(page)
|
||||
|
||||
def persist_settings(self, settings, dest='session'):
|
||||
""" """
|
||||
if dest not in ('defaults', 'session'):
|
||||
|
@ -1257,89 +1164,12 @@ class Grid(WuttaGrid):
|
|||
|
||||
return data
|
||||
|
||||
def sort_data(self, data, sorters=None):
|
||||
""" """
|
||||
if sorters is None:
|
||||
sorters = self.active_sorters
|
||||
if not sorters:
|
||||
return data
|
||||
|
||||
# nb. when data is a query, we want to apply sorters in the
|
||||
# requested order, so the final query has order_by() in the
|
||||
# correct "as-is" sequence. however when data is a list we
|
||||
# must do the opposite, applying in the reverse order, so the
|
||||
# final list has the most "important" sort(s) applied last.
|
||||
if not isinstance(data, orm.Query):
|
||||
sorters = reversed(sorters)
|
||||
|
||||
for sorter in sorters:
|
||||
sortkey = sorter['key']
|
||||
sortdir = sorter['dir']
|
||||
|
||||
# cannot sort unless we have a sorter callable
|
||||
sortfunc = self.sorters.get(sortkey)
|
||||
if not sortfunc:
|
||||
return data
|
||||
|
||||
# join appropriate model if needed
|
||||
if sortkey in self.joiners and sortkey not in self.joined:
|
||||
data = self.joiners[sortkey](data)
|
||||
self.joined.add(sortkey)
|
||||
|
||||
# invoke the sorter
|
||||
data = sortfunc(data, sortdir)
|
||||
|
||||
return data
|
||||
|
||||
def paginate_data(self, data):
|
||||
"""
|
||||
Paginate the given data set according to current settings, and return
|
||||
the result.
|
||||
"""
|
||||
# we of course assume our current page is correct, at first
|
||||
pager = self.make_pager(data)
|
||||
|
||||
# if pager has detected that our current page is outside the valid
|
||||
# range, we must re-orient ourself around the "new" (valid) page
|
||||
if pager.page != self.page:
|
||||
self.page = pager.page
|
||||
self.request.session['grid.{}.page'.format(self.key)] = self.page
|
||||
pager = self.make_pager(data)
|
||||
|
||||
return pager
|
||||
|
||||
def make_pager(self, data):
|
||||
|
||||
# TODO: this seems hacky..normally we expect `data` to be a
|
||||
# query of course, but in some cases it may be a list instead.
|
||||
# if so then we can't use ORM pager
|
||||
if isinstance(data, list):
|
||||
import paginate
|
||||
return paginate.Page(data,
|
||||
items_per_page=self.pagesize,
|
||||
page=self.page)
|
||||
|
||||
return SqlalchemyOrmPage(data,
|
||||
items_per_page=self.pagesize,
|
||||
page=self.page,
|
||||
url_maker=URLMaker(self.request))
|
||||
|
||||
def make_visible_data(self):
|
||||
"""
|
||||
Apply various settings to the raw data set, to produce a final data
|
||||
set. This will page / sort / filter as necessary, according to the
|
||||
grid's defaults and the current request etc.
|
||||
"""
|
||||
self.joined = set()
|
||||
data = self.data
|
||||
if self.filterable:
|
||||
data = self.filter_data(data)
|
||||
if self.sortable:
|
||||
data = self.sort_data(data)
|
||||
if self.paginated:
|
||||
self.pager = self.paginate_data(data)
|
||||
data = self.pager
|
||||
return data
|
||||
""" """
|
||||
warnings.warn("grid.make_visible_data() method is deprecated; "
|
||||
"please use grid.get_visible_data() instead",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
return self.get_visible_data()
|
||||
|
||||
def render_vue_tag(self, master=None, **kwargs):
|
||||
""" """
|
||||
|
@ -1362,7 +1192,7 @@ class Grid(WuttaGrid):
|
|||
includes the context menu items and grid tools.
|
||||
"""
|
||||
if 'grid_columns' not in kwargs:
|
||||
kwargs['grid_columns'] = self.get_table_columns()
|
||||
kwargs['grid_columns'] = self.get_vue_columns()
|
||||
|
||||
if 'grid_data' not in kwargs:
|
||||
kwargs['grid_data'] = self.get_table_data()
|
||||
|
@ -1385,6 +1215,7 @@ class Grid(WuttaGrid):
|
|||
return HTML.literal(html)
|
||||
|
||||
def render_buefy(self, **kwargs):
|
||||
""" """
|
||||
warnings.warn("Grid.render_buefy() is deprecated; "
|
||||
"please use Grid.render_complete() instead",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
|
@ -1392,6 +1223,7 @@ class Grid(WuttaGrid):
|
|||
|
||||
def render_table_element(self, template='/grids/b-table.mako',
|
||||
data_prop='gridData', empty_labels=False,
|
||||
literal=False,
|
||||
**kwargs):
|
||||
"""
|
||||
This is intended for ad-hoc "small" grids with static data. Renders
|
||||
|
@ -1403,12 +1235,15 @@ class Grid(WuttaGrid):
|
|||
context['data_prop'] = data_prop
|
||||
context['empty_labels'] = empty_labels
|
||||
if 'grid_columns' not in context:
|
||||
context['grid_columns'] = self.get_table_columns()
|
||||
context['grid_columns'] = self.get_vue_columns()
|
||||
context.setdefault('paginated', False)
|
||||
if context['paginated']:
|
||||
context.setdefault('per_page', 20)
|
||||
context['view_click_handler'] = self.get_view_click_handler()
|
||||
return render(template, context)
|
||||
result = render(template, context)
|
||||
if literal:
|
||||
result = HTML.literal(result)
|
||||
return result
|
||||
|
||||
def get_view_click_handler(self):
|
||||
""" """
|
||||
|
@ -1417,7 +1252,7 @@ class Grid(WuttaGrid):
|
|||
view = None
|
||||
for action in self.actions:
|
||||
if action.key == 'view':
|
||||
return action.click_handler
|
||||
return getattr(action, 'click_handler', None)
|
||||
|
||||
def set_filters_sequence(self, filters, only=False):
|
||||
"""
|
||||
|
@ -1491,28 +1326,6 @@ class Grid(WuttaGrid):
|
|||
|
||||
return data
|
||||
|
||||
def render_filters(self, template='/grids/filters.mako', **kwargs):
|
||||
"""
|
||||
Render the filters to a Unicode string, using the specified template.
|
||||
Additional kwargs are passed along as context to the template.
|
||||
"""
|
||||
# Provide default data to filters form, so renderer can do some of the
|
||||
# work for us.
|
||||
data = {}
|
||||
for filtr in self.iter_active_filters():
|
||||
data['{}.active'.format(filtr.key)] = filtr.active
|
||||
data['{}.verb'.format(filtr.key)] = filtr.verb
|
||||
data[filtr.key] = filtr.value
|
||||
|
||||
form = gridfilters.GridFiltersForm(self.filters,
|
||||
request=self.request,
|
||||
defaults=data)
|
||||
|
||||
kwargs['request'] = self.request
|
||||
kwargs['grid'] = self
|
||||
kwargs['form'] = form
|
||||
return render(template, kwargs)
|
||||
|
||||
def render_actions(self, row, i): # pragma: no cover
|
||||
""" """
|
||||
warnings.warn("grid.render_actions() is deprecated!",
|
||||
|
@ -1574,22 +1387,19 @@ class Grid(WuttaGrid):
|
|||
|
||||
def get_vue_columns(self):
|
||||
""" """
|
||||
return self.get_table_columns()
|
||||
columns = super().get_vue_columns()
|
||||
|
||||
for column in columns:
|
||||
column['visible'] = column['field'] not in self.invisible
|
||||
|
||||
return columns
|
||||
|
||||
def get_table_columns(self):
|
||||
"""
|
||||
Return a list of dicts representing all grid columns. Meant
|
||||
for use with the client-side JS table.
|
||||
"""
|
||||
columns = []
|
||||
for name in self.columns:
|
||||
columns.append({
|
||||
'field': name,
|
||||
'label': self.get_label(name),
|
||||
'sortable': self.is_sortable(name),
|
||||
'visible': name not in self.invisible,
|
||||
})
|
||||
return columns
|
||||
""" """
|
||||
warnings.warn("grid.get_table_columns() method is deprecated; "
|
||||
"please use grid.get_vue_columns() instead",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
return self.get_vue_columns()
|
||||
|
||||
def get_uuid_for_row(self, rowobj):
|
||||
|
||||
|
@ -1601,6 +1411,10 @@ class Grid(WuttaGrid):
|
|||
if hasattr(rowobj, 'uuid'):
|
||||
return rowobj.uuid
|
||||
|
||||
def get_vue_context(self):
|
||||
""" """
|
||||
return self.get_table_data()
|
||||
|
||||
def get_vue_data(self):
|
||||
""" """
|
||||
table_data = self.get_table_data()
|
||||
|
@ -1615,7 +1429,7 @@ class Grid(WuttaGrid):
|
|||
return self._table_data
|
||||
|
||||
# filter / sort / paginate to get "visible" data
|
||||
raw_data = self.make_visible_data()
|
||||
raw_data = self.get_visible_data()
|
||||
data = []
|
||||
status_map = {}
|
||||
checked = []
|
||||
|
@ -1656,10 +1470,22 @@ class Grid(WuttaGrid):
|
|||
|
||||
# leverage configured rendering logic where applicable;
|
||||
# otherwise use "raw" data value as string
|
||||
value = self.obtain_value(rowobj, name)
|
||||
if self.renderers and name in self.renderers:
|
||||
value = self.renderers[name](rowobj, name)
|
||||
else:
|
||||
value = self.obtain_value(rowobj, name)
|
||||
renderer = self.renderers[name]
|
||||
|
||||
# TODO: legacy renderer callables require 2 args,
|
||||
# but wuttaweb callables require 3 args
|
||||
sig = inspect.signature(renderer)
|
||||
required = [param for param in sig.parameters.values()
|
||||
if param.default == param.empty]
|
||||
|
||||
if len(required) == 2:
|
||||
# TODO: legacy renderer
|
||||
value = renderer(rowobj, name)
|
||||
else: # the future
|
||||
value = renderer(rowobj, name, value)
|
||||
|
||||
if value is None:
|
||||
value = ""
|
||||
|
||||
|
@ -1692,6 +1518,8 @@ class Grid(WuttaGrid):
|
|||
|
||||
results = {
|
||||
'data': data,
|
||||
'row_classes': status_map,
|
||||
# TODO: deprecate / remove this
|
||||
'row_status_map': status_map,
|
||||
}
|
||||
|
||||
|
@ -1703,6 +1531,10 @@ class Grid(WuttaGrid):
|
|||
results['checked_rows_code'] = '[{}]'.format(
|
||||
', '.join(['{}[{}]'.format(var, i) for i in checked]))
|
||||
|
||||
if self.paginated and self.paginate_on_backend:
|
||||
results['pager_stats'] = self.get_vue_pager_stats()
|
||||
|
||||
# TODO: is this actually needed now that we have pager_stats?
|
||||
if self.paginated and self.pager is not None:
|
||||
results['total_items'] = self.pager.item_count
|
||||
results['per_page'] = self.pager.items_per_page
|
||||
|
@ -1716,6 +1548,11 @@ class Grid(WuttaGrid):
|
|||
self._table_data = results
|
||||
return self._table_data
|
||||
|
||||
# TODO: remove this when we use upstream GridAction
|
||||
def add_action(self, key, **kwargs):
|
||||
""" """
|
||||
self.actions.append(GridAction(self.request, key, **kwargs))
|
||||
|
||||
def set_action_urls(self, row, rowobj, i):
|
||||
"""
|
||||
Pre-generate all action URLs for the given data row. Meant for use
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2023 Lance Edgar
|
||||
# Copyright © 2010-2024 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
|
@ -24,6 +24,9 @@
|
|||
Template Context Helpers
|
||||
"""
|
||||
|
||||
# start off with all from wuttaweb
|
||||
from wuttaweb.helpers import *
|
||||
|
||||
import os
|
||||
import datetime
|
||||
from decimal import Decimal
|
||||
|
@ -33,12 +36,7 @@ from rattail.time import localtime, make_utc
|
|||
from rattail.util import pretty_quantity, pretty_hours, hours_as_decimal
|
||||
from rattail.db.util import maxlen
|
||||
|
||||
from webhelpers2.html import *
|
||||
from webhelpers2.html.tags import *
|
||||
|
||||
from wuttaweb.util import get_liburl
|
||||
from tailbone.util import (csrf_token, get_csrf_token,
|
||||
pretty_datetime, raw_datetime,
|
||||
from tailbone.util import (pretty_datetime, raw_datetime,
|
||||
render_markdown,
|
||||
route_exists)
|
||||
|
||||
|
|
|
@ -394,6 +394,11 @@ class TailboneMenuHandler(WuttaMenuHandler):
|
|||
'route': 'products',
|
||||
'perm': 'products.list',
|
||||
},
|
||||
{
|
||||
'title': "Product Costs",
|
||||
'route': 'product_costs',
|
||||
'perm': 'product_costs.list',
|
||||
},
|
||||
{
|
||||
'title': "Departments",
|
||||
'route': 'departments',
|
||||
|
@ -451,6 +456,11 @@ class TailboneMenuHandler(WuttaMenuHandler):
|
|||
'route': 'vendors',
|
||||
'perm': 'vendors.list',
|
||||
},
|
||||
{
|
||||
'title': "Product Costs",
|
||||
'route': 'product_costs',
|
||||
'perm': 'product_costs.list',
|
||||
},
|
||||
{'type': 'sep'},
|
||||
{
|
||||
'title': "Ordering",
|
||||
|
@ -703,7 +713,7 @@ class TailboneMenuHandler(WuttaMenuHandler):
|
|||
},
|
||||
{'type': 'sep'},
|
||||
{
|
||||
'title': "App Details",
|
||||
'title': "App Info",
|
||||
'route': 'appinfo',
|
||||
'perm': 'appinfo.list',
|
||||
},
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2017 Lance Edgar
|
||||
# Copyright © 2010-2024 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
|
@ -24,9 +24,8 @@
|
|||
Static Assets
|
||||
"""
|
||||
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
|
||||
def includeme(config):
|
||||
config.include('wuttaweb.static')
|
||||
config.add_static_view('tailbone', 'tailbone:static')
|
||||
config.add_static_view('deform', 'deform:static')
|
||||
|
|
|
@ -1,250 +1,2 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="/configure.mako" />
|
||||
|
||||
<%def name="form_content()">
|
||||
|
||||
<h3 class="block is-size-3">Basics</h3>
|
||||
<div class="block" style="padding-left: 2rem;">
|
||||
|
||||
<b-field grouped>
|
||||
|
||||
<b-field label="App Title">
|
||||
<b-input name="rattail.app_title"
|
||||
v-model="simpleSettings['rattail.app_title']"
|
||||
@input="settingsNeedSaved = true">
|
||||
</b-input>
|
||||
</b-field>
|
||||
|
||||
<b-field label="Node Type">
|
||||
## TODO: should be a dropdown, app handler defines choices
|
||||
<b-input name="rattail.node_type"
|
||||
v-model="simpleSettings['rattail.node_type']"
|
||||
@input="settingsNeedSaved = true">
|
||||
</b-input>
|
||||
</b-field>
|
||||
|
||||
<b-field label="Node Title">
|
||||
<b-input name="rattail.node_title"
|
||||
v-model="simpleSettings['rattail.node_title']"
|
||||
@input="settingsNeedSaved = true">
|
||||
</b-input>
|
||||
</b-field>
|
||||
|
||||
</b-field>
|
||||
|
||||
<b-field>
|
||||
<b-checkbox name="rattail.production"
|
||||
v-model="simpleSettings['rattail.production']"
|
||||
native-value="true"
|
||||
@input="settingsNeedSaved = true">
|
||||
Production Mode
|
||||
</b-checkbox>
|
||||
</b-field>
|
||||
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<b-field>
|
||||
<b-checkbox name="rattail.running_from_source"
|
||||
v-model="simpleSettings['rattail.running_from_source']"
|
||||
native-value="true"
|
||||
@input="settingsNeedSaved = true">
|
||||
Running from Source
|
||||
</b-checkbox>
|
||||
</b-field>
|
||||
</div>
|
||||
<div class="level-item">
|
||||
<b-field label="Top-Level Package" horizontal
|
||||
v-if="simpleSettings['rattail.running_from_source']">
|
||||
<b-input name="rattail.running_from_source.rootpkg"
|
||||
v-model="simpleSettings['rattail.running_from_source.rootpkg']"
|
||||
@input="settingsNeedSaved = true">
|
||||
</b-input>
|
||||
</b-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<h3 class="block is-size-3">Display</h3>
|
||||
<div class="block" style="padding-left: 2rem;">
|
||||
|
||||
<b-field grouped>
|
||||
|
||||
<b-field label="Background Color">
|
||||
<b-input name="tailbone.background_color"
|
||||
v-model="simpleSettings['tailbone.background_color']"
|
||||
@input="settingsNeedSaved = true">
|
||||
</b-input>
|
||||
</b-field>
|
||||
|
||||
</b-field>
|
||||
|
||||
</div>
|
||||
|
||||
<h3 class="block is-size-3">Grids</h3>
|
||||
<div class="block" style="padding-left: 2rem;">
|
||||
|
||||
<b-field grouped>
|
||||
|
||||
<b-field label="Default Page Size">
|
||||
<b-input name="tailbone.grid.default_pagesize"
|
||||
v-model="simpleSettings['tailbone.grid.default_pagesize']"
|
||||
@input="settingsNeedSaved = true">
|
||||
</b-input>
|
||||
</b-field>
|
||||
|
||||
</b-field>
|
||||
|
||||
</div>
|
||||
|
||||
<h3 class="block is-size-3">Web Libraries</h3>
|
||||
<div class="block" style="padding-left: 2rem;">
|
||||
|
||||
<${b}-table :data="weblibs">
|
||||
|
||||
<${b}-table-column field="title"
|
||||
label="Name"
|
||||
v-slot="props">
|
||||
{{ props.row.title }}
|
||||
</${b}-table-column>
|
||||
|
||||
<${b}-table-column field="configured_version"
|
||||
label="Version"
|
||||
v-slot="props">
|
||||
{{ props.row.configured_version || props.row.default_version }}
|
||||
</${b}-table-column>
|
||||
|
||||
<${b}-table-column field="configured_url"
|
||||
label="URL Override"
|
||||
v-slot="props">
|
||||
{{ props.row.configured_url }}
|
||||
</${b}-table-column>
|
||||
|
||||
<${b}-table-column field="live_url"
|
||||
label="Effective (Live) URL"
|
||||
v-slot="props">
|
||||
<span v-if="props.row.modified"
|
||||
class="has-text-warning">
|
||||
save settings and refresh page to see new URL
|
||||
</span>
|
||||
<span v-if="!props.row.modified">
|
||||
{{ props.row.live_url }}
|
||||
</span>
|
||||
</${b}-table-column>
|
||||
|
||||
<${b}-table-column field="actions"
|
||||
label="Actions"
|
||||
v-slot="props">
|
||||
<a href="#"
|
||||
@click.prevent="editWebLibraryInit(props.row)">
|
||||
% if request.use_oruga:
|
||||
<o-icon icon="edit" />
|
||||
% else:
|
||||
<i class="fas fa-edit"></i>
|
||||
% endif
|
||||
Edit
|
||||
</a>
|
||||
</${b}-table-column>
|
||||
|
||||
</${b}-table>
|
||||
|
||||
% for weblib in weblibs:
|
||||
${h.hidden('wuttaweb.libver.{}'.format(weblib['key']), **{':value': "simpleSettings['wuttaweb.libver.{}']".format(weblib['key'])})}
|
||||
${h.hidden('wuttaweb.liburl.{}'.format(weblib['key']), **{':value': "simpleSettings['wuttaweb.liburl.{}']".format(weblib['key'])})}
|
||||
% endfor
|
||||
|
||||
<${b}-modal has-modal-card
|
||||
% if request.use_oruga:
|
||||
v-model:active="editWebLibraryShowDialog"
|
||||
% else:
|
||||
:active.sync="editWebLibraryShowDialog"
|
||||
% endif
|
||||
>
|
||||
<div class="modal-card">
|
||||
|
||||
<header class="modal-card-head">
|
||||
<p class="modal-card-title">Web Library: {{ editWebLibraryRecord.title }}</p>
|
||||
</header>
|
||||
|
||||
<section class="modal-card-body">
|
||||
|
||||
<b-field grouped>
|
||||
|
||||
<b-field label="Default Version">
|
||||
<b-input v-model="editWebLibraryRecord.default_version"
|
||||
disabled>
|
||||
</b-input>
|
||||
</b-field>
|
||||
|
||||
<b-field label="Override Version">
|
||||
<b-input v-model="editWebLibraryVersion">
|
||||
</b-input>
|
||||
</b-field>
|
||||
|
||||
</b-field>
|
||||
|
||||
<b-field label="Override URL">
|
||||
<b-input v-model="editWebLibraryURL"
|
||||
expanded />
|
||||
</b-field>
|
||||
|
||||
<b-field label="Effective URL (as of last page load)">
|
||||
<b-input v-model="editWebLibraryRecord.live_url"
|
||||
disabled
|
||||
expanded />
|
||||
</b-field>
|
||||
|
||||
</section>
|
||||
|
||||
<footer class="modal-card-foot">
|
||||
<b-button type="is-primary"
|
||||
@click="editWebLibrarySave()"
|
||||
icon-pack="fas"
|
||||
icon-left="save">
|
||||
Save
|
||||
</b-button>
|
||||
<b-button @click="editWebLibraryShowDialog = false">
|
||||
Cancel
|
||||
</b-button>
|
||||
</footer>
|
||||
</div>
|
||||
</${b}-modal>
|
||||
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
ThisPageData.weblibs = ${json.dumps(weblibs)|n}
|
||||
|
||||
ThisPageData.editWebLibraryShowDialog = false
|
||||
ThisPageData.editWebLibraryRecord = {}
|
||||
ThisPageData.editWebLibraryVersion = null
|
||||
ThisPageData.editWebLibraryURL = null
|
||||
|
||||
ThisPage.methods.editWebLibraryInit = function(row) {
|
||||
this.editWebLibraryRecord = row
|
||||
this.editWebLibraryVersion = row.configured_version
|
||||
this.editWebLibraryURL = row.configured_url
|
||||
this.editWebLibraryShowDialog = true
|
||||
}
|
||||
|
||||
ThisPage.methods.editWebLibrarySave = function() {
|
||||
this.editWebLibraryRecord.configured_version = this.editWebLibraryVersion
|
||||
this.editWebLibraryRecord.configured_url = this.editWebLibraryURL
|
||||
this.editWebLibraryRecord.modified = true
|
||||
|
||||
this.simpleSettings[`wuttaweb.libver.${'$'}{this.editWebLibraryRecord.key}`] = this.editWebLibraryVersion
|
||||
this.simpleSettings[`wuttaweb.liburl.${'$'}{this.editWebLibraryRecord.key}`] = this.editWebLibraryURL
|
||||
|
||||
this.settingsNeedSaved = true
|
||||
this.editWebLibraryShowDialog = false
|
||||
}
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
<%inherit file="wuttaweb:templates/appinfo/configure.mako" />
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="/master/index.mako" />
|
||||
|
||||
<%def name="render_grid_component()">
|
||||
<%inherit file="wuttaweb:templates/appinfo/index.mako" />
|
||||
|
||||
<%def name="page_content()">
|
||||
<div class="buttons">
|
||||
|
||||
<once-button type="is-primary"
|
||||
|
@ -28,100 +27,5 @@
|
|||
|
||||
</div>
|
||||
|
||||
<${b}-collapse class="panel" open>
|
||||
|
||||
<template #trigger="props">
|
||||
<div class="panel-heading"
|
||||
style="cursor: pointer;"
|
||||
role="button">
|
||||
|
||||
## TODO: for some reason buefy will "reuse" the icon
|
||||
## element in such a way that its display does not
|
||||
## refresh. so to work around that, we use different
|
||||
## structure for the two icons, so buefy is forced to
|
||||
## re-draw
|
||||
|
||||
<b-icon v-if="props.open"
|
||||
pack="fas"
|
||||
icon="angle-down">
|
||||
</b-icon>
|
||||
|
||||
<span v-if="!props.open">
|
||||
<b-icon pack="fas"
|
||||
icon="angle-right">
|
||||
</b-icon>
|
||||
</span>
|
||||
|
||||
<span>Configuration Files</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="panel-block">
|
||||
<div style="width: 100%;">
|
||||
<${b}-table :data="configFiles">
|
||||
|
||||
<${b}-table-column field="priority"
|
||||
label="Priority"
|
||||
v-slot="props">
|
||||
{{ props.row.priority }}
|
||||
</${b}-table-column>
|
||||
|
||||
<${b}-table-column field="path"
|
||||
label="File Path"
|
||||
v-slot="props">
|
||||
{{ props.row.path }}
|
||||
</${b}-table-column>
|
||||
|
||||
</${b}-table>
|
||||
</div>
|
||||
</div>
|
||||
</${b}-collapse>
|
||||
|
||||
<${b}-collapse class="panel"
|
||||
:open="false">
|
||||
|
||||
<template #trigger="props">
|
||||
<div class="panel-heading"
|
||||
style="cursor: pointer;"
|
||||
role="button">
|
||||
|
||||
## TODO: for some reason buefy will "reuse" the icon
|
||||
## element in such a way that its display does not
|
||||
## refresh. so to work around that, we use different
|
||||
## structure for the two icons, so buefy is forced to
|
||||
## re-draw
|
||||
|
||||
<b-icon v-if="props.open"
|
||||
pack="fas"
|
||||
icon="angle-down">
|
||||
</b-icon>
|
||||
|
||||
<span v-if="!props.open">
|
||||
<b-icon pack="fas"
|
||||
icon="angle-right">
|
||||
</b-icon>
|
||||
</span>
|
||||
|
||||
<strong>Installed Packages</strong>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="panel-block">
|
||||
<div style="width: 100%;">
|
||||
${parent.render_grid_component()}
|
||||
</div>
|
||||
</div>
|
||||
</${b}-collapse>
|
||||
${parent.page_content()}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
ThisPageData.configFiles = ${json.dumps([dict(path=p, priority=i) for i, p in enumerate(request.rattail_config.prioritized_files, 1)])|n}
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
<app-settings :groups="groups" :showing-group="showingGroup"></app-settings>
|
||||
</%def>
|
||||
|
||||
<%def name="render_this_page_template()">
|
||||
${parent.render_this_page_template()}
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
<script type="text/x-template" id="app-settings-template">
|
||||
|
||||
<div class="form">
|
||||
|
@ -150,19 +150,18 @@
|
|||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
ThisPageData.groups = ${json.dumps(settings_data)|n}
|
||||
ThisPageData.showingGroup = ${json.dumps(current_group or '')|n}
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_this_page_component()">
|
||||
${parent.make_this_page_component()}
|
||||
<script type="text/javascript">
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
<script>
|
||||
|
||||
Vue.component('app-settings', {
|
||||
template: '#app-settings-template',
|
||||
|
@ -193,6 +192,3 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%namespace file="/wutta-components.mako" import="make_wutta_components" />
|
||||
<%namespace file="/grids/nav.mako" import="grid_index_nav" />
|
||||
<%namespace file="/autocomplete.mako" import="tailbone_autocomplete_template" />
|
||||
<%namespace name="base_meta" file="/base_meta.mako" />
|
||||
|
@ -34,17 +35,21 @@
|
|||
</head>
|
||||
|
||||
<body>
|
||||
${declare_formposter_mixin()}
|
||||
|
||||
${self.body()}
|
||||
|
||||
<div id="whole-page-app">
|
||||
<div id="app" style="height: 100%;">
|
||||
<whole-page></whole-page>
|
||||
</div>
|
||||
|
||||
${self.render_whole_page_template()}
|
||||
${self.make_whole_page_component()}
|
||||
${self.make_whole_page_app()}
|
||||
## TODO: this must come before the self.body() call..but why?
|
||||
${declare_formposter_mixin()}
|
||||
|
||||
## content body from derived/child template
|
||||
${self.body()}
|
||||
|
||||
## Vue app
|
||||
${self.render_vue_templates()}
|
||||
${self.modify_vue_vars()}
|
||||
${self.make_vue_components()}
|
||||
${self.make_vue_app()}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
@ -181,7 +186,7 @@
|
|||
|
||||
<%def name="head_tags()"></%def>
|
||||
|
||||
<%def name="render_whole_page_template()">
|
||||
<%def name="render_vue_template_whole_page()">
|
||||
<script type="text/x-template" id="whole-page-template">
|
||||
<div>
|
||||
<header>
|
||||
|
@ -627,9 +632,23 @@
|
|||
% endif
|
||||
<div class="navbar-dropdown">
|
||||
% if request.is_root:
|
||||
${h.link_to("Stop being root", url('stop_root'), class_='navbar-item root-user')}
|
||||
${h.form(url('stop_root'), ref='stopBeingRootForm')}
|
||||
${h.csrf_token(request)}
|
||||
<input type="hidden" name="referrer" value="${request.current_route_url()}" />
|
||||
<a @click="$refs.stopBeingRootForm.submit()"
|
||||
class="navbar-item root-user">
|
||||
Stop being root
|
||||
</a>
|
||||
${h.end_form()}
|
||||
% elif request.is_admin:
|
||||
${h.link_to("Become root", url('become_root'), class_='navbar-item root-user')}
|
||||
${h.form(url('become_root'), ref='startBeingRootForm')}
|
||||
${h.csrf_token(request)}
|
||||
<input type="hidden" name="referrer" value="${request.current_route_url()}" />
|
||||
<a @click="$refs.startBeingRootForm.submit()"
|
||||
class="navbar-item root-user">
|
||||
Become root
|
||||
</a>
|
||||
${h.end_form()}
|
||||
% endif
|
||||
% if messaging_enabled:
|
||||
${h.link_to("Messages{}".format(" ({})".format(inbox_count) if inbox_count else ''), url('messages.inbox'), class_='navbar-item')}
|
||||
|
@ -637,7 +656,11 @@
|
|||
% if request.is_root or not request.user.prevent_password_change:
|
||||
${h.link_to("Change Password", url('change_password'), class_='navbar-item')}
|
||||
% endif
|
||||
${h.link_to("Edit Preferences", url('my.preferences'), class_='navbar-item')}
|
||||
% try:
|
||||
## nb. does not exist yet for wuttaweb
|
||||
${h.link_to("Edit Preferences", url('my.preferences'), class_='navbar-item')}
|
||||
% except:
|
||||
% endtry
|
||||
${h.link_to("Logout", url('logout'), class_='navbar-item')}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -663,7 +686,7 @@
|
|||
text="Edit This">
|
||||
</once-button>
|
||||
% endif
|
||||
% if getattr(master, 'cloneable', False) and master.has_perm('clone'):
|
||||
% if getattr(master, 'cloneable', False) and not master.cloning and master.has_perm('clone'):
|
||||
<once-button tag="a" href="${master.get_action_url('clone', instance)}"
|
||||
icon-left="object-ungroup"
|
||||
text="Clone This">
|
||||
|
@ -749,11 +772,8 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="declare_whole_page_vars()">
|
||||
${page_help.declare_vars()}
|
||||
${multi_file_upload.declare_vars()}
|
||||
${h.javascript_link(request.static_url('tailbone:static/js/tailbone.feedback.js') + '?ver={}'.format(tailbone.__version__))}
|
||||
<script type="text/javascript">
|
||||
<%def name="render_vue_script_whole_page()">
|
||||
<script>
|
||||
|
||||
let WholePage = {
|
||||
template: '#whole-page-template',
|
||||
|
@ -889,57 +909,6 @@
|
|||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_whole_page_vars()">
|
||||
<script type="text/javascript">
|
||||
|
||||
% if request.user:
|
||||
FeedbackFormData.userUUID = ${json.dumps(request.user.uuid)|n}
|
||||
FeedbackFormData.userName = ${json.dumps(str(request.user))|n}
|
||||
% endif
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="finalize_whole_page_vars()">
|
||||
## NOTE: if you override this, must use <script> tags
|
||||
</%def>
|
||||
|
||||
<%def name="make_whole_page_component()">
|
||||
|
||||
${make_grid_filter_components()}
|
||||
|
||||
${self.declare_whole_page_vars()}
|
||||
${self.modify_whole_page_vars()}
|
||||
${self.finalize_whole_page_vars()}
|
||||
|
||||
${h.javascript_link(request.static_url('tailbone:static/js/tailbone.buefy.autocomplete.js') + '?ver={}'.format(tailbone.__version__))}
|
||||
|
||||
${page_help.make_component()}
|
||||
${multi_file_upload.make_component()}
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
FeedbackForm.data = function() { return FeedbackFormData }
|
||||
|
||||
Vue.component('feedback-form', FeedbackForm)
|
||||
|
||||
WholePage.data = function() { return WholePageData }
|
||||
|
||||
Vue.component('whole-page', WholePage)
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_whole_page_app()">
|
||||
<script type="text/javascript">
|
||||
|
||||
new Vue({
|
||||
el: '#whole-page-app'
|
||||
})
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="wtfield(form, name, **kwargs)">
|
||||
<div class="field-wrapper${' error' if form[name].errors else ''}">
|
||||
<label for="${name}">${form[name].label}</label>
|
||||
|
@ -961,3 +930,88 @@
|
|||
</div>
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
##############################
|
||||
## vue components + app
|
||||
##############################
|
||||
|
||||
<%def name="render_vue_templates()">
|
||||
${page_help.declare_vars()}
|
||||
${multi_file_upload.declare_vars()}
|
||||
${h.javascript_link(request.static_url('tailbone:static/js/tailbone.feedback.js') + '?ver={}'.format(tailbone.__version__))}
|
||||
${h.javascript_link(request.static_url('tailbone:static/js/tailbone.buefy.autocomplete.js') + '?ver={}'.format(tailbone.__version__))}
|
||||
|
||||
## DEPRECATED; called for back-compat
|
||||
${self.render_whole_page_template()}
|
||||
</%def>
|
||||
|
||||
## DEPRECATED; remains for back-compat
|
||||
<%def name="render_whole_page_template()">
|
||||
${self.render_vue_template_whole_page()}
|
||||
${self.declare_whole_page_vars()}
|
||||
</%def>
|
||||
|
||||
## DEPRECATED; remains for back-compat
|
||||
<%def name="declare_whole_page_vars()">
|
||||
${self.render_vue_script_whole_page()}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
## DEPRECATED; called for back-compat
|
||||
${self.modify_whole_page_vars()}
|
||||
</%def>
|
||||
|
||||
## DEPRECATED; remains for back-compat
|
||||
<%def name="modify_whole_page_vars()">
|
||||
<script>
|
||||
|
||||
% if request.user:
|
||||
FeedbackFormData.userUUID = ${json.dumps(request.user.uuid)|n}
|
||||
FeedbackFormData.userName = ${json.dumps(str(request.user))|n}
|
||||
% endif
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_vue_components()">
|
||||
${make_wutta_components()}
|
||||
${make_grid_filter_components()}
|
||||
${page_help.make_component()}
|
||||
${multi_file_upload.make_component()}
|
||||
<script>
|
||||
FeedbackForm.data = function() { return FeedbackFormData }
|
||||
Vue.component('feedback-form', FeedbackForm)
|
||||
</script>
|
||||
|
||||
## DEPRECATED; called for back-compat
|
||||
${self.finalize_whole_page_vars()}
|
||||
${self.make_whole_page_component()}
|
||||
</%def>
|
||||
|
||||
## DEPRECATED; remains for back-compat
|
||||
<%def name="make_whole_page_component()">
|
||||
<script>
|
||||
WholePage.data = function() { return WholePageData }
|
||||
Vue.component('whole-page', WholePage)
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_vue_app()">
|
||||
## DEPRECATED; called for back-compat
|
||||
${self.make_whole_page_app()}
|
||||
</%def>
|
||||
|
||||
## DEPRECATED; remains for back-compat
|
||||
<%def name="make_whole_page_app()">
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app'
|
||||
})
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
##############################
|
||||
## DEPRECATED
|
||||
##############################
|
||||
|
||||
<%def name="finalize_whole_page_vars()"></%def>
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="wuttaweb:templates/base_meta.mako" />
|
||||
|
||||
<%def name="app_title()">${rattail_app.get_node_title()}</%def>
|
||||
|
||||
<%def name="global_title()">${"[STAGE] " if not request.rattail_config.production() else ''}${self.app_title()}</%def>
|
||||
|
||||
<%def name="extra_styles()"></%def>
|
||||
<%def name="app_title()">${app.get_node_title()}</%def>
|
||||
|
||||
<%def name="favicon()">
|
||||
<link rel="icon" type="image/x-icon" href="${request.rattail_config.get('tailbone', 'favicon_url', default=request.static_url('tailbone:static/img/rattail.ico'))}" />
|
||||
|
@ -13,9 +10,3 @@
|
|||
<%def name="header_logo()">
|
||||
${h.image(request.rattail_config.get('tailbone', 'header_image_url', default=request.static_url('tailbone:static/img/rattail.ico')), "Header Logo", style="height: 49px;")}
|
||||
</%def>
|
||||
|
||||
<%def name="footer()">
|
||||
<p class="has-text-centered">
|
||||
powered by ${h.link_to("Rattail", url('about'))}
|
||||
</p>
|
||||
</%def>
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
<br />
|
||||
<div class="form-wrapper">
|
||||
<div class="form">
|
||||
<${execute_form.component} ref="executeResultsForm"></${execute_form.component}>
|
||||
${execute_form.render_vue_tag(ref='executeResultsForm')}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
@ -64,10 +64,17 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
% if master.results_executable and master.has_perm('execute_multiple'):
|
||||
${execute_form.render_vue_template(form_kwargs={'ref': 'actualExecuteForm'}, buttons=False)}
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
% if master.results_refreshable and master.has_perm('refresh'):
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
TailboneGridData.refreshResultsButtonText = "Refresh Results"
|
||||
TailboneGridData.refreshResultsButtonDisabled = false
|
||||
|
@ -81,7 +88,7 @@
|
|||
</script>
|
||||
% endif
|
||||
% if master.results_executable and master.has_perm('execute_multiple'):
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
${execute_form.vue_component}.methods.submit = function() {
|
||||
this.$refs.actualExecuteForm.submit()
|
||||
|
@ -118,25 +125,9 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="make_this_page_component()">
|
||||
${parent.make_this_page_component()}
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
% if master.results_executable and master.has_perm('execute_multiple'):
|
||||
<script type="text/javascript">
|
||||
|
||||
${execute_form.vue_component}.data = function() { return ${execute_form.vue_component}Data }
|
||||
|
||||
Vue.component('${execute_form.component}', ${execute_form.vue_component})
|
||||
|
||||
</script>
|
||||
${execute_form.render_vue_finalize()}
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="render_this_page_template()">
|
||||
${parent.render_this_page_template()}
|
||||
% if master.results_executable and master.has_perm('execute_multiple'):
|
||||
${execute_form.render_deform(form_kwargs={'ref': 'actualExecuteForm'}, buttons=False)|n}
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -297,14 +297,9 @@
|
|||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
ThisPageData.toggleCompleteSubmitting = false
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="/batch/view.mako" />
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
${form.vue_component}Data.taxesData = ${json.dumps(taxes_data)|n}
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -39,14 +39,9 @@
|
|||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
ThisPageData.catalogParsers = ${json.dumps(catalog_parsers_data)|n}
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="/batch/create.mako" />
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
${form.vue_component}Data.parsers = ${json.dumps(parsers_data)|n}
|
||||
|
||||
|
@ -37,6 +37,3 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -119,8 +119,7 @@
|
|||
<div class="markdown">
|
||||
${execution_described|n}
|
||||
</div>
|
||||
<${execute_form.component} ref="executeBatchForm">
|
||||
</${execute_form.component}>
|
||||
${execute_form.render_vue_tag(ref='executeBatchForm')}
|
||||
</section>
|
||||
|
||||
<footer class="modal-card-foot">
|
||||
|
@ -149,12 +148,6 @@
|
|||
</nav>
|
||||
</%def>
|
||||
|
||||
<%def name="render_form_template()">
|
||||
## TODO: should use self.render_form_buttons()
|
||||
## ${form.render_deform(form_id='batch-form', buttons=capture(self.render_form_buttons))|n}
|
||||
${form.render_deform(form_id='batch-form', buttons=capture(buttons))|n}
|
||||
</%def>
|
||||
|
||||
<%def name="render_this_page()">
|
||||
${parent.render_this_page()}
|
||||
|
||||
|
@ -174,8 +167,7 @@
|
|||
Please be certain to use the right one!
|
||||
</p>
|
||||
<br />
|
||||
<${upload_worksheet_form.component} ref="uploadForm">
|
||||
</${upload_worksheet_form.component}>
|
||||
${upload_worksheet_form.render_vue_tag(ref='uploadForm')}
|
||||
</section>
|
||||
|
||||
<footer class="modal-card-foot">
|
||||
|
@ -197,16 +189,6 @@
|
|||
|
||||
</%def>
|
||||
|
||||
<%def name="render_this_page_template()">
|
||||
${parent.render_this_page_template()}
|
||||
% if master.has_worksheet_file and master.allow_worksheet(batch) and master.has_perm('worksheet'):
|
||||
${upload_worksheet_form.render_deform(buttons=False, form_kwargs={'ref': 'actualUploadForm'})|n}
|
||||
% endif
|
||||
% if master.handler.executable(batch) and master.has_perm('execute'):
|
||||
${execute_form.render_deform(form_kwargs={'ref': 'actualExecuteForm'}, buttons=False)|n}
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="render_form()">
|
||||
<div class="form">
|
||||
<${form.component} @show-upload="showUploadDialog = true">
|
||||
|
@ -267,9 +249,27 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
% if master.has_worksheet_file and master.allow_worksheet(batch) and master.has_perm('worksheet'):
|
||||
${upload_worksheet_form.render_vue_template(buttons=False, form_kwargs={'ref': 'actualUploadForm'})}
|
||||
% endif
|
||||
% if master.handler.executable(batch) and master.has_perm('execute'):
|
||||
${execute_form.render_vue_template(form_kwargs={'ref': 'actualExecuteForm'}, buttons=False)}
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
## DEPRECATED; remains for back-compat
|
||||
## nb. this is called by parent template, /form.mako
|
||||
<%def name="render_form_template()">
|
||||
## TODO: should use self.render_form_buttons()
|
||||
## ${form.render_deform(form_id='batch-form', buttons=capture(self.render_form_buttons))|n}
|
||||
${form.render_deform(form_id='batch-form', buttons=capture(buttons))|n}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
ThisPageData.statusBreakdownData = ${json.dumps(status_breakdown_data)|n}
|
||||
|
||||
|
@ -340,28 +340,12 @@
|
|||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_this_page_component()">
|
||||
${parent.make_this_page_component()}
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
% if master.has_worksheet_file and master.allow_worksheet(batch) and master.has_perm('worksheet'):
|
||||
<script type="text/javascript">
|
||||
|
||||
## UploadForm
|
||||
${upload_worksheet_form.vue_component}.data = function() { return ${upload_worksheet_form.vue_component}Data }
|
||||
Vue.component('${upload_worksheet_form.component}', ${upload_worksheet_form.vue_component})
|
||||
|
||||
</script>
|
||||
${upload_worksheet_form.render_vue_finalize()}
|
||||
% endif
|
||||
|
||||
% if execute_enabled and master.has_perm('execute'):
|
||||
<script type="text/javascript">
|
||||
|
||||
## ExecuteForm
|
||||
${execute_form.vue_component}.data = function() { return ${execute_form.vue_component}Data }
|
||||
Vue.component('${execute_form.component}', ${execute_form.vue_component})
|
||||
|
||||
</script>
|
||||
${execute_form.render_vue_finalize()}
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -208,9 +208,9 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
ThisPageData.menuSequence = ${json.dumps([m['key'] for m in menus])|n}
|
||||
|
||||
|
@ -443,6 +443,3 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -92,7 +92,7 @@
|
|||
<b-select name="${tmpl['setting_file']}"
|
||||
v-model="inputFileTemplateSettings['${tmpl['setting_file']}']"
|
||||
@input="settingsNeedSaved = true">
|
||||
<option :value="null">-new-</option>
|
||||
<option value="">-new-</option>
|
||||
<option v-for="option in inputFileTemplateFileOptions['${tmpl['key']}']"
|
||||
:key="option"
|
||||
:value="option">
|
||||
|
@ -104,22 +104,40 @@
|
|||
<b-field label="Upload"
|
||||
v-show="inputFileTemplateSettings['${tmpl['setting_mode']}'] == 'hosted' && !inputFileTemplateSettings['${tmpl['setting_file']}']">
|
||||
|
||||
<b-field class="file is-primary"
|
||||
:class="{'has-name': !!inputFileTemplateSettings['${tmpl['setting_file']}']}">
|
||||
<b-upload name="${tmpl['setting_file']}.upload"
|
||||
v-model="inputFileTemplateUploads['${tmpl['key']}']"
|
||||
class="file-label"
|
||||
@input="settingsNeedSaved = true">
|
||||
<span class="file-cta">
|
||||
<b-icon class="file-icon" pack="fas" icon="upload"></b-icon>
|
||||
<span class="file-label">Click to upload</span>
|
||||
</span>
|
||||
</b-upload>
|
||||
<span v-if="inputFileTemplateUploads['${tmpl['key']}']"
|
||||
class="file-name">
|
||||
{{ inputFileTemplateUploads['${tmpl['key']}'].name }}
|
||||
</span>
|
||||
</b-field>
|
||||
% if request.use_oruga:
|
||||
<o-field class="file">
|
||||
<o-upload name="${tmpl['setting_file']}.upload"
|
||||
v-model="inputFileTemplateUploads['${tmpl['key']}']"
|
||||
v-slot="{ onclick }"
|
||||
@input="settingsNeedSaved = true">
|
||||
<o-button variant="primary"
|
||||
@click="onclick">
|
||||
<o-icon icon="upload" />
|
||||
<span>Click to upload</span>
|
||||
</o-button>
|
||||
<span class="file-name" v-if="inputFileTemplateUploads['${tmpl['key']}']">
|
||||
{{ inputFileTemplateUploads['${tmpl['key']}'].name }}
|
||||
</span>
|
||||
</o-upload>
|
||||
</o-field>
|
||||
% else:
|
||||
<b-field class="file is-primary"
|
||||
:class="{'has-name': !!inputFileTemplateSettings['${tmpl['setting_file']}']}">
|
||||
<b-upload name="${tmpl['setting_file']}.upload"
|
||||
v-model="inputFileTemplateUploads['${tmpl['key']}']"
|
||||
class="file-label"
|
||||
@input="settingsNeedSaved = true">
|
||||
<span class="file-cta">
|
||||
<b-icon class="file-icon" pack="fas" icon="upload"></b-icon>
|
||||
<span class="file-label">Click to upload</span>
|
||||
</span>
|
||||
</b-upload>
|
||||
<span v-if="inputFileTemplateUploads['${tmpl['key']}']"
|
||||
class="file-name">
|
||||
{{ inputFileTemplateUploads['${tmpl['key']}'].name }}
|
||||
</span>
|
||||
</b-field>
|
||||
% endif
|
||||
|
||||
</b-field>
|
||||
|
||||
|
@ -143,6 +161,85 @@
|
|||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="output_file_template_field(key)">
|
||||
<% tmpl = output_file_templates[key] %>
|
||||
<b-field grouped>
|
||||
|
||||
<b-field label="${tmpl['label']}">
|
||||
<b-select name="${tmpl['setting_mode']}"
|
||||
v-model="outputFileTemplateSettings['${tmpl['setting_mode']}']"
|
||||
@input="settingsNeedSaved = true">
|
||||
<option value="default">use default</option>
|
||||
<option value="hosted">use uploaded file</option>
|
||||
</b-select>
|
||||
</b-field>
|
||||
|
||||
<b-field label="File"
|
||||
v-show="outputFileTemplateSettings['${tmpl['setting_mode']}'] == 'hosted'"
|
||||
:message="outputFileTemplateSettings['${tmpl['setting_file']}'] ? 'This file lives on disk at: ${output_file_option_dirs[tmpl['key']]}' : null">
|
||||
<b-select name="${tmpl['setting_file']}"
|
||||
v-model="outputFileTemplateSettings['${tmpl['setting_file']}']"
|
||||
@input="settingsNeedSaved = true">
|
||||
<option value="">-new-</option>
|
||||
<option v-for="option in outputFileTemplateFileOptions['${tmpl['key']}']"
|
||||
:key="option"
|
||||
:value="option">
|
||||
{{ option }}
|
||||
</option>
|
||||
</b-select>
|
||||
</b-field>
|
||||
|
||||
<b-field label="Upload"
|
||||
v-show="outputFileTemplateSettings['${tmpl['setting_mode']}'] == 'hosted' && !outputFileTemplateSettings['${tmpl['setting_file']}']">
|
||||
|
||||
% if request.use_oruga:
|
||||
<o-field class="file">
|
||||
<o-upload name="${tmpl['setting_file']}.upload"
|
||||
v-model="outputFileTemplateUploads['${tmpl['key']}']"
|
||||
v-slot="{ onclick }"
|
||||
@input="settingsNeedSaved = true">
|
||||
<o-button variant="primary"
|
||||
@click="onclick">
|
||||
<o-icon icon="upload" />
|
||||
<span>Click to upload</span>
|
||||
</o-button>
|
||||
<span class="file-name" v-if="outputFileTemplateUploads['${tmpl['key']}']">
|
||||
{{ outputFileTemplateUploads['${tmpl['key']}'].name }}
|
||||
</span>
|
||||
</o-upload>
|
||||
</o-field>
|
||||
% else:
|
||||
<b-field class="file is-primary"
|
||||
:class="{'has-name': !!outputFileTemplateSettings['${tmpl['setting_file']}']}">
|
||||
<b-upload name="${tmpl['setting_file']}.upload"
|
||||
v-model="outputFileTemplateUploads['${tmpl['key']}']"
|
||||
class="file-label"
|
||||
@input="settingsNeedSaved = true">
|
||||
<span class="file-cta">
|
||||
<b-icon class="file-icon" pack="fas" icon="upload"></b-icon>
|
||||
<span class="file-label">Click to upload</span>
|
||||
</span>
|
||||
</b-upload>
|
||||
<span v-if="outputFileTemplateUploads['${tmpl['key']}']"
|
||||
class="file-name">
|
||||
{{ outputFileTemplateUploads['${tmpl['key']}'].name }}
|
||||
</span>
|
||||
</b-field>
|
||||
% endif
|
||||
</b-field>
|
||||
|
||||
</b-field>
|
||||
</%def>
|
||||
|
||||
<%def name="output_file_templates_section()">
|
||||
<h3 class="block is-size-3">Output File Templates</h3>
|
||||
<div class="block" style="padding-left: 2rem;">
|
||||
% for key in output_file_templates:
|
||||
${self.output_file_template_field(key)}
|
||||
% endfor
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="form_content()"></%def>
|
||||
|
||||
<%def name="page_content()">
|
||||
|
@ -183,15 +280,14 @@
|
|||
<b-button @click="purgeSettingsShowDialog = false">
|
||||
Cancel
|
||||
</b-button>
|
||||
${h.form(request.current_route_url())}
|
||||
${h.form(request.current_route_url(), **{'@submit': 'purgingSettings = true'})}
|
||||
${h.csrf_token(request)}
|
||||
${h.hidden('remove_settings', 'true')}
|
||||
<b-button type="is-danger"
|
||||
native-type="submit"
|
||||
:disabled="purgingSettings"
|
||||
icon-pack="fas"
|
||||
icon-left="trash"
|
||||
@click="purgingSettings = true">
|
||||
icon-left="trash">
|
||||
{{ purgingSettings ? "Working, please wait..." : "Remove All Settings" }}
|
||||
</b-button>
|
||||
${h.end_form()}
|
||||
|
@ -205,62 +301,42 @@
|
|||
${h.end_form()}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
% if simple_settings is not Undefined:
|
||||
ThisPageData.simpleSettings = ${json.dumps(simple_settings)|n}
|
||||
% endif
|
||||
|
||||
% if input_file_template_settings is not Undefined:
|
||||
ThisPageData.inputFileTemplateSettings = ${json.dumps(input_file_template_settings)|n}
|
||||
ThisPageData.inputFileTemplateFileOptions = ${json.dumps(input_file_options)|n}
|
||||
ThisPageData.inputFileTemplateUploads = {
|
||||
% for key in input_file_templates:
|
||||
'${key}': null,
|
||||
% endfor
|
||||
}
|
||||
% endif
|
||||
|
||||
ThisPageData.purgeSettingsShowDialog = false
|
||||
ThisPageData.purgingSettings = false
|
||||
|
||||
ThisPageData.settingsNeedSaved = false
|
||||
ThisPageData.undoChanges = false
|
||||
ThisPageData.savingSettings = false
|
||||
ThisPageData.validators = []
|
||||
|
||||
ThisPage.methods.purgeSettingsInit = function() {
|
||||
this.purgeSettingsShowDialog = true
|
||||
}
|
||||
|
||||
% if input_file_template_settings is not Undefined:
|
||||
ThisPage.methods.validateInputFileTemplateSettings = function() {
|
||||
% for tmpl in input_file_templates.values():
|
||||
if (this.inputFileTemplateSettings['${tmpl['setting_mode']}'] == 'hosted') {
|
||||
if (!this.inputFileTemplateSettings['${tmpl['setting_file']}']) {
|
||||
if (!this.inputFileTemplateUploads['${tmpl['key']}']) {
|
||||
return "You must provide a file to upload for the ${tmpl['label']} template."
|
||||
}
|
||||
}
|
||||
}
|
||||
% endfor
|
||||
}
|
||||
% endif
|
||||
|
||||
ThisPage.methods.validateSettings = function() {
|
||||
let msg
|
||||
|
||||
% if input_file_template_settings is not Undefined:
|
||||
msg = this.validateInputFileTemplateSettings()
|
||||
if (msg) {
|
||||
return msg
|
||||
}
|
||||
% endif
|
||||
}
|
||||
ThisPage.methods.validateSettings = function() {}
|
||||
|
||||
ThisPage.methods.saveSettings = function() {
|
||||
let msg = this.validateSettings()
|
||||
let msg
|
||||
|
||||
// nb. this is the future
|
||||
for (let validator of this.validators) {
|
||||
msg = validator.call(this)
|
||||
if (msg) {
|
||||
alert(msg)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// nb. legacy method
|
||||
msg = this.validateSettings()
|
||||
if (msg) {
|
||||
alert(msg)
|
||||
return
|
||||
|
@ -291,8 +367,65 @@
|
|||
window.addEventListener('beforeunload', this.beforeWindowUnload)
|
||||
}
|
||||
|
||||
##############################
|
||||
## input file templates
|
||||
##############################
|
||||
|
||||
% if input_file_template_settings is not Undefined:
|
||||
|
||||
ThisPageData.inputFileTemplateSettings = ${json.dumps(input_file_template_settings)|n}
|
||||
ThisPageData.inputFileTemplateFileOptions = ${json.dumps(input_file_options)|n}
|
||||
ThisPageData.inputFileTemplateUploads = {
|
||||
% for key in input_file_templates:
|
||||
'${key}': null,
|
||||
% endfor
|
||||
}
|
||||
|
||||
ThisPage.methods.validateInputFileTemplateSettings = function() {
|
||||
% for tmpl in input_file_templates.values():
|
||||
if (this.inputFileTemplateSettings['${tmpl['setting_mode']}'] == 'hosted') {
|
||||
if (!this.inputFileTemplateSettings['${tmpl['setting_file']}']) {
|
||||
if (!this.inputFileTemplateUploads['${tmpl['key']}']) {
|
||||
return "You must provide a file to upload for the ${tmpl['label']} template."
|
||||
}
|
||||
}
|
||||
}
|
||||
% endfor
|
||||
}
|
||||
|
||||
ThisPageData.validators.push(ThisPage.methods.validateInputFileTemplateSettings)
|
||||
|
||||
% endif
|
||||
|
||||
##############################
|
||||
## output file templates
|
||||
##############################
|
||||
|
||||
% if output_file_template_settings is not Undefined:
|
||||
|
||||
ThisPageData.outputFileTemplateSettings = ${json.dumps(output_file_template_settings)|n}
|
||||
ThisPageData.outputFileTemplateFileOptions = ${json.dumps(output_file_options)|n}
|
||||
ThisPageData.outputFileTemplateUploads = {
|
||||
% for key in output_file_templates:
|
||||
'${key}': null,
|
||||
% endfor
|
||||
}
|
||||
|
||||
ThisPage.methods.validateOutputFileTemplateSettings = function() {
|
||||
% for tmpl in output_file_templates.values():
|
||||
if (this.outputFileTemplateSettings['${tmpl['setting_mode']}'] == 'hosted') {
|
||||
if (!this.outputFileTemplateSettings['${tmpl['setting_file']}']) {
|
||||
if (!this.outputFileTemplateUploads['${tmpl['key']}']) {
|
||||
return "You must provide a file to upload for the ${tmpl['label']} template."
|
||||
}
|
||||
}
|
||||
}
|
||||
% endfor
|
||||
}
|
||||
|
||||
ThisPageData.validators.push(ThisPage.methods.validateOutputFileTemplateSettings)
|
||||
|
||||
% endif
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -88,9 +88,9 @@
|
|||
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
ThisPage.methods.getLabelForKey = function(key) {
|
||||
switch (key) {
|
||||
|
@ -111,6 +111,3 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -106,9 +106,9 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
ThisPageData.resolvePersonShowDialog = false
|
||||
ThisPageData.resolvePersonUUID = null
|
||||
|
@ -139,5 +139,3 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
% if expose_shoppers:
|
||||
${form.vue_component}Data.shoppers = ${json.dumps(shoppers_data)|n}
|
||||
|
@ -36,5 +36,3 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -47,10 +47,9 @@
|
|||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="render_this_page_template()">
|
||||
${parent.render_this_page_template()}
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
${product_lookup.tailbone_product_lookup_template()}
|
||||
|
||||
<script type="text/x-template" id="customer-order-creator-template">
|
||||
<div>
|
||||
|
||||
|
@ -1265,12 +1264,7 @@
|
|||
|
||||
</div>
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_this_page_component()">
|
||||
${parent.make_this_page_component()}
|
||||
${product_lookup.tailbone_product_lookup_component()}
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
const CustomerOrderCreator = {
|
||||
template: '#customer-order-creator-template',
|
||||
|
@ -2406,5 +2400,7 @@
|
|||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
${product_lookup.tailbone_product_lookup_component()}
|
||||
</%def>
|
||||
|
|
|
@ -291,9 +291,9 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
${form.vue_component}Data.eventsData = ${json.dumps(events_data)|n}
|
||||
|
||||
|
@ -448,5 +448,3 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -26,9 +26,9 @@
|
|||
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
% if request.has_perm('datasync.restart'):
|
||||
TailboneGridData.restartDatasyncFormSubmitting = false
|
||||
|
@ -50,6 +50,3 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -83,8 +83,8 @@
|
|||
</b-notification>
|
||||
|
||||
<b-field>
|
||||
<b-checkbox name="use_profile_settings"
|
||||
v-model="useProfileSettings"
|
||||
<b-checkbox name="rattail.datasync.use_profile_settings"
|
||||
v-model="simpleSettings['rattail.datasync.use_profile_settings']"
|
||||
native-value="true"
|
||||
@input="settingsNeedSaved = true">
|
||||
Use these Settings to configure watchers and consumers
|
||||
|
@ -99,7 +99,7 @@
|
|||
</div>
|
||||
<div class="level-right">
|
||||
<div class="level-item"
|
||||
v-show="useProfileSettings">
|
||||
v-show="simpleSettings['rattail.datasync.use_profile_settings']">
|
||||
<b-button type="is-primary"
|
||||
@click="newProfile()"
|
||||
icon-pack="fas"
|
||||
|
@ -162,7 +162,7 @@
|
|||
</${b}-table-column>
|
||||
<${b}-table-column label="Actions"
|
||||
v-slot="props"
|
||||
v-if="useProfileSettings">
|
||||
v-if="simpleSettings['rattail.datasync.use_profile_settings']">
|
||||
<a href="#"
|
||||
class="grid-action"
|
||||
@click.prevent="editProfile(props.row)">
|
||||
|
@ -580,18 +580,27 @@
|
|||
<b-field label="Supervisor Process Name"
|
||||
message="This should be the complete name, including group - e.g. poser:poser_datasync"
|
||||
expanded>
|
||||
<b-input name="supervisor_process_name"
|
||||
v-model="supervisorProcessName"
|
||||
<b-input name="rattail.datasync.supervisor_process_name"
|
||||
v-model="simpleSettings['rattail.datasync.supervisor_process_name']"
|
||||
@input="settingsNeedSaved = true"
|
||||
expanded>
|
||||
</b-input>
|
||||
</b-field>
|
||||
|
||||
<b-field label="Consumer Batch Size"
|
||||
message="Max number of changes to be consumed at once."
|
||||
expanded>
|
||||
<numeric-input name="rattail.datasync.batch_size_limit"
|
||||
v-model="simpleSettings['rattail.datasync.batch_size_limit']"
|
||||
@input="settingsNeedSaved = true" />
|
||||
</b-field>
|
||||
|
||||
<h3 class="is-size-3">Legacy</h3>
|
||||
<b-field label="Restart Command"
|
||||
message="This will run as '${system_user}' system user - please configure sudoers as needed. Typical command is like: sudo supervisorctl restart poser:poser_datasync"
|
||||
expanded>
|
||||
<b-input name="restart_command"
|
||||
v-model="restartCommand"
|
||||
<b-input name="tailbone.datasync.restart"
|
||||
v-model="simpleSettings['tailbone.datasync.restart']"
|
||||
@input="settingsNeedSaved = true"
|
||||
expanded>
|
||||
</b-input>
|
||||
|
@ -599,14 +608,13 @@
|
|||
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
ThisPageData.showConfigFilesNote = false
|
||||
ThisPageData.profilesData = ${json.dumps(profiles_data)|n}
|
||||
ThisPageData.showDisabledProfiles = false
|
||||
ThisPageData.useProfileSettings = ${json.dumps(use_profile_settings)|n}
|
||||
|
||||
ThisPageData.editProfileShowDialog = false
|
||||
ThisPageData.editingProfile = null
|
||||
|
@ -631,9 +639,6 @@
|
|||
ThisPageData.editingConsumerRunas = null
|
||||
ThisPageData.editingConsumerEnabled = true
|
||||
|
||||
ThisPageData.supervisorProcessName = ${json.dumps(supervisor_process_name)|n}
|
||||
ThisPageData.restartCommand = ${json.dumps(restart_command)|n}
|
||||
|
||||
ThisPage.computed.updateConsumerDisabled = function() {
|
||||
if (!this.editingConsumerKey) {
|
||||
return true
|
||||
|
@ -982,6 +987,3 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -115,8 +115,9 @@
|
|||
</${b}-table>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
ThisPageData.processInfo = ${json.dumps(process_info)|n}
|
||||
|
||||
|
@ -171,6 +172,3 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<div i18n:domain="deform" tal:omit-tag=""
|
||||
tal:define="oid oid|field.oid;
|
||||
name name|field.name;
|
||||
vmodel vmodel|'field_model_' + name;
|
||||
css_class css_class|field.widget.css_class;
|
||||
style style|field.widget.style;">
|
||||
|
||||
|
@ -8,7 +9,7 @@
|
|||
${field.start_mapping()}
|
||||
<b-input type="password"
|
||||
name="${name}"
|
||||
value="${field.widget.redisplay and cstruct or ''}"
|
||||
v-model="${vmodel}"
|
||||
tal:attributes="class string: form-control ${css_class or ''};
|
||||
style style;
|
||||
attributes|field.widget.attributes|{};"
|
||||
|
@ -18,7 +19,6 @@
|
|||
</b-input>
|
||||
<b-input type="password"
|
||||
name="${name}-confirm"
|
||||
value="${field.widget.redisplay and confirm or ''}"
|
||||
tal:attributes="class string: form-control ${css_class or ''};
|
||||
style style;
|
||||
confirm_attributes|field.widget.confirm_attributes|{};"
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="/master/view.mako" />
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
${form.vue_component}Data.employeesData = ${json.dumps(employees_data)|n}
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -90,15 +90,15 @@
|
|||
|
||||
<%def name="before_object_helpers()"></%def>
|
||||
|
||||
<%def name="render_this_page_template()">
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
% if form is not Undefined:
|
||||
${self.render_form_template()}
|
||||
% endif
|
||||
${parent.render_this_page_template()}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
% if main_form_collapsible:
|
||||
<script>
|
||||
ThisPageData.mainFormPanelOpen = ${'false' if main_form_autocollapse else 'true'}
|
||||
|
@ -106,18 +106,9 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="finalize_this_page_vars()">
|
||||
${parent.finalize_this_page_vars()}
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
% if form is not Undefined:
|
||||
<script type="text/javascript">
|
||||
|
||||
${form.vue_component}.data = function() { return ${form.vue_component}Data }
|
||||
|
||||
Vue.component('${form.vue_tagname}', ${form.vue_component})
|
||||
|
||||
</script>
|
||||
${form.render_vue_finalize()}
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
|
||||
simplePOST(action, params, success, failure) {
|
||||
|
||||
let csrftoken = ${json.dumps(request.session.get_csrf_token() or request.session.new_csrf_token())|n}
|
||||
let csrftoken = ${json.dumps(h.get_csrf_token(request))|n}
|
||||
|
||||
let headers = {
|
||||
'${csrf_header_name}': csrftoken,
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
native-type="submit"
|
||||
:disabled="${form.vue_component}Submitting"
|
||||
icon-pack="fas"
|
||||
icon-left="save">
|
||||
icon-left="${form.button_icon_submit}">
|
||||
{{ ${form.vue_component}Submitting ? "Working, please wait..." : "${form.button_label_submit}" }}
|
||||
</b-button>
|
||||
% else:
|
||||
|
@ -180,7 +180,7 @@
|
|||
let ${form.vue_component}Data = {
|
||||
|
||||
## TODO: should find a better way to handle CSRF token
|
||||
csrftoken: ${json.dumps(request.session.get_csrf_token() or request.session.new_csrf_token())|n},
|
||||
csrftoken: ${json.dumps(h.get_csrf_token(request))|n},
|
||||
|
||||
% if can_edit_help:
|
||||
fieldLabels: ${json.dumps(field_labels)|n},
|
||||
|
|
|
@ -276,9 +276,9 @@
|
|||
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
ThisPageData.featureType = ${json.dumps(feature_type)|n}
|
||||
ThisPageData.resultGenerated = ${json.dumps(bool(result))|n}
|
||||
|
@ -385,6 +385,3 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -10,8 +10,70 @@
|
|||
<div style="display: flex; flex-direction: column; justify-content: end;">
|
||||
<div class="filters">
|
||||
% if getattr(grid, 'filterable', False):
|
||||
## TODO: stop using |n filter
|
||||
${grid.render_filters(allow_save_defaults=allow_save_defaults)|n}
|
||||
<form method="GET" @submit.prevent="applyFilters()">
|
||||
|
||||
<div style="display: flex; flex-direction: column; gap: 0.5rem;">
|
||||
<grid-filter v-for="key in filtersSequence"
|
||||
:key="key"
|
||||
:filter="filters[key]"
|
||||
ref="gridFilters">
|
||||
</grid-filter>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 0.5rem; margin-top: 0.5rem;">
|
||||
|
||||
<b-button type="is-primary"
|
||||
native-type="submit"
|
||||
icon-pack="fas"
|
||||
icon-left="check">
|
||||
Apply Filters
|
||||
</b-button>
|
||||
|
||||
<b-button v-if="!addFilterShow"
|
||||
icon-pack="fas"
|
||||
icon-left="plus"
|
||||
@click="addFilterInit()">
|
||||
Add Filter
|
||||
</b-button>
|
||||
|
||||
<b-autocomplete v-if="addFilterShow"
|
||||
ref="addFilterAutocomplete"
|
||||
:data="addFilterChoices"
|
||||
v-model="addFilterTerm"
|
||||
placeholder="Add Filter"
|
||||
field="key"
|
||||
:custom-formatter="formatAddFilterItem"
|
||||
open-on-focus
|
||||
keep-first
|
||||
icon-pack="fas"
|
||||
clearable
|
||||
clear-on-select
|
||||
@select="addFilterSelect">
|
||||
</b-autocomplete>
|
||||
|
||||
<b-button @click="resetView()"
|
||||
icon-pack="fas"
|
||||
icon-left="home">
|
||||
Default View
|
||||
</b-button>
|
||||
|
||||
<b-button @click="clearFilters()"
|
||||
icon-pack="fas"
|
||||
icon-left="trash">
|
||||
No Filters
|
||||
</b-button>
|
||||
|
||||
% if allow_save_defaults and request.user:
|
||||
<b-button @click="saveDefaults()"
|
||||
icon-pack="fas"
|
||||
icon-left="save"
|
||||
:disabled="savingDefaults">
|
||||
{{ savingDefaults ? "Working, please wait..." : "Save Defaults" }}
|
||||
</b-button>
|
||||
% endif
|
||||
|
||||
</div>
|
||||
</form>
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
|
@ -115,7 +177,7 @@
|
|||
## paging
|
||||
% if grid.paginated:
|
||||
paginated
|
||||
pagination-size="is-small"
|
||||
pagination-size="${'small' if request.use_oruga else 'is-small'}"
|
||||
:per-page="perPage"
|
||||
:current-page="currentPage"
|
||||
@page-change="onPageChange"
|
||||
|
@ -136,10 +198,8 @@
|
|||
<${b}-table-column field="${column['field']}"
|
||||
label="${column['label']}"
|
||||
v-slot="props"
|
||||
:sortable="${json.dumps(column.get('sortable', False))}"
|
||||
% if hasattr(grid, 'is_searchable') and grid.is_searchable(column['field']):
|
||||
searchable
|
||||
% endif
|
||||
:sortable="${json.dumps(column.get('sortable', False))|n}"
|
||||
:searchable="${json.dumps(column.get('searchable', False))|n}"
|
||||
cell-class="c_${column['field']}"
|
||||
:visible="${json.dumps(column.get('visible', True))}">
|
||||
% if hasattr(grid, 'raw_renderers') and column['field'] in grid.raw_renderers:
|
||||
|
@ -251,12 +311,16 @@
|
|||
|
||||
<script type="text/javascript">
|
||||
|
||||
let ${grid.vue_component}CurrentData = ${json.dumps(grid.get_vue_data())|n}
|
||||
const ${grid.vue_component}Context = ${json.dumps(grid.get_vue_context())|n}
|
||||
let ${grid.vue_component}CurrentData = ${grid.vue_component}Context.data
|
||||
|
||||
let ${grid.vue_component}Data = {
|
||||
loading: false,
|
||||
ajaxDataUrl: ${json.dumps(getattr(grid, 'ajax_data_url', request.path_url))|n},
|
||||
|
||||
## nb. this tracks whether grid.fetchFirstData() happened
|
||||
fetchedFirstData: false,
|
||||
|
||||
savingDefaults: false,
|
||||
|
||||
data: ${grid.vue_component}CurrentData,
|
||||
|
@ -519,6 +583,17 @@
|
|||
...this.getFilterParams()}
|
||||
},
|
||||
|
||||
## nb. this is meant to call for a grid which is hidden at
|
||||
## first, when it is first being shown to the user. and if
|
||||
## it was initialized with empty data set.
|
||||
async fetchFirstData() {
|
||||
if (this.fetchedFirstData) {
|
||||
return
|
||||
}
|
||||
await this.loadAsyncData()
|
||||
this.fetchedFirstData = true
|
||||
},
|
||||
|
||||
## TODO: i noticed buefy docs show using `async` keyword here,
|
||||
## so now i am too. knowing nothing at all of if/how this is
|
||||
## supposed to improve anything. we shall see i guess
|
||||
|
@ -671,7 +746,7 @@
|
|||
this.loading = true
|
||||
|
||||
// use current url proper, plus reset param
|
||||
let url = '?reset-to-default-filters=true'
|
||||
let url = '?reset-view=true'
|
||||
|
||||
// add current hash, to preserve that in redirect
|
||||
if (location.hash) {
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
|
||||
<form action="${form.action_url}" method="GET" @submit.prevent="applyFilters()">
|
||||
|
||||
<div style="display: flex; flex-direction: column; gap: 0.5rem;">
|
||||
<grid-filter v-for="key in filtersSequence"
|
||||
:key="key"
|
||||
:filter="filters[key]"
|
||||
ref="gridFilters">
|
||||
</grid-filter>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 0.5rem; margin-top: 0.5rem;">
|
||||
|
||||
<b-button type="is-primary"
|
||||
native-type="submit"
|
||||
icon-pack="fas"
|
||||
icon-left="check">
|
||||
Apply Filters
|
||||
</b-button>
|
||||
|
||||
<b-button v-if="!addFilterShow"
|
||||
icon-pack="fas"
|
||||
icon-left="plus"
|
||||
@click="addFilterInit()">
|
||||
Add Filter
|
||||
</b-button>
|
||||
|
||||
<b-autocomplete v-if="addFilterShow"
|
||||
ref="addFilterAutocomplete"
|
||||
:data="addFilterChoices"
|
||||
v-model="addFilterTerm"
|
||||
placeholder="Add Filter"
|
||||
field="key"
|
||||
:custom-formatter="formatAddFilterItem"
|
||||
open-on-focus
|
||||
keep-first
|
||||
icon-pack="fas"
|
||||
clearable
|
||||
clear-on-select
|
||||
@select="addFilterSelect">
|
||||
</b-autocomplete>
|
||||
|
||||
<b-button @click="resetView()"
|
||||
icon-pack="fas"
|
||||
icon-left="home">
|
||||
Default View
|
||||
</b-button>
|
||||
|
||||
<b-button @click="clearFilters()"
|
||||
icon-pack="fas"
|
||||
icon-left="trash">
|
||||
No Filters
|
||||
</b-button>
|
||||
|
||||
% if allow_save_defaults and request.user:
|
||||
<b-button @click="saveDefaults()"
|
||||
icon-pack="fas"
|
||||
icon-left="save"
|
||||
:disabled="savingDefaults">
|
||||
{{ savingDefaults ? "Working, please wait..." : "Save Defaults" }}
|
||||
</b-button>
|
||||
% endif
|
||||
|
||||
</div>
|
||||
|
||||
</form>
|
|
@ -1,33 +1,7 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="/page.mako" />
|
||||
<%namespace name="base_meta" file="/base_meta.mako" />
|
||||
|
||||
<%def name="title()">Home</%def>
|
||||
|
||||
<%def name="extra_styles()">
|
||||
${parent.extra_styles()}
|
||||
<style type="text/css">
|
||||
.logo {
|
||||
text-align: center;
|
||||
}
|
||||
.logo img {
|
||||
margin: 3em auto;
|
||||
max-height: 350px;
|
||||
max-width: 800px;
|
||||
}
|
||||
</style>
|
||||
</%def>
|
||||
<%inherit file="wuttaweb:templates/home.mako" />
|
||||
|
||||
## DEPRECATED; remains for back-compat
|
||||
<%def name="render_this_page()">
|
||||
${self.page_content()}
|
||||
</%def>
|
||||
|
||||
<%def name="page_content()">
|
||||
<div class="logo">
|
||||
${h.image(image_url, "{} logo".format(capture(base_meta.app_title)))}
|
||||
<h1>Welcome to ${base_meta.app_title()}</h1>
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -144,9 +144,9 @@
|
|||
</b-modal>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
ThisPageData.handlersData = ${json.dumps(handlers_data)|n}
|
||||
|
||||
|
@ -203,6 +203,3 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -63,9 +63,9 @@
|
|||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
${form.vue_component}Data.submittingRun = false
|
||||
${form.vue_component}Data.submittingExplain = false
|
||||
|
@ -86,5 +86,3 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -1,86 +1,17 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="/form.mako" />
|
||||
<%namespace name="base_meta" file="/base_meta.mako" />
|
||||
|
||||
<%def name="title()">Login</%def>
|
||||
<%inherit file="wuttaweb:templates/auth/login.mako" />
|
||||
|
||||
## TODO: this will not be needed with wuttaform
|
||||
<%def name="extra_styles()">
|
||||
${parent.extra_styles()}
|
||||
<style type="text/css">
|
||||
.logo img {
|
||||
display: block;
|
||||
margin: 3rem auto;
|
||||
max-height: 350px;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
/* must force a particular label with, in order to make sure */
|
||||
/* the username and password inputs are the same size */
|
||||
.field.is-horizontal .field-label .label {
|
||||
text-align: left;
|
||||
width: 6rem;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
<style>
|
||||
.card-content .buttons {
|
||||
justify-content: right;
|
||||
}
|
||||
</style>
|
||||
</%def>
|
||||
|
||||
<%def name="logo()">
|
||||
${h.image(image_url, "{} logo".format(capture(base_meta.app_title)))}
|
||||
</%def>
|
||||
|
||||
<%def name="login_form()">
|
||||
<div class="form">
|
||||
${form.render_deform(form_kwargs={'data-ajax': 'false'})|n}
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
## DEPRECATED; remains for back-compat
|
||||
<%def name="render_this_page()">
|
||||
${self.page_content()}
|
||||
</%def>
|
||||
|
||||
<%def name="page_content()">
|
||||
<div class="logo">
|
||||
${self.logo()}
|
||||
</div>
|
||||
|
||||
<div class="columns is-centered">
|
||||
<div class="column is-narrow">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<tailbone-form></tailbone-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
<script type="text/javascript">
|
||||
|
||||
${form.vue_component}Data.usernameInput = null
|
||||
|
||||
${form.vue_component}.mounted = function() {
|
||||
this.$refs.username.focus()
|
||||
this.usernameInput = this.$refs.username.$el.querySelector('input')
|
||||
this.usernameInput.addEventListener('keydown', this.usernameKeydown)
|
||||
}
|
||||
|
||||
${form.vue_component}.beforeDestroy = function() {
|
||||
this.usernameInput.removeEventListener('keydown', this.usernameKeydown)
|
||||
}
|
||||
|
||||
${form.vue_component}.methods.usernameKeydown = function(event) {
|
||||
if (event.which == 13) {
|
||||
event.preventDefault()
|
||||
this.$refs.password.focus()
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -297,9 +297,9 @@
|
|||
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
ThisPageData.overnightTasks = ${json.dumps(overnight_tasks)|n}
|
||||
ThisPageData.overnightTaskShowDialog = false
|
||||
|
@ -425,6 +425,3 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -255,9 +255,9 @@
|
|||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
% if master.has_perm('restart_scheduler'):
|
||||
|
||||
|
@ -374,6 +374,3 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -34,9 +34,9 @@
|
|||
${h.end_form()}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
TailboneFormData.formSubmitting = false
|
||||
TailboneFormData.submitButtonText = "Yes, please clone away"
|
||||
|
@ -48,6 +48,3 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -33,8 +33,8 @@
|
|||
${h.end_form()}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
${form.vue_component}Data.formSubmitting = false
|
||||
|
@ -45,6 +45,3 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="/form.mako" />
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
## declare extra data needed by form
|
||||
% if form is not Undefined and getattr(form, 'json_data', None):
|
||||
|
@ -28,6 +28,3 @@
|
|||
% endif
|
||||
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -265,6 +265,11 @@
|
|||
|
||||
</%def>
|
||||
|
||||
## DEPRECATED; remains for back-compat
|
||||
<%def name="render_this_page()">
|
||||
${self.page_content()}
|
||||
</%def>
|
||||
|
||||
<%def name="page_content()">
|
||||
|
||||
% if download_results_path:
|
||||
|
@ -290,34 +295,28 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="make_grid_component()">
|
||||
${grid.render_vue_template(tools=capture(self.grid_tools).strip(), context_menu=capture(self.context_menu_items).strip())}
|
||||
</%def>
|
||||
|
||||
<%def name="render_grid_component()">
|
||||
${grid.render_vue_tag()}
|
||||
</%def>
|
||||
|
||||
<%def name="make_this_page_component()">
|
||||
##############################
|
||||
## vue components
|
||||
##############################
|
||||
|
||||
## define grid
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
|
||||
## DEPRECATED; called for back-compat
|
||||
${self.make_grid_component()}
|
||||
|
||||
${parent.make_this_page_component()}
|
||||
|
||||
## finalize grid
|
||||
<script>
|
||||
${grid.vue_component}.data = function() { return ${grid.vue_component}Data }
|
||||
Vue.component('${grid.vue_tagname}', ${grid.vue_component})
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="render_this_page()">
|
||||
${self.page_content()}
|
||||
## DEPRECATED; remains for back-compat
|
||||
<%def name="make_grid_component()">
|
||||
${grid.render_vue_template(tools=capture(self.grid_tools).strip(), context_menu=capture(self.context_menu_items).strip())}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
% if getattr(master, 'supports_grid_totals', False):
|
||||
|
@ -624,5 +623,10 @@
|
|||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
<script>
|
||||
${grid.vue_component}.data = function() { return ${grid.vue_component}Data }
|
||||
Vue.component('${grid.vue_tagname}', ${grid.vue_component})
|
||||
</script>
|
||||
</%def>
|
||||
|
|
|
@ -109,8 +109,8 @@
|
|||
<merge-buttons></merge-buttons>
|
||||
</%def>
|
||||
|
||||
<%def name="render_this_page_template()">
|
||||
${parent.render_this_page_template()}
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
|
||||
<script type="text/x-template" id="merge-buttons-template">
|
||||
<div class="level" style="margin-top: 2em;">
|
||||
|
@ -147,11 +147,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_this_page_component()">
|
||||
${parent.make_this_page_component()}
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
const MergeButtons = {
|
||||
template: '#merge-buttons-template',
|
||||
|
@ -175,12 +171,13 @@
|
|||
}
|
||||
}
|
||||
|
||||
Vue.component('merge-buttons', MergeButtons)
|
||||
|
||||
<% request.register_component('merge-buttons', 'MergeButtons') %>
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
<script>
|
||||
Vue.component('merge-buttons', MergeButtons)
|
||||
<% request.register_component('merge-buttons', 'MergeButtons') %>
|
||||
</script>
|
||||
</%def>
|
||||
|
|
|
@ -16,27 +16,16 @@
|
|||
${self.page_content()}
|
||||
</%def>
|
||||
|
||||
<%def name="make_this_page_component()">
|
||||
${parent.make_this_page_component()}
|
||||
<script type="text/javascript">
|
||||
|
||||
TailboneGrid.data = function() { return TailboneGridData }
|
||||
|
||||
Vue.component('tailbone-grid', TailboneGrid)
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="render_this_page_template()">
|
||||
${parent.render_this_page_template()}
|
||||
|
||||
## TODO: stop using |n filter
|
||||
${grid.render_complete()|n}
|
||||
</%def>
|
||||
|
||||
<%def name="page_content()">
|
||||
<tailbone-grid :csrftoken="csrftoken">
|
||||
</tailbone-grid>
|
||||
${grid.render_vue_tag(**{':csrftoken': 'csrftoken'})}
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
${grid.render_vue_template()}
|
||||
</%def>
|
||||
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
${grid.render_vue_finalize()}
|
||||
</%def>
|
||||
|
|
|
@ -120,9 +120,7 @@
|
|||
</p>
|
||||
</div>
|
||||
|
||||
<versions-grid ref="versionsGrid"
|
||||
@view-revision="viewRevision">
|
||||
</versions-grid>
|
||||
${versions_grid.render_vue_tag(ref='versionsGrid', **{'@view-revision': 'viewRevision'})}
|
||||
|
||||
<${b}-modal :width="1200"
|
||||
% if request.use_oruga:
|
||||
|
@ -198,6 +196,7 @@
|
|||
|
||||
<p class="block has-text-weight-bold">
|
||||
{{ version.model_title }}
|
||||
({{ version.operation }})
|
||||
</p>
|
||||
|
||||
<table class="diff monospace is-size-7"
|
||||
|
@ -237,25 +236,37 @@
|
|||
</%def>
|
||||
|
||||
<%def name="render_row_grid_component()">
|
||||
<tailbone-grid ref="rowGrid" id="rowGrid"></tailbone-grid>
|
||||
${rows_grid.render_vue_tag(id='rowGrid', ref='rowGrid')}
|
||||
</%def>
|
||||
|
||||
<%def name="render_this_page_template()">
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
% if getattr(master, 'has_rows', False):
|
||||
## TODO: stop using |n filter
|
||||
${rows_grid.render_complete(allow_save_defaults=False, tools=capture(self.render_row_grid_tools))|n}
|
||||
${rows_grid.render_vue_template(allow_save_defaults=False, tools=capture(self.render_row_grid_tools))}
|
||||
% endif
|
||||
${parent.render_this_page_template()}
|
||||
% if expose_versions:
|
||||
${versions_grid.render_complete()|n}
|
||||
${versions_grid.render_vue_template()}
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
% if expose_versions:
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
% if getattr(master, 'touchable', False) and master.has_perm('touch'):
|
||||
|
||||
WholePageData.touchSubmitting = false
|
||||
|
||||
WholePage.methods.touchRecord = function() {
|
||||
this.touchSubmitting = true
|
||||
location.href = '${master.get_action_url('touch', instance)}'
|
||||
}
|
||||
|
||||
% endif
|
||||
|
||||
% if expose_versions:
|
||||
|
||||
WholePageData.viewingHistory = false
|
||||
ThisPage.props.viewingHistory = Boolean
|
||||
|
||||
ThisPageData.gettingRevisions = false
|
||||
|
@ -310,48 +321,16 @@
|
|||
this.viewVersionShowAllFields = !this.viewVersionShowAllFields
|
||||
}
|
||||
|
||||
</script>
|
||||
% endif
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
% if getattr(master, 'has_rows', False):
|
||||
${rows_grid.render_vue_finalize()}
|
||||
% endif
|
||||
% if expose_versions:
|
||||
${versions_grid.render_vue_finalize()}
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_whole_page_vars()">
|
||||
${parent.modify_whole_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
% if getattr(master, 'touchable', False) and master.has_perm('touch'):
|
||||
|
||||
WholePageData.touchSubmitting = false
|
||||
|
||||
WholePage.methods.touchRecord = function() {
|
||||
this.touchSubmitting = true
|
||||
location.href = '${master.get_action_url('touch', instance)}'
|
||||
}
|
||||
|
||||
% endif
|
||||
|
||||
% if expose_versions:
|
||||
WholePageData.viewingHistory = false
|
||||
% endif
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="finalize_this_page_vars()">
|
||||
${parent.finalize_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
% if getattr(master, 'has_rows', False):
|
||||
TailboneGrid.data = function() { return TailboneGridData }
|
||||
Vue.component('tailbone-grid', TailboneGrid)
|
||||
% endif
|
||||
|
||||
% if expose_versions:
|
||||
VersionsGrid.data = function() { return VersionsGridData }
|
||||
Vue.component('versions-grid', VersionsGrid)
|
||||
% endif
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -52,9 +52,9 @@
|
|||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
ThisPage.methods.getLabelForKey = function(key) {
|
||||
switch (key) {
|
||||
|
@ -75,6 +75,3 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -32,14 +32,14 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="render_this_page_template()">
|
||||
${parent.render_this_page_template()}
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
${message_recipients_template()}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
TailboneFormData.possibleRecipients = new Map(${json.dumps(available_recipients)|n})
|
||||
TailboneFormData.recipientDisplayMap = ${json.dumps(recipient_display_map)|n}
|
||||
|
@ -59,6 +59,3 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -22,15 +22,15 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
% if request.matched_route.name in ('messages.inbox', 'messages.archive'):
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
TailboneGridData.moveMessagesSubmitting = false
|
||||
TailboneGridData.moveMessagesText = null
|
||||
${grid.vue_component}Data.moveMessagesSubmitting = false
|
||||
${grid.vue_component}Data.moveMessagesText = null
|
||||
|
||||
TailboneGrid.computed.moveMessagesTextCurrent = function() {
|
||||
${grid.vue_component}.computed.moveMessagesTextCurrent = function() {
|
||||
if (this.moveMessagesText) {
|
||||
return this.moveMessagesText
|
||||
}
|
||||
|
@ -38,7 +38,7 @@
|
|||
return "Move " + count.toString() + " selected to ${'Archive' if request.matched_route.name == 'messages.inbox' else 'Inbox'}"
|
||||
}
|
||||
|
||||
TailboneGrid.methods.moveMessagesSubmit = function() {
|
||||
${grid.vue_component}.methods.moveMessagesSubmit = function() {
|
||||
this.moveMessagesSubmitting = true
|
||||
this.moveMessagesText = "Working, please wait..."
|
||||
}
|
||||
|
@ -46,6 +46,3 @@
|
|||
</script>
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -82,22 +82,19 @@
|
|||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
TailboneFormData.showingAllRecipients = false
|
||||
${form.vue_component}Data.showingAllRecipients = false
|
||||
|
||||
TailboneForm.methods.showMoreRecipients = function() {
|
||||
${form.vue_component}.methods.showMoreRecipients = function() {
|
||||
this.showingAllRecipients = true
|
||||
}
|
||||
|
||||
TailboneForm.methods.hideMoreRecipients = function() {
|
||||
${form.vue_component}.methods.hideMoreRecipients = function() {
|
||||
this.showingAllRecipients = false
|
||||
}
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
74
tailbone/templates/ordering/configure.mako
Normal file
74
tailbone/templates/ordering/configure.mako
Normal file
|
@ -0,0 +1,74 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="/configure.mako" />
|
||||
|
||||
<%def name="form_content()">
|
||||
|
||||
<h3 class="block is-size-3">Workflows</h3>
|
||||
<div class="block" style="padding-left: 2rem;">
|
||||
|
||||
<p class="block">
|
||||
Users can only choose from the workflows enabled below.
|
||||
</p>
|
||||
|
||||
<b-field>
|
||||
<b-checkbox name="rattail.batch.purchase.allow_ordering_from_scratch"
|
||||
v-model="simpleSettings['rattail.batch.purchase.allow_ordering_from_scratch']"
|
||||
native-value="true"
|
||||
@input="settingsNeedSaved = true">
|
||||
From Scratch
|
||||
</b-checkbox>
|
||||
</b-field>
|
||||
|
||||
<b-field>
|
||||
<b-checkbox name="rattail.batch.purchase.allow_ordering_from_file"
|
||||
v-model="simpleSettings['rattail.batch.purchase.allow_ordering_from_file']"
|
||||
native-value="true"
|
||||
@input="settingsNeedSaved = true">
|
||||
From Order File
|
||||
</b-checkbox>
|
||||
</b-field>
|
||||
|
||||
</div>
|
||||
|
||||
<h3 class="block is-size-3">Vendors</h3>
|
||||
<div class="block" style="padding-left: 2rem;">
|
||||
|
||||
<b-field message="If not set, user must choose a "supported" vendor.">
|
||||
<b-checkbox name="rattail.batch.purchase.allow_ordering_any_vendor"
|
||||
v-model="simpleSettings['rattail.batch.purchase.allow_ordering_any_vendor']"
|
||||
native-value="true"
|
||||
@input="settingsNeedSaved = true">
|
||||
Allow ordering for <span class="has-text-weight-bold">any</span> vendor
|
||||
</b-checkbox>
|
||||
</b-field>
|
||||
|
||||
</div>
|
||||
|
||||
<h3 class="block is-size-3">Order Parsers</h3>
|
||||
<div class="block" style="padding-left: 2rem;">
|
||||
|
||||
<p class="block">
|
||||
Only the selected file parsers will be exposed to users.
|
||||
</p>
|
||||
|
||||
% for Parser in order_parsers:
|
||||
<b-field message="${Parser.key}">
|
||||
<b-checkbox name="order_parser_${Parser.key}"
|
||||
v-model="orderParsers['${Parser.key}']"
|
||||
native-value="true"
|
||||
@input="settingsNeedSaved = true">
|
||||
${Parser.title}
|
||||
</b-checkbox>
|
||||
</b-field>
|
||||
% endfor
|
||||
|
||||
</div>
|
||||
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
ThisPageData.orderParsers = ${json.dumps(order_parsers_data)|n}
|
||||
</script>
|
||||
</%def>
|
|
@ -21,8 +21,8 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="render_this_page_template()">
|
||||
${parent.render_this_page_template()}
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
% if not batch.executed and not batch.complete and master.has_perm('edit_row'):
|
||||
<script type="text/x-template" id="ordering-scanner-template">
|
||||
<div>
|
||||
|
@ -185,10 +185,10 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
% if not batch.executed and not batch.complete and master.has_perm('edit_row'):
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
let OrderingScanner = {
|
||||
template: '#ordering-scanner-template',
|
||||
|
@ -204,7 +204,7 @@
|
|||
saving: false,
|
||||
|
||||
## TODO: should find a better way to handle CSRF token
|
||||
csrftoken: ${json.dumps(request.session.get_csrf_token() or request.session.new_csrf_token())|n},
|
||||
csrftoken: ${json.dumps(h.get_csrf_token(request))|n},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -408,16 +408,11 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="make_this_page_component()">
|
||||
${parent.make_this_page_component()}
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
% if not batch.executed and not batch.complete and master.has_perm('edit_row'):
|
||||
<script type="text/javascript">
|
||||
|
||||
<script>
|
||||
Vue.component('ordering-scanner', OrderingScanner)
|
||||
|
||||
</script>
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -199,9 +199,8 @@
|
|||
<ordering-worksheet></ordering-worksheet>
|
||||
</%def>
|
||||
|
||||
<%def name="render_this_page_template()">
|
||||
${parent.render_this_page_template()}
|
||||
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
<script type="text/x-template" id="ordering-worksheet-template">
|
||||
<div>
|
||||
<div class="form-wrapper">
|
||||
|
@ -239,11 +238,7 @@
|
|||
${self.order_form_grid()}
|
||||
</div>
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_this_page_component()">
|
||||
${parent.make_this_page_component()}
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
const OrderingWorksheet = {
|
||||
template: '#ordering-worksheet-template',
|
||||
|
@ -255,7 +250,7 @@
|
|||
submitting: false,
|
||||
|
||||
## TODO: should find a better way to handle CSRF token
|
||||
csrftoken: ${json.dumps(request.session.get_csrf_token() or request.session.new_csrf_token())|n},
|
||||
csrftoken: ${json.dumps(h.get_csrf_token(request))|n},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -298,14 +293,12 @@
|
|||
},
|
||||
}
|
||||
|
||||
Vue.component('ordering-worksheet', OrderingWorksheet)
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
##############################
|
||||
## page body
|
||||
##############################
|
||||
|
||||
${parent.body()}
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
<script>
|
||||
Vue.component('ordering-worksheet', OrderingWorksheet)
|
||||
</script>
|
||||
</%def>
|
||||
|
|
|
@ -1,42 +1,26 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="/base.mako" />
|
||||
|
||||
<%def name="context_menu_items()">
|
||||
% if context_menu_list_items is not Undefined:
|
||||
% for item in context_menu_list_items:
|
||||
<li>${item}</li>
|
||||
% endfor
|
||||
% endif
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
${self.render_vue_template_this_page()}
|
||||
</%def>
|
||||
|
||||
<%def name="page_content()"></%def>
|
||||
|
||||
<%def name="render_this_page()">
|
||||
<div style="display: flex;">
|
||||
|
||||
<div class="this-page-content" style="flex-grow: 1;">
|
||||
${self.page_content()}
|
||||
</div>
|
||||
|
||||
<ul id="context-menu">
|
||||
${self.context_menu_items()}
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
<%def name="render_vue_template_this_page()">
|
||||
## DEPRECATED; called for back-compat
|
||||
${self.render_this_page_template()}
|
||||
</%def>
|
||||
|
||||
<%def name="render_this_page_template()">
|
||||
<script type="text/x-template" id="this-page-template">
|
||||
<div>
|
||||
## DEPRECATED; called for back-compat
|
||||
${self.render_this_page()}
|
||||
</div>
|
||||
</script>
|
||||
</%def>
|
||||
<script>
|
||||
|
||||
<%def name="declare_this_page_vars()">
|
||||
<script type="text/javascript">
|
||||
|
||||
let ThisPage = {
|
||||
const ThisPage = {
|
||||
template: '#this-page-template',
|
||||
mixins: [SimpleRequestMixin],
|
||||
props: {
|
||||
|
@ -52,37 +36,71 @@
|
|||
},
|
||||
}
|
||||
|
||||
let ThisPageData = {
|
||||
const ThisPageData = {
|
||||
## TODO: should find a better way to handle CSRF token
|
||||
csrftoken: ${json.dumps(request.session.get_csrf_token() or request.session.new_csrf_token())|n},
|
||||
csrftoken: ${json.dumps(h.get_csrf_token(request))|n},
|
||||
}
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
## NOTE: if you override this, must use <script> tags
|
||||
## DEPRECATED; remains for back-compat
|
||||
<%def name="render_this_page()">
|
||||
<div style="display: flex;">
|
||||
|
||||
<div class="this-page-content" style="flex-grow: 1;">
|
||||
${self.page_content()}
|
||||
</div>
|
||||
|
||||
## DEPRECATED; remains for back-compat
|
||||
<ul id="context-menu">
|
||||
${self.context_menu_items()}
|
||||
</ul>
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="finalize_this_page_vars()">
|
||||
## NOTE: if you override this, must use <script> tags
|
||||
## nb. this is the canonical block for page content!
|
||||
<%def name="page_content()"></%def>
|
||||
|
||||
## DEPRECATED; remains for back-compat
|
||||
<%def name="context_menu_items()">
|
||||
% if context_menu_list_items is not Undefined:
|
||||
% for item in context_menu_list_items:
|
||||
<li>${item}</li>
|
||||
% endfor
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
|
||||
## DEPRECATED; called for back-compat
|
||||
${self.declare_this_page_vars()}
|
||||
${self.modify_this_page_vars()}
|
||||
</%def>
|
||||
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
|
||||
## DEPRECATED; called for back-compat
|
||||
${self.make_this_page_component()}
|
||||
</%def>
|
||||
|
||||
<%def name="make_this_page_component()">
|
||||
${self.declare_this_page_vars()}
|
||||
${self.modify_this_page_vars()}
|
||||
${self.finalize_this_page_vars()}
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
<script>
|
||||
ThisPage.data = function() { return ThisPageData }
|
||||
|
||||
Vue.component('this-page', ThisPage)
|
||||
<% request.register_component('this-page', 'ThisPage') %>
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
##############################
|
||||
## DEPRECATED
|
||||
##############################
|
||||
|
||||
${self.render_this_page_template()}
|
||||
${self.make_this_page_component()}
|
||||
<%def name="declare_this_page_vars()"></%def>
|
||||
|
||||
<%def name="modify_this_page_vars()"></%def>
|
||||
|
||||
<%def name="finalize_this_page_vars()"></%def>
|
||||
|
|
|
@ -61,9 +61,9 @@
|
|||
${parent.grid_tools()}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
% if getattr(master, 'mergeable', False) and master.has_perm('request_merge'):
|
||||
|
||||
|
@ -100,5 +100,3 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -18,10 +18,10 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
% if not instance.merged and request.has_perm('people.merge'):
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
ThisPageData.mergeFormButtonText = "Perform Merge"
|
||||
ThisPageData.mergeFormSubmitting = false
|
||||
|
@ -34,5 +34,3 @@
|
|||
</script>
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -2,6 +2,16 @@
|
|||
<%inherit file="/master/view.mako" />
|
||||
<%namespace file="/util.mako" import="view_profiles_helper" />
|
||||
|
||||
<%def name="page_content()">
|
||||
${parent.page_content()}
|
||||
% if not instance.users and request.has_perm('users.create'):
|
||||
${h.form(url('people.make_user'), ref='makeUserForm')}
|
||||
${h.csrf_token(request)}
|
||||
${h.hidden('person_uuid', value=instance.uuid)}
|
||||
${h.end_form()}
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="object_helpers()">
|
||||
${parent.object_helpers()}
|
||||
${view_profiles_helper([instance])}
|
||||
|
@ -13,9 +23,9 @@
|
|||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
${form.vue_component}.methods.clickMakeUser = function(event) {
|
||||
this.$emit('make-user')
|
||||
|
@ -29,17 +39,3 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="page_content()">
|
||||
${parent.page_content()}
|
||||
% if not instance.users and request.has_perm('users.create'):
|
||||
${h.form(url('people.make_user'), ref='makeUserForm')}
|
||||
${h.csrf_token(request)}
|
||||
${h.hidden('person_uuid', value=instance.uuid)}
|
||||
${h.end_form()}
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
||||
|
|
|
@ -1966,30 +1966,97 @@
|
|||
|
||||
</div>
|
||||
</script>
|
||||
</%def>
|
||||
<script>
|
||||
|
||||
<%def name="render_this_page_template()">
|
||||
${parent.render_this_page_template()}
|
||||
${self.render_personal_tab_template()}
|
||||
let ProfileInfoData = {
|
||||
activeTab: location.hash ? location.hash.substring(1) : 'personal',
|
||||
tabchecks: ${json.dumps(tabchecks or {})|n},
|
||||
today: '${rattail_app.today()}',
|
||||
profileLastChanged: Date.now(),
|
||||
person: ${json.dumps(person_data or {})|n},
|
||||
phoneTypeOptions: ${json.dumps(phone_type_options or [])|n},
|
||||
emailTypeOptions: ${json.dumps(email_type_options or [])|n},
|
||||
maxLengths: ${json.dumps(max_lengths or {})|n},
|
||||
|
||||
% if expose_members:
|
||||
${self.render_member_tab_template()}
|
||||
% endif
|
||||
% if request.has_perm('people_profile.view_versions'):
|
||||
loadingRevisions: false,
|
||||
showingRevisionDialog: false,
|
||||
revision: {},
|
||||
revisionShowAllFields: false,
|
||||
% endif
|
||||
}
|
||||
|
||||
${self.render_customer_tab_template()}
|
||||
% if expose_customer_shoppers:
|
||||
${self.render_shopper_tab_template()}
|
||||
% endif
|
||||
${self.render_employee_tab_template()}
|
||||
${self.render_notes_tab_template()}
|
||||
let ProfileInfo = {
|
||||
template: '#profile-info-template',
|
||||
props: {
|
||||
% if request.has_perm('people_profile.view_versions'):
|
||||
viewingHistory: Boolean,
|
||||
gettingRevisions: Boolean,
|
||||
revisions: Array,
|
||||
revisionVersionMap: null,
|
||||
% endif
|
||||
},
|
||||
computed: {},
|
||||
mounted() {
|
||||
|
||||
% if expose_transactions:
|
||||
${transactions_grid.render_complete(allow_save_defaults=False)|n}
|
||||
${self.render_transactions_tab_template()}
|
||||
% endif
|
||||
// auto-refresh whichever tab is shown first
|
||||
## TODO: how to not assume 'personal' is the default tab?
|
||||
let tab = this.$refs['tab_' + (this.activeTab || 'personal')]
|
||||
if (tab && tab.refreshTab) {
|
||||
tab.refreshTab()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
${self.render_user_tab_template()}
|
||||
${self.render_profile_info_template()}
|
||||
profileChanged(data) {
|
||||
this.$emit('change-content-title', data.person.dynamic_content_title)
|
||||
this.person = data.person
|
||||
this.tabchecks = data.tabchecks
|
||||
this.profileLastChanged = Date.now()
|
||||
},
|
||||
|
||||
activeTabChanged(value) {
|
||||
location.hash = value
|
||||
this.refreshTabIfNeeded(value)
|
||||
this.activeTabChangedExtra(value)
|
||||
},
|
||||
|
||||
refreshTabIfNeeded(key) {
|
||||
// TODO: this is *always* refreshing, should be more selective (?)
|
||||
let tab = this.$refs['tab_' + key]
|
||||
if (tab && tab.refreshIfNeeded) {
|
||||
tab.refreshIfNeeded(this.profileLastChanged)
|
||||
}
|
||||
},
|
||||
|
||||
activeTabChangedExtra(value) {},
|
||||
|
||||
% if request.has_perm('people_profile.view_versions'):
|
||||
|
||||
viewRevision(row) {
|
||||
this.revision = this.revisionVersionMap[row.txnid]
|
||||
this.showingRevisionDialog = true
|
||||
},
|
||||
|
||||
viewPrevRevision() {
|
||||
let txnid = this.revision.prev_txnid
|
||||
this.revision = this.revisionVersionMap[txnid]
|
||||
},
|
||||
|
||||
viewNextRevision() {
|
||||
let txnid = this.revision.next_txnid
|
||||
this.revision = this.revisionVersionMap[txnid]
|
||||
},
|
||||
|
||||
toggleVersionFields() {
|
||||
this.revisionShowAllFields = !this.revisionShowAllFields
|
||||
},
|
||||
|
||||
% endif
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="declare_personal_tab_vars()">
|
||||
|
@ -3022,114 +3089,46 @@
|
|||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="declare_profile_info_vars()">
|
||||
<script type="text/javascript">
|
||||
|
||||
let ProfileInfoData = {
|
||||
activeTab: location.hash ? location.hash.substring(1) : 'personal',
|
||||
tabchecks: ${json.dumps(tabchecks or {})|n},
|
||||
today: '${rattail_app.today()}',
|
||||
profileLastChanged: Date.now(),
|
||||
person: ${json.dumps(person_data or {})|n},
|
||||
phoneTypeOptions: ${json.dumps(phone_type_options or [])|n},
|
||||
emailTypeOptions: ${json.dumps(email_type_options or [])|n},
|
||||
maxLengths: ${json.dumps(max_lengths or {})|n},
|
||||
|
||||
% if request.has_perm('people_profile.view_versions'):
|
||||
loadingRevisions: false,
|
||||
showingRevisionDialog: false,
|
||||
revision: {},
|
||||
revisionShowAllFields: false,
|
||||
% endif
|
||||
}
|
||||
|
||||
let ProfileInfo = {
|
||||
template: '#profile-info-template',
|
||||
props: {
|
||||
% if request.has_perm('people_profile.view_versions'):
|
||||
viewingHistory: Boolean,
|
||||
gettingRevisions: Boolean,
|
||||
revisions: Array,
|
||||
revisionVersionMap: null,
|
||||
% endif
|
||||
},
|
||||
computed: {},
|
||||
mounted() {
|
||||
|
||||
// auto-refresh whichever tab is shown first
|
||||
## TODO: how to not assume 'personal' is the default tab?
|
||||
let tab = this.$refs['tab_' + (this.activeTab || 'personal')]
|
||||
if (tab && tab.refreshTab) {
|
||||
tab.refreshTab()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
profileChanged(data) {
|
||||
this.$emit('change-content-title', data.person.dynamic_content_title)
|
||||
this.person = data.person
|
||||
this.tabchecks = data.tabchecks
|
||||
this.profileLastChanged = Date.now()
|
||||
},
|
||||
|
||||
activeTabChanged(value) {
|
||||
location.hash = value
|
||||
this.refreshTabIfNeeded(value)
|
||||
this.activeTabChangedExtra(value)
|
||||
},
|
||||
|
||||
refreshTabIfNeeded(key) {
|
||||
// TODO: this is *always* refreshing, should be more selective (?)
|
||||
let tab = this.$refs['tab_' + key]
|
||||
if (tab && tab.refreshIfNeeded) {
|
||||
tab.refreshIfNeeded(this.profileLastChanged)
|
||||
}
|
||||
},
|
||||
|
||||
activeTabChangedExtra(value) {},
|
||||
|
||||
% if request.has_perm('people_profile.view_versions'):
|
||||
|
||||
viewRevision(row) {
|
||||
this.revision = this.revisionVersionMap[row.txnid]
|
||||
this.showingRevisionDialog = true
|
||||
},
|
||||
|
||||
viewPrevRevision() {
|
||||
let txnid = this.revision.prev_txnid
|
||||
this.revision = this.revisionVersionMap[txnid]
|
||||
},
|
||||
|
||||
viewNextRevision() {
|
||||
let txnid = this.revision.next_txnid
|
||||
this.revision = this.revisionVersionMap[txnid]
|
||||
},
|
||||
|
||||
toggleVersionFields() {
|
||||
this.revisionShowAllFields = !this.revisionShowAllFields
|
||||
},
|
||||
|
||||
% endif
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_profile_info_component()">
|
||||
${self.declare_profile_info_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
## DEPRECATED; called for back-compat
|
||||
${self.declare_profile_info_vars()}
|
||||
|
||||
<script>
|
||||
ProfileInfo.data = function() { return ProfileInfoData }
|
||||
Vue.component('profile-info', ProfileInfo)
|
||||
<% request.register_component('profile-info', 'ProfileInfo') %>
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
|
||||
${self.render_personal_tab_template()}
|
||||
|
||||
% if expose_members:
|
||||
${self.render_member_tab_template()}
|
||||
% endif
|
||||
|
||||
${self.render_customer_tab_template()}
|
||||
% if expose_customer_shoppers:
|
||||
${self.render_shopper_tab_template()}
|
||||
% endif
|
||||
${self.render_employee_tab_template()}
|
||||
${self.render_notes_tab_template()}
|
||||
|
||||
% if expose_transactions:
|
||||
${transactions_grid.render_complete(allow_save_defaults=False)|n}
|
||||
${self.render_transactions_tab_template()}
|
||||
% endif
|
||||
|
||||
${self.render_user_tab_template()}
|
||||
${self.render_profile_info_template()}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
% if request.has_perm('people_profile.view_versions'):
|
||||
ThisPage.props.viewingHistory = Boolean
|
||||
|
@ -3177,45 +3176,8 @@
|
|||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_this_page_component()">
|
||||
${parent.make_this_page_component()}
|
||||
${self.make_personal_tab_component()}
|
||||
|
||||
% if expose_members:
|
||||
${self.make_member_tab_component()}
|
||||
% endif
|
||||
|
||||
${self.make_customer_tab_component()}
|
||||
% if expose_customer_shoppers:
|
||||
${self.make_shopper_tab_component()}
|
||||
% endif
|
||||
${self.make_employee_tab_component()}
|
||||
${self.make_notes_tab_component()}
|
||||
|
||||
% if expose_transactions:
|
||||
<script type="text/javascript">
|
||||
|
||||
TransactionsGrid.data = function() { return TransactionsGridData }
|
||||
Vue.component('transactions-grid', TransactionsGrid)
|
||||
## TODO: why is this line not needed?
|
||||
## <% request.register_component('transactions-grid', 'TransactionsGrid') %>
|
||||
|
||||
</script>
|
||||
${self.make_transactions_tab_component()}
|
||||
% endif
|
||||
|
||||
${self.make_user_tab_component()}
|
||||
${self.make_profile_info_component()}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_whole_page_vars()">
|
||||
${parent.modify_whole_page_vars()}
|
||||
|
||||
% if request.has_perm('people_profile.view_versions'):
|
||||
<script type="text/javascript">
|
||||
% if request.has_perm('people_profile.view_versions'):
|
||||
|
||||
WholePageData.viewingHistory = false
|
||||
WholePageData.gettingRevisions = false
|
||||
|
@ -3251,9 +3213,44 @@
|
|||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
% endif
|
||||
% endif
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
|
||||
${parent.body()}
|
||||
${self.make_personal_tab_component()}
|
||||
|
||||
% if expose_members:
|
||||
${self.make_member_tab_component()}
|
||||
% endif
|
||||
|
||||
${self.make_customer_tab_component()}
|
||||
% if expose_customer_shoppers:
|
||||
${self.make_shopper_tab_component()}
|
||||
% endif
|
||||
${self.make_employee_tab_component()}
|
||||
${self.make_notes_tab_component()}
|
||||
|
||||
% if expose_transactions:
|
||||
<script type="text/javascript">
|
||||
|
||||
TransactionsGrid.data = function() { return TransactionsGridData }
|
||||
Vue.component('transactions-grid', TransactionsGrid)
|
||||
## TODO: why is this line not needed?
|
||||
## <% request.register_component('transactions-grid', 'TransactionsGrid') %>
|
||||
|
||||
</script>
|
||||
${self.make_transactions_tab_component()}
|
||||
% endif
|
||||
|
||||
${self.make_user_tab_component()}
|
||||
${self.make_profile_info_component()}
|
||||
</%def>
|
||||
|
||||
##############################
|
||||
## DEPRECATED
|
||||
##############################
|
||||
|
||||
<%def name="declare_profile_info_vars()"></%def>
|
||||
|
|
|
@ -62,19 +62,13 @@
|
|||
<br />
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
% if master.has_perm('replace'):
|
||||
<script type="text/javascript">
|
||||
|
||||
${form.vue_component}Data.showUploadForm = false
|
||||
|
||||
${form.vue_component}Data.uploadFile = null
|
||||
|
||||
${form.vue_component}Data.uploadSubmitting = false
|
||||
|
||||
</script>
|
||||
<script>
|
||||
${form.vue_component}Data.showUploadForm = false
|
||||
${form.vue_component}Data.uploadFile = null
|
||||
${form.vue_component}Data.uploadSubmitting = false
|
||||
</script>
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -118,14 +118,9 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
ThisPageData.setupSubmitting = false
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -10,8 +10,16 @@
|
|||
</find-principals>
|
||||
</%def>
|
||||
|
||||
<%def name="render_this_page_template()">
|
||||
${parent.render_this_page_template()}
|
||||
<%def name="principal_table()">
|
||||
<div
|
||||
style="width: 50%;"
|
||||
>
|
||||
${grid.render_table_element(data_prop='principalsData')|n}
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
<script type="text/x-template" id="find-principals-template">
|
||||
<div>
|
||||
|
||||
|
@ -90,28 +98,6 @@
|
|||
|
||||
</div>
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="principal_table()">
|
||||
<div
|
||||
style="width: 50%;"
|
||||
>
|
||||
${grid.render_table_element(data_prop='principalsData')|n}
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
ThisPageData.permissionGroups = ${json.dumps(perms_data)|n}
|
||||
ThisPageData.sortedGroups = ${json.dumps(sorted_groups_data)|n}
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_this_page_component()">
|
||||
${parent.make_this_page_component()}
|
||||
<script type="text/javascript">
|
||||
|
||||
const FindPrincipals = {
|
||||
|
@ -240,12 +226,21 @@
|
|||
}
|
||||
}
|
||||
|
||||
Vue.component('find-principals', FindPrincipals)
|
||||
|
||||
<% request.register_component('find-principals', 'FindPrincipals') %>
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
ThisPageData.permissionGroups = ${json.dumps(perms_data)|n}
|
||||
ThisPageData.sortedGroups = ${json.dumps(sorted_groups_data)|n}
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
<script>
|
||||
Vue.component('find-principals', FindPrincipals)
|
||||
<% request.register_component('find-principals', 'FindPrincipals') %>
|
||||
</script>
|
||||
</%def>
|
||||
|
|
|
@ -55,19 +55,20 @@
|
|||
</%def>
|
||||
|
||||
<%def name="render_form_template()">
|
||||
<script type="text/x-template" id="${form.component}-template">
|
||||
<script type="text/x-template" id="${form.vue_tagname}-template">
|
||||
${self.render_form_innards()}
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<% request.register_component(form.vue_tagname, form.vue_component) %>
|
||||
<script>
|
||||
|
||||
## TODO: ugh, an awful lot of duplicated code here (from /forms/deform.mako)
|
||||
|
||||
let ${form.vue_component} = {
|
||||
template: '#${form.component}-template',
|
||||
template: '#${form.vue_tagname}-template',
|
||||
methods: {
|
||||
|
||||
## TODO: deprecate / remove the latter option here
|
||||
|
@ -114,6 +115,3 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -95,9 +95,9 @@
|
|||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
ThisPage.methods.getTitleForKey = function(key) {
|
||||
switch (key) {
|
||||
|
@ -118,6 +118,3 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -36,10 +36,10 @@
|
|||
</${grid.component}>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
% if label_profiles and master.has_perm('print_labels'):
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
${grid.vue_component}Data.quickLabelProfile = ${json.dumps(label_profiles[0].uuid)|n}
|
||||
${grid.vue_component}Data.quickLabelQuantity = 1
|
||||
|
@ -83,6 +83,3 @@
|
|||
</script>
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -2,11 +2,6 @@
|
|||
<%inherit file="/master/view.mako" />
|
||||
<%namespace name="product_lookup" file="/products/lookup.mako" />
|
||||
|
||||
<%def name="render_this_page_template()">
|
||||
${parent.render_this_page_template()}
|
||||
${product_lookup.tailbone_product_lookup_template()}
|
||||
</%def>
|
||||
|
||||
<%def name="page_content()">
|
||||
${parent.page_content()}
|
||||
|
||||
|
@ -67,9 +62,14 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
${product_lookup.tailbone_product_lookup_template()}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
% if master.has_perm('ignore_product') and instance.status_code in (enum.PENDING_PRODUCT_STATUS_PENDING, enum.PENDING_PRODUCT_STATUS_READY):
|
||||
|
||||
|
@ -124,10 +124,7 @@
|
|||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_this_page_component()">
|
||||
${parent.make_this_page_component()}
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
${product_lookup.tailbone_product_lookup_component()}
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -282,9 +282,9 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
ThisPageData.vendorSourcesData = ${json.dumps(vendor_sources['data'])|n}
|
||||
ThisPageData.lookupCodesData = ${json.dumps(lookup_codes['data'])|n}
|
||||
|
@ -411,6 +411,3 @@
|
|||
% endif
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -59,9 +59,9 @@
|
|||
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
${grid.vue_component}Data.changeStatusShowDialog = false
|
||||
${grid.vue_component}Data.changeStatusOptions = ${json.dumps(status_options)|n}
|
||||
|
@ -80,6 +80,3 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -69,12 +69,12 @@
|
|||
<h3 class="block is-size-3">Vendors</h3>
|
||||
<div class="block" style="padding-left: 2rem;">
|
||||
|
||||
<b-field message="If set, user must choose a "supported" vendor; otherwise they may choose "any" vendor.">
|
||||
<b-checkbox name="rattail.batch.purchase.supported_vendors_only"
|
||||
v-model="simpleSettings['rattail.batch.purchase.supported_vendors_only']"
|
||||
<b-field message="If not set, user must choose a "supported" vendor.">
|
||||
<b-checkbox name="rattail.batch.purchase.allow_receiving_any_vendor"
|
||||
v-model="simpleSettings['rattail.batch.purchase.allow_receiving_any_vendor']"
|
||||
native-value="true"
|
||||
@input="settingsNeedSaved = true">
|
||||
Only allow batch for "supported" vendors
|
||||
Allow receiving for <span class="has-text-weight-bold">any</span> vendor
|
||||
</b-checkbox>
|
||||
</b-field>
|
||||
|
||||
|
|
|
@ -139,9 +139,15 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="render_this_page_template()">
|
||||
${parent.render_this_page_template()}
|
||||
<%def name="object_helpers()">
|
||||
${self.render_status_breakdown()}
|
||||
${self.render_po_vs_invoice_helper()}
|
||||
${self.render_execute_helper()}
|
||||
${self.render_tools_helper()}
|
||||
</%def>
|
||||
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
% if allow_edit_catalog_unit_cost or allow_edit_invoice_unit_cost:
|
||||
<script type="text/x-template" id="receiving-cost-editor-template">
|
||||
<div>
|
||||
|
@ -162,16 +168,9 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="object_helpers()">
|
||||
${self.render_status_breakdown()}
|
||||
${self.render_po_vs_invoice_helper()}
|
||||
${self.render_execute_helper()}
|
||||
${self.render_tools_helper()}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
% if allow_confirm_all_costs:
|
||||
|
||||
|
@ -389,6 +388,3 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -484,9 +484,9 @@
|
|||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
## ThisPage.methods.editUnitCost = function() {
|
||||
## alert("TODO: not yet implemented")
|
||||
|
@ -720,6 +720,3 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -53,13 +53,13 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
TailboneFormData.reportDescriptions = ${json.dumps(report_descriptions)|n}
|
||||
${form.vue_component}Data.reportDescriptions = ${json.dumps(report_descriptions)|n}
|
||||
|
||||
TailboneForm.methods.reportTypeChanged = function(reportType) {
|
||||
${form.vue_component}.methods.reportTypeChanged = function(reportType) {
|
||||
this.$emit('report-change', this.reportDescriptions[reportType])
|
||||
}
|
||||
|
||||
|
@ -71,6 +71,3 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -1,16 +1,11 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="/master/delete.mako" />
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
% if params_data is not Undefined:
|
||||
${form.vue_component}Data.paramsData = ${json.dumps(params_data)|n}
|
||||
% endif
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -23,16 +23,11 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
% if params_data is not Undefined:
|
||||
${form.vue_component}Data.paramsData = ${json.dumps(params_data)|n}
|
||||
% endif
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -48,15 +48,10 @@
|
|||
${h.end_form()}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
ThisPageData.departments = ${json.dumps([{'uuid': d.uuid, 'name': d.name} for d in departments])|n}
|
||||
ThisPageData.excludeNotForSale = true
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -81,9 +81,9 @@
|
|||
|
||||
<%def name="extra_fields()"></%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
ThisPageData.vendorUUID = null
|
||||
ThisPageData.departments = []
|
||||
|
@ -127,6 +127,3 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -45,11 +45,10 @@
|
|||
<b-button @click="runReportShowDialog = false">
|
||||
Cancel
|
||||
</b-button>
|
||||
${h.form(master.get_action_url('execute', instance))}
|
||||
${h.form(master.get_action_url('execute', instance), **{'@submit': 'runReportSubmitting = true'})}
|
||||
${h.csrf_token(request)}
|
||||
<b-button type="is-primary"
|
||||
native-type="submit"
|
||||
@click="runReportSubmitting = true"
|
||||
:disabled="runReportSubmitting"
|
||||
icon-pack="fas"
|
||||
icon-left="arrow-circle-right">
|
||||
|
@ -62,9 +61,9 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
% if weekdays_data is not Undefined:
|
||||
${form.vue_component}Data.weekdaysData = ${json.dumps(weekdays_data)|n}
|
||||
|
@ -75,6 +74,3 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -6,15 +6,11 @@
|
|||
${h.stylesheet_link(request.static_url('tailbone:static/css/perms.css'))}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
// TODO: this variable name should be more dynamic (?) since this is
|
||||
// connected to (and only here b/c of) the permissions field
|
||||
TailboneFormData.showingPermissionGroup = ''
|
||||
|
||||
${form.vue_component}Data.showingPermissionGroup = ''
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -6,15 +6,11 @@
|
|||
${h.stylesheet_link(request.static_url('tailbone:static/css/perms.css'))}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
// TODO: this variable name should be more dynamic (?) since this is
|
||||
// connected to (and only here b/c of) the permissions field
|
||||
TailboneFormData.showingPermissionGroup = ''
|
||||
|
||||
${form.vue_component}Data.showingPermissionGroup = ''
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
${h.stylesheet_link(request.static_url('tailbone:static/css/perms.css'))}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
% if users_data is not Undefined:
|
||||
${form.vue_component}Data.usersData = ${json.dumps(users_data)|n}
|
||||
|
@ -23,5 +23,3 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -86,9 +86,9 @@
|
|||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
ThisPageData.testRecipient = ${json.dumps(user_email_address)|n}
|
||||
ThisPageData.sendingTest = false
|
||||
|
@ -137,6 +137,3 @@
|
|||
% endif
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -15,10 +15,10 @@
|
|||
${parent.render_grid_component()}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
% if master.has_perm('configure'):
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
ThisPageData.showEmails = 'available'
|
||||
|
||||
|
@ -65,5 +65,3 @@
|
|||
</script>
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
<email-preview-tools></email-preview-tools>
|
||||
</%def>
|
||||
|
||||
<%def name="render_this_page_template()">
|
||||
${parent.render_this_page_template()}
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
<script type="text/x-template" id="email-preview-tools-template">
|
||||
|
||||
${h.form(url('email.preview'), **{'@submit': 'submitPreviewForm'})}
|
||||
|
@ -72,10 +72,6 @@
|
|||
|
||||
${h.end_form()}
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_this_page_component()">
|
||||
${parent.make_this_page_component()}
|
||||
<script type="text/javascript">
|
||||
|
||||
const EmailPreviewTools = {
|
||||
|
@ -100,12 +96,13 @@
|
|||
}
|
||||
}
|
||||
|
||||
Vue.component('email-preview-tools', EmailPreviewTools)
|
||||
|
||||
<% request.register_component('email-preview-tools', 'EmailPreviewTools') %>
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
<script>
|
||||
Vue.component('email-preview-tools', EmailPreviewTools)
|
||||
<% request.register_component('email-preview-tools', 'EmailPreviewTools') %>
|
||||
</script>
|
||||
</%def>
|
||||
|
|
|
@ -695,9 +695,9 @@
|
|||
</b-steps>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
// nb. for warning user they may lose changes if leaving page
|
||||
ThisPageData.dirty = false
|
||||
|
@ -983,6 +983,3 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -8,14 +8,9 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
${form.vue_component}Data.probesData = ${json.dumps(probes_data)|n}
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -22,14 +22,9 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
${form.vue_component}Data.probesData = ${json.dumps(probes_data)|n}
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -59,9 +59,9 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
ThisPageData.appliances = ${json.dumps(appliances_data)|n}
|
||||
ThisPageData.applianceUUID = ${json.dumps(appliance.uuid if appliance else None)|n}
|
||||
|
@ -118,6 +118,3 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -66,9 +66,9 @@
|
|||
<canvas ref="tempchart" width="400" height="150"></canvas>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
ThisPageData.currentTimeRange = ${json.dumps(current_time_range)|n}
|
||||
ThisPageData.chart = null
|
||||
|
@ -128,6 +128,3 @@
|
|||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -20,38 +20,21 @@
|
|||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app" style="height: 100%; display: flex; flex-direction: column; justify-content: space-between;">
|
||||
<div id="app" style="height: 100%;">
|
||||
<whole-page></whole-page>
|
||||
</div>
|
||||
|
||||
## TODO: this must come before the self.body() call..but why?
|
||||
${declare_formposter_mixin()}
|
||||
|
||||
## global components used by various (but not all) pages
|
||||
${make_field_components()}
|
||||
${make_grid_filter_components()}
|
||||
|
||||
## global components for buefy-based template compatibility
|
||||
${make_http_plugin()}
|
||||
${make_buefy_plugin()}
|
||||
${make_buefy_components()}
|
||||
|
||||
## special global components, used by WholePage
|
||||
${self.make_menu_search_component()}
|
||||
${page_help.render_template()}
|
||||
${page_help.declare_vars()}
|
||||
% if request.has_perm('common.feedback'):
|
||||
${self.make_feedback_component()}
|
||||
% endif
|
||||
|
||||
## WholePage component
|
||||
${self.make_whole_page_component()}
|
||||
|
||||
## content body from derived/child template
|
||||
${self.body()}
|
||||
|
||||
## Vue app
|
||||
${self.make_whole_page_app()}
|
||||
${self.render_vue_templates()}
|
||||
${self.modify_vue_vars()}
|
||||
${self.make_vue_components()}
|
||||
${self.make_vue_app()}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
@ -596,7 +579,7 @@
|
|||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="render_whole_page_template()">
|
||||
<%def name="render_vue_template_whole_page()">
|
||||
<script type="text/x-template" id="whole-page-template">
|
||||
<div id="whole-page" style="height: 100%; display: flex; flex-direction: column; justify-content: space-between;">
|
||||
|
||||
|
@ -896,8 +879,6 @@
|
|||
</footer>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
## ${multi_file_upload.render_template()}
|
||||
</%def>
|
||||
|
||||
<%def name="render_this_page_component()">
|
||||
|
@ -928,7 +909,7 @@
|
|||
${h.form(url('stop_root'), ref='stopBeingRootForm')}
|
||||
${h.csrf_token(request)}
|
||||
<input type="hidden" name="referrer" value="${request.current_route_url()}" />
|
||||
<a @click="stopBeingRoot()"
|
||||
<a @click="$refs.stopBeingRootForm.submit()"
|
||||
class="navbar-item has-background-danger has-text-white">
|
||||
Stop being root
|
||||
</a>
|
||||
|
@ -937,7 +918,7 @@
|
|||
${h.form(url('become_root'), ref='startBeingRootForm')}
|
||||
${h.csrf_token(request)}
|
||||
<input type="hidden" name="referrer" value="${request.current_route_url()}" />
|
||||
<a @click="startBeingRoot()"
|
||||
<a @click="$refs.startBeingRootForm.submit()"
|
||||
class="navbar-item has-background-danger has-text-white">
|
||||
Become root
|
||||
</a>
|
||||
|
@ -1068,9 +1049,7 @@
|
|||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="declare_whole_page_vars()">
|
||||
## ${multi_file_upload.declare_vars()}
|
||||
|
||||
<%def name="render_vue_script_whole_page()">
|
||||
<script>
|
||||
|
||||
const WholePage = {
|
||||
|
@ -1124,18 +1103,6 @@
|
|||
const key = 'menu_' + hash + '_shown'
|
||||
this[key] = !this[key]
|
||||
},
|
||||
|
||||
% if request.is_admin:
|
||||
|
||||
startBeingRoot() {
|
||||
this.$refs.startBeingRootForm.submit()
|
||||
},
|
||||
|
||||
stopBeingRoot() {
|
||||
this.$refs.stopBeingRootForm.submit()
|
||||
},
|
||||
|
||||
% endif
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -1172,26 +1139,71 @@
|
|||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="modify_whole_page_vars()"></%def>
|
||||
##############################
|
||||
## vue components + app
|
||||
##############################
|
||||
|
||||
## TODO: do we really need this?
|
||||
## <%def name="finalize_whole_page_vars()"></%def>
|
||||
<%def name="render_vue_templates()">
|
||||
## ${multi_file_upload.render_template()}
|
||||
## ${multi_file_upload.declare_vars()}
|
||||
|
||||
<%def name="make_whole_page_component()">
|
||||
## global components used by various (but not all) pages
|
||||
${make_field_components()}
|
||||
${make_grid_filter_components()}
|
||||
|
||||
## global components for buefy-based template compatibility
|
||||
${make_http_plugin()}
|
||||
${make_buefy_plugin()}
|
||||
${make_buefy_components()}
|
||||
|
||||
## special global components, used by WholePage
|
||||
${self.make_menu_search_component()}
|
||||
${page_help.render_template()}
|
||||
${page_help.declare_vars()}
|
||||
% if request.has_perm('common.feedback'):
|
||||
${self.make_feedback_component()}
|
||||
% endif
|
||||
|
||||
## DEPRECATED; called for back-compat
|
||||
${self.render_whole_page_template()}
|
||||
|
||||
## DEPRECATED; called for back-compat
|
||||
${self.declare_whole_page_vars()}
|
||||
</%def>
|
||||
|
||||
## DEPRECATED; remains for back-compat
|
||||
<%def name="render_whole_page_template()">
|
||||
${self.render_vue_template_whole_page()}
|
||||
${self.render_vue_script_whole_page()}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
## DEPRECATED; called for back-compat
|
||||
${self.modify_whole_page_vars()}
|
||||
## ${self.finalize_whole_page_vars()}
|
||||
</%def>
|
||||
|
||||
<%def name="make_vue_components()">
|
||||
${page_help.make_component()}
|
||||
## ${multi_file_upload.make_component()}
|
||||
## ${multi_file_upload.make_component()}
|
||||
|
||||
## DEPRECATED; called for back-compat (?)
|
||||
${self.make_whole_page_component()}
|
||||
</%def>
|
||||
|
||||
## DEPRECATED; remains for back-compat
|
||||
<%def name="make_whole_page_component()">
|
||||
<script>
|
||||
WholePage.data = () => { return WholePageData }
|
||||
</script>
|
||||
<% request.register_component('whole-page', 'WholePage') %>
|
||||
</%def>
|
||||
|
||||
<%def name="make_vue_app()">
|
||||
## DEPRECATED; called for back-compat
|
||||
${self.make_whole_page_app()}
|
||||
</%def>
|
||||
|
||||
## DEPRECATED; remains for back-compat
|
||||
<%def name="make_whole_page_app()">
|
||||
<script type="module">
|
||||
import {createApp} from 'vue'
|
||||
|
@ -1223,3 +1235,11 @@
|
|||
app.mount('#app')
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
##############################
|
||||
## DEPRECATED
|
||||
##############################
|
||||
|
||||
<%def name="declare_whole_page_vars()"></%def>
|
||||
|
||||
<%def name="modify_whole_page_vars()"></%def>
|
||||
|
|
|
@ -666,6 +666,7 @@
|
|||
<%def name="make_b_tooltip_component()">
|
||||
<script type="text/x-template" id="b-tooltip-template">
|
||||
<o-tooltip :label="label"
|
||||
:position="orugaPosition"
|
||||
:multiline="multilined">
|
||||
<slot />
|
||||
</o-tooltip>
|
||||
|
@ -676,6 +677,14 @@
|
|||
props: {
|
||||
label: String,
|
||||
multilined: Boolean,
|
||||
position: String,
|
||||
},
|
||||
computed: {
|
||||
orugaPosition() {
|
||||
if (this.position) {
|
||||
return this.position.replace(/^is-/, '')
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
504
tailbone/templates/themes/waterpark/base.mako
Normal file
504
tailbone/templates/themes/waterpark/base.mako
Normal file
|
@ -0,0 +1,504 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="wuttaweb:templates/base.mako" />
|
||||
<%namespace name="base_meta" file="/base_meta.mako" />
|
||||
<%namespace file="/formposter.mako" import="declare_formposter_mixin" />
|
||||
<%namespace file="/grids/filter-components.mako" import="make_grid_filter_components" />
|
||||
<%namespace name="page_help" file="/page_help.mako" />
|
||||
|
||||
<%def name="base_styles()">
|
||||
${parent.base_styles()}
|
||||
${h.stylesheet_link(request.static_url('tailbone:static/css/diffs.css') + '?ver={}'.format(tailbone.__version__))}
|
||||
<style>
|
||||
|
||||
.filters .filter-fieldname .field,
|
||||
.filters .filter-fieldname .field label {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.filters .filter-fieldname,
|
||||
.filters .filter-fieldname .field label,
|
||||
.filters .filter-fieldname .button {
|
||||
justify-content: left;
|
||||
}
|
||||
|
||||
.filters .filter-verb .select,
|
||||
.filters .filter-verb .select select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
% if filter_fieldname_width is not Undefined:
|
||||
|
||||
.filters .filter-fieldname,
|
||||
.filters .filter-fieldname .button {
|
||||
min-width: ${filter_fieldname_width};
|
||||
}
|
||||
|
||||
.filters .filter-verb {
|
||||
min-width: ${filter_verb_width};
|
||||
}
|
||||
|
||||
% endif
|
||||
|
||||
</style>
|
||||
</%def>
|
||||
|
||||
<%def name="before_content()">
|
||||
## TODO: this must come before the self.body() call..but why?
|
||||
${declare_formposter_mixin()}
|
||||
</%def>
|
||||
|
||||
<%def name="render_navbar_brand()">
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item" href="${url('home')}"
|
||||
v-show="!menuSearchActive">
|
||||
<div style="display: flex; align-items: center;">
|
||||
${base_meta.header_logo()}
|
||||
<div id="navbar-brand-title">
|
||||
${base_meta.global_title()}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<div v-show="menuSearchActive"
|
||||
class="navbar-item">
|
||||
<b-autocomplete ref="menuSearchAutocomplete"
|
||||
v-model="menuSearchTerm"
|
||||
:data="menuSearchFilteredData"
|
||||
field="label"
|
||||
open-on-focus
|
||||
keep-first
|
||||
icon-pack="fas"
|
||||
clearable
|
||||
@keydown.native="menuSearchKeydown"
|
||||
@select="menuSearchSelect">
|
||||
</b-autocomplete>
|
||||
</div>
|
||||
<a role="button" class="navbar-burger" data-target="navbar-menu" aria-label="menu" aria-expanded="false">
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</a>
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="render_navbar_start()">
|
||||
<div class="navbar-start">
|
||||
|
||||
<div v-if="menuSearchData.length"
|
||||
class="navbar-item">
|
||||
<b-button type="is-primary"
|
||||
size="is-small"
|
||||
@click="menuSearchInit()">
|
||||
<span><i class="fa fa-search"></i></span>
|
||||
</b-button>
|
||||
</div>
|
||||
|
||||
% for topitem in menus:
|
||||
% if topitem['is_link']:
|
||||
${h.link_to(topitem['title'], topitem['url'], target=topitem['target'], class_='navbar-item')}
|
||||
% else:
|
||||
<div class="navbar-item has-dropdown is-hoverable">
|
||||
<a class="navbar-link">${topitem['title']}</a>
|
||||
<div class="navbar-dropdown">
|
||||
% for item in topitem['items']:
|
||||
% if item['is_menu']:
|
||||
<% item_hash = id(item) %>
|
||||
<% toggle = 'menu_{}_shown'.format(item_hash) %>
|
||||
<div>
|
||||
<a class="navbar-link" @click.prevent="toggleNestedMenu('${item_hash}')">
|
||||
${item['title']}
|
||||
</a>
|
||||
</div>
|
||||
% for subitem in item['items']:
|
||||
% if subitem['is_sep']:
|
||||
<hr class="navbar-divider" v-show="${toggle}">
|
||||
% else:
|
||||
${h.link_to("{}".format(subitem['title']), subitem['url'], class_='navbar-item nested', target=subitem['target'], **{'v-show': toggle})}
|
||||
% endif
|
||||
% endfor
|
||||
% else:
|
||||
% if item['is_sep']:
|
||||
<hr class="navbar-divider">
|
||||
% else:
|
||||
${h.link_to(item['title'], item['url'], class_='navbar-item', target=item['target'])}
|
||||
% endif
|
||||
% endif
|
||||
% endfor
|
||||
</div>
|
||||
</div>
|
||||
% endif
|
||||
% endfor
|
||||
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="render_theme_picker()">
|
||||
% if expose_theme_picker and request.has_perm('common.change_app_theme'):
|
||||
<div class="level-item">
|
||||
${h.form(url('change_theme'), method="post", ref='themePickerForm')}
|
||||
${h.csrf_token(request)}
|
||||
<input type="hidden" name="referrer" :value="referrer" />
|
||||
<div style="display: flex; align-items: center; gap: 0.5rem;">
|
||||
<span>Theme:</span>
|
||||
<b-select name="theme"
|
||||
v-model="globalTheme"
|
||||
@input="changeTheme()">
|
||||
% for option in theme_picker_options:
|
||||
<option value="${option.value}">
|
||||
${option.label}
|
||||
</option>
|
||||
% endfor
|
||||
</b-select>
|
||||
</div>
|
||||
${h.end_form()}
|
||||
</div>
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="render_feedback_button()">
|
||||
|
||||
<div class="level-item">
|
||||
<page-help
|
||||
% if can_edit_help:
|
||||
@configure-fields-help="configureFieldsHelp = true"
|
||||
% endif
|
||||
/>
|
||||
</div>
|
||||
|
||||
${parent.render_feedback_button()}
|
||||
</%def>
|
||||
|
||||
<%def name="render_crud_header_buttons()">
|
||||
% if master:
|
||||
% if master.viewing:
|
||||
% if instance_editable and master.has_perm('edit'):
|
||||
<wutta-button once
|
||||
tag="a" href="${master.get_action_url('edit', instance)}"
|
||||
icon-left="edit"
|
||||
label="Edit This" />
|
||||
% endif
|
||||
% if getattr(master, 'cloneable', False) and not master.cloning and master.has_perm('clone'):
|
||||
<wutta-button once
|
||||
tag="a" href="${master.get_action_url('clone', instance)}"
|
||||
icon-left="object-ungroup"
|
||||
label="Clone This" />
|
||||
% endif
|
||||
% if instance_deletable and master.has_perm('delete'):
|
||||
<wutta-button once type="is-danger"
|
||||
tag="a" href="${master.get_action_url('delete', instance)}"
|
||||
icon-left="trash"
|
||||
label="Delete This" />
|
||||
% endif
|
||||
% elif master.editing:
|
||||
% if master.has_perm('view'):
|
||||
<wutta-button once
|
||||
tag="a" href="${master.get_action_url('view', instance)}"
|
||||
icon-left="eye"
|
||||
label="View This" />
|
||||
% endif
|
||||
% if instance_deletable and master.has_perm('delete'):
|
||||
<wutta-button once type="is-danger"
|
||||
tag="a" href="${master.get_action_url('delete', instance)}"
|
||||
icon-left="trash"
|
||||
label="Delete This" />
|
||||
% endif
|
||||
% elif master.deleting:
|
||||
% if master.has_perm('view'):
|
||||
<wutta-button once
|
||||
tag="a" href="${master.get_action_url('view', instance)}"
|
||||
icon-left="eye"
|
||||
label="View This" />
|
||||
% endif
|
||||
% if instance_editable and master.has_perm('edit'):
|
||||
<wutta-button once
|
||||
tag="a" href="${master.get_action_url('edit', instance)}"
|
||||
icon-left="edit"
|
||||
label="Edit This" />
|
||||
% endif
|
||||
% endif
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="render_prevnext_header_buttons()">
|
||||
% if show_prev_next is not Undefined and show_prev_next:
|
||||
% if prev_url:
|
||||
<wutta-button once
|
||||
tag="a" href="${prev_url}"
|
||||
icon-left="arrow-left"
|
||||
label="Older" />
|
||||
% else:
|
||||
<b-button tag="a" href="#"
|
||||
disabled
|
||||
icon-pack="fas"
|
||||
icon-left="arrow-left">
|
||||
Older
|
||||
</b-button>
|
||||
% endif
|
||||
% if next_url:
|
||||
<wutta-button once
|
||||
tag="a" href="${next_url}"
|
||||
icon-left="arrow-right"
|
||||
label="Newer" />
|
||||
% else:
|
||||
<b-button tag="a" href="#"
|
||||
disabled
|
||||
icon-pack="fas"
|
||||
icon-left="arrow-right">
|
||||
Newer
|
||||
</b-button>
|
||||
% endif
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="render_this_page_component()">
|
||||
<this-page @change-content-title="changeContentTitle"
|
||||
% if can_edit_help:
|
||||
:configure-fields-help="configureFieldsHelp"
|
||||
% endif
|
||||
/>
|
||||
</%def>
|
||||
|
||||
<%def name="render_vue_template_feedback()">
|
||||
<script type="text/x-template" id="feedback-template">
|
||||
<div>
|
||||
|
||||
<div class="level-item">
|
||||
<b-button type="is-primary"
|
||||
@click="showFeedback()"
|
||||
icon-pack="fas"
|
||||
icon-left="comment">
|
||||
Feedback
|
||||
</b-button>
|
||||
</div>
|
||||
|
||||
<b-modal has-modal-card
|
||||
:active.sync="showDialog">
|
||||
<div class="modal-card">
|
||||
|
||||
<header class="modal-card-head">
|
||||
<p class="modal-card-title">User Feedback</p>
|
||||
</header>
|
||||
|
||||
<section class="modal-card-body">
|
||||
<p class="block">
|
||||
Questions, suggestions, comments, complaints, etc.
|
||||
<span class="red">regarding this website</span> are
|
||||
welcome and may be submitted below.
|
||||
</p>
|
||||
|
||||
<b-field label="User Name">
|
||||
<b-input v-model="userName"
|
||||
% if request.user:
|
||||
disabled
|
||||
% endif
|
||||
>
|
||||
</b-input>
|
||||
</b-field>
|
||||
|
||||
<b-field label="Referring URL">
|
||||
<b-input
|
||||
v-model="referrer"
|
||||
disabled="true">
|
||||
</b-input>
|
||||
</b-field>
|
||||
|
||||
<b-field label="Message">
|
||||
<b-input type="textarea"
|
||||
v-model="message"
|
||||
ref="textarea">
|
||||
</b-input>
|
||||
</b-field>
|
||||
|
||||
% if config.get_bool('tailbone.feedback_allows_reply'):
|
||||
<div class="level">
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<b-checkbox v-model="pleaseReply"
|
||||
@input="pleaseReplyChanged">
|
||||
Please email me back{{ pleaseReply ? " at: " : "" }}
|
||||
</b-checkbox>
|
||||
</div>
|
||||
<div class="level-item" v-show="pleaseReply">
|
||||
<b-input v-model="userEmail"
|
||||
ref="userEmail">
|
||||
</b-input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
% endif
|
||||
|
||||
</section>
|
||||
|
||||
<footer class="modal-card-foot">
|
||||
<b-button @click="showDialog = false">
|
||||
Cancel
|
||||
</b-button>
|
||||
<b-button type="is-primary"
|
||||
icon-pack="fas"
|
||||
icon-left="paper-plane"
|
||||
@click="sendFeedback()"
|
||||
:disabled="sendingFeedback || !message || !message.trim()">
|
||||
{{ sendingFeedback ? "Working, please wait..." : "Send Message" }}
|
||||
</b-button>
|
||||
</footer>
|
||||
</div>
|
||||
</b-modal>
|
||||
|
||||
</div>
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="render_vue_script_feedback()">
|
||||
${parent.render_vue_script_feedback()}
|
||||
<script>
|
||||
|
||||
WuttaFeedbackForm.template = '#feedback-template'
|
||||
WuttaFeedbackForm.props.message = String
|
||||
|
||||
% if config.get_bool('tailbone.feedback_allows_reply'):
|
||||
|
||||
WuttaFeedbackFormData.pleaseReply = false
|
||||
WuttaFeedbackFormData.userEmail = null
|
||||
|
||||
WuttaFeedbackForm.methods.pleaseReplyChanged = function(value) {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.userEmail.focus()
|
||||
})
|
||||
}
|
||||
|
||||
WuttaFeedbackForm.methods.getExtraParams = function() {
|
||||
return {
|
||||
please_reply_to: this.pleaseReply ? this.userEmail : null,
|
||||
}
|
||||
}
|
||||
|
||||
% endif
|
||||
|
||||
// TODO: deprecate / remove these
|
||||
const FeedbackForm = WuttaFeedbackForm
|
||||
const FeedbackFormData = WuttaFeedbackFormData
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
${page_help.render_template()}
|
||||
${page_help.declare_vars()}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
##############################
|
||||
## menu search
|
||||
##############################
|
||||
|
||||
WholePageData.menuSearchActive = false
|
||||
WholePageData.menuSearchTerm = ''
|
||||
WholePageData.menuSearchData = ${json.dumps(global_search_data or [])|n}
|
||||
|
||||
WholePage.computed.menuSearchFilteredData = function() {
|
||||
if (!this.menuSearchTerm.length) {
|
||||
return this.menuSearchData
|
||||
}
|
||||
|
||||
const terms = []
|
||||
for (let term of this.menuSearchTerm.toLowerCase().split(' ')) {
|
||||
term = term.trim()
|
||||
if (term) {
|
||||
terms.push(term)
|
||||
}
|
||||
}
|
||||
if (!terms.length) {
|
||||
return this.menuSearchData
|
||||
}
|
||||
|
||||
// all terms must match
|
||||
return this.menuSearchData.filter((option) => {
|
||||
const label = option.label.toLowerCase()
|
||||
for (const term of terms) {
|
||||
if (label.indexOf(term) < 0) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
WholePage.methods.globalKey = function(event) {
|
||||
|
||||
// Ctrl+8 opens menu search
|
||||
if (event.target.tagName == 'BODY') {
|
||||
if (event.ctrlKey && event.key == '8') {
|
||||
this.menuSearchInit()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WholePage.mounted = function() {
|
||||
window.addEventListener('keydown', this.globalKey)
|
||||
for (let hook of this.mountedHooks) {
|
||||
hook(this)
|
||||
}
|
||||
}
|
||||
|
||||
WholePage.beforeDestroy = function() {
|
||||
window.removeEventListener('keydown', this.globalKey)
|
||||
}
|
||||
|
||||
WholePage.methods.menuSearchInit = function() {
|
||||
this.menuSearchTerm = ''
|
||||
this.menuSearchActive = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs.menuSearchAutocomplete.focus()
|
||||
})
|
||||
}
|
||||
|
||||
WholePage.methods.menuSearchKeydown = function(event) {
|
||||
|
||||
// ESC will dismiss searchbox
|
||||
if (event.which == 27) {
|
||||
this.menuSearchActive = false
|
||||
}
|
||||
}
|
||||
|
||||
WholePage.methods.menuSearchSelect = function(option) {
|
||||
location.href = option.url
|
||||
}
|
||||
|
||||
##############################
|
||||
## theme picker
|
||||
##############################
|
||||
|
||||
% if expose_theme_picker and request.has_perm('common.change_app_theme'):
|
||||
|
||||
WholePageData.globalTheme = ${json.dumps(theme or None)|n}
|
||||
## WholePageData.referrer = location.href
|
||||
|
||||
WholePage.methods.changeTheme = function() {
|
||||
this.$refs.themePickerForm.submit()
|
||||
}
|
||||
|
||||
% endif
|
||||
|
||||
##############################
|
||||
## edit fields help
|
||||
##############################
|
||||
|
||||
% if can_edit_help:
|
||||
WholePageData.configureFieldsHelp = false
|
||||
% endif
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
${h.javascript_link(request.static_url('tailbone:static/js/tailbone.buefy.datepicker.js') + f'?ver={tailbone.__version__}')}
|
||||
${h.javascript_link(request.static_url('tailbone:static/js/tailbone.buefy.numericinput.js') + f'?ver={tailbone.__version__}')}
|
||||
${h.javascript_link(request.static_url('tailbone:static/js/tailbone.buefy.oncebutton.js') + f'?ver={tailbone.__version__}')}
|
||||
${h.javascript_link(request.static_url('tailbone:static/js/tailbone.buefy.timepicker.js') + f'?ver={tailbone.__version__}')}
|
||||
${make_grid_filter_components()}
|
||||
${page_help.make_component()}
|
||||
</%def>
|
78
tailbone/templates/themes/waterpark/configure.mako
Normal file
78
tailbone/templates/themes/waterpark/configure.mako
Normal file
|
@ -0,0 +1,78 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="wuttaweb:templates/configure.mako" />
|
||||
<%namespace name="tailbone_base" file="tailbone:templates/configure.mako" />
|
||||
|
||||
<%def name="input_file_templates_section()">
|
||||
${tailbone_base.input_file_templates_section()}
|
||||
</%def>
|
||||
|
||||
<%def name="output_file_templates_section()">
|
||||
${tailbone_base.output_file_templates_section()}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
##############################
|
||||
## input file templates
|
||||
##############################
|
||||
|
||||
% if input_file_template_settings is not Undefined:
|
||||
|
||||
ThisPageData.inputFileTemplateSettings = ${json.dumps(input_file_template_settings)|n}
|
||||
ThisPageData.inputFileTemplateFileOptions = ${json.dumps(input_file_options)|n}
|
||||
ThisPageData.inputFileTemplateUploads = {
|
||||
% for key in input_file_templates:
|
||||
'${key}': null,
|
||||
% endfor
|
||||
}
|
||||
|
||||
ThisPage.methods.validateInputFileTemplateSettings = function() {
|
||||
% for tmpl in input_file_templates.values():
|
||||
if (this.inputFileTemplateSettings['${tmpl['setting_mode']}'] == 'hosted') {
|
||||
if (!this.inputFileTemplateSettings['${tmpl['setting_file']}']) {
|
||||
if (!this.inputFileTemplateUploads['${tmpl['key']}']) {
|
||||
return "You must provide a file to upload for the ${tmpl['label']} template."
|
||||
}
|
||||
}
|
||||
}
|
||||
% endfor
|
||||
}
|
||||
|
||||
ThisPageData.validators.push(ThisPage.methods.validateInputFileTemplateSettings)
|
||||
|
||||
% endif
|
||||
|
||||
##############################
|
||||
## output file templates
|
||||
##############################
|
||||
|
||||
% if output_file_template_settings is not Undefined:
|
||||
|
||||
ThisPageData.outputFileTemplateSettings = ${json.dumps(output_file_template_settings)|n}
|
||||
ThisPageData.outputFileTemplateFileOptions = ${json.dumps(output_file_options)|n}
|
||||
ThisPageData.outputFileTemplateUploads = {
|
||||
% for key in output_file_templates:
|
||||
'${key}': null,
|
||||
% endfor
|
||||
}
|
||||
|
||||
ThisPage.methods.validateOutputFileTemplateSettings = function() {
|
||||
% for tmpl in output_file_templates.values():
|
||||
if (this.outputFileTemplateSettings['${tmpl['setting_mode']}'] == 'hosted') {
|
||||
if (!this.outputFileTemplateSettings['${tmpl['setting_file']}']) {
|
||||
if (!this.outputFileTemplateUploads['${tmpl['key']}']) {
|
||||
return "You must provide a file to upload for the ${tmpl['label']} template."
|
||||
}
|
||||
}
|
||||
}
|
||||
% endfor
|
||||
}
|
||||
|
||||
ThisPageData.validators.push(ThisPage.methods.validateOutputFileTemplateSettings)
|
||||
|
||||
% endif
|
||||
|
||||
</script>
|
||||
</%def>
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue