Add initial support for Vue 3 + Oruga, via "butterball" theme
just a savepoint, still have lots to do and test before this really works
This commit is contained in:
parent
5aa8d1f9a3
commit
2eaeb1891d
|
@ -1103,18 +1103,22 @@ class Form(object):
|
||||||
|
|
||||||
# only declare label template if it's complex
|
# only declare label template if it's complex
|
||||||
html = [html]
|
html = [html]
|
||||||
if len(label_contents) > 1:
|
# TODO: figure out why complex label does not work for oruga
|
||||||
|
if self.request.use_oruga:
|
||||||
# nb. must apply hack to get <template #label> as final result
|
|
||||||
label_template = HTML.tag('template', c=label_contents,
|
|
||||||
**{'#label': 1})
|
|
||||||
label_template = label_template.replace(
|
|
||||||
HTML.literal('<template #label="1"'),
|
|
||||||
HTML.literal('<template #label'))
|
|
||||||
html.insert(0, label_template)
|
|
||||||
|
|
||||||
else: # simple label
|
|
||||||
attrs['label'] = label
|
attrs['label'] = label
|
||||||
|
else:
|
||||||
|
if len(label_contents) > 1:
|
||||||
|
|
||||||
|
# nb. must apply hack to get <template #label> as final result
|
||||||
|
label_template = HTML.tag('template', c=label_contents,
|
||||||
|
**{'#label': 1})
|
||||||
|
label_template = label_template.replace(
|
||||||
|
HTML.literal('<template #label="1"'),
|
||||||
|
HTML.literal('<template #label'))
|
||||||
|
html.insert(0, label_template)
|
||||||
|
|
||||||
|
else: # simple label
|
||||||
|
attrs['label'] = label
|
||||||
|
|
||||||
# and finally wrap it all in a <b-field>
|
# and finally wrap it all in a <b-field>
|
||||||
return HTML.tag('b-field', c=html, **attrs)
|
return HTML.tag('b-field', c=html, **attrs)
|
||||||
|
|
|
@ -27,7 +27,9 @@ Event Subscribers
|
||||||
import six
|
import six
|
||||||
import json
|
import json
|
||||||
import datetime
|
import datetime
|
||||||
|
import logging
|
||||||
import warnings
|
import warnings
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
import rattail
|
import rattail
|
||||||
|
|
||||||
|
@ -41,7 +43,11 @@ from tailbone import helpers
|
||||||
from tailbone.db import Session
|
from tailbone.db import Session
|
||||||
from tailbone.config import csrf_header_name, should_expose_websockets
|
from tailbone.config import csrf_header_name, should_expose_websockets
|
||||||
from tailbone.menus import make_simple_menus
|
from tailbone.menus import make_simple_menus
|
||||||
from tailbone.util import get_available_themes, get_global_search_options
|
from tailbone.util import (get_available_themes, get_global_search_options,
|
||||||
|
should_use_oruga)
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def new_request(event):
|
def new_request(event):
|
||||||
|
@ -92,6 +98,11 @@ def new_request(event):
|
||||||
|
|
||||||
request.set_property(user, reify=True)
|
request.set_property(user, reify=True)
|
||||||
|
|
||||||
|
def use_oruga(request):
|
||||||
|
return should_use_oruga(request)
|
||||||
|
|
||||||
|
request.set_property(use_oruga, reify=True)
|
||||||
|
|
||||||
# assign client IP address to the session, for sake of versioning
|
# assign client IP address to the session, for sake of versioning
|
||||||
Session().continuum_remote_addr = request.client_addr
|
Session().continuum_remote_addr = request.client_addr
|
||||||
|
|
||||||
|
@ -119,6 +130,25 @@ def new_request(event):
|
||||||
return False
|
return False
|
||||||
request.has_any_perm = has_any_perm
|
request.has_any_perm = has_any_perm
|
||||||
|
|
||||||
|
def register_component(tagname, classname):
|
||||||
|
"""
|
||||||
|
Register a Vue 3 component, so the base template knows to
|
||||||
|
declare it for use within the app (page).
|
||||||
|
"""
|
||||||
|
if not hasattr(request, '_tailbone_registered_components'):
|
||||||
|
request._tailbone_registered_components = OrderedDict()
|
||||||
|
|
||||||
|
if tagname in request._tailbone_registered_components:
|
||||||
|
log.warning("component with tagname '%s' already registered "
|
||||||
|
"with class '%s' but we are replacing that with "
|
||||||
|
"class '%s'",
|
||||||
|
tagname,
|
||||||
|
request._tailbone_registered_components[tagname],
|
||||||
|
classname)
|
||||||
|
|
||||||
|
request._tailbone_registered_components[tagname] = classname
|
||||||
|
request.register_component = register_component
|
||||||
|
|
||||||
|
|
||||||
def before_render(event):
|
def before_render(event):
|
||||||
"""
|
"""
|
||||||
|
@ -143,6 +173,7 @@ def before_render(event):
|
||||||
renderer_globals['colander'] = colander
|
renderer_globals['colander'] = colander
|
||||||
renderer_globals['deform'] = deform
|
renderer_globals['deform'] = deform
|
||||||
renderer_globals['csrf_header_name'] = csrf_header_name(request.rattail_config)
|
renderer_globals['csrf_header_name'] = csrf_header_name(request.rattail_config)
|
||||||
|
renderer_globals['b'] = 'o' if request.use_oruga else 'b' # for buefy
|
||||||
|
|
||||||
# theme - we only want do this for classic web app, *not* API
|
# theme - we only want do this for classic web app, *not* API
|
||||||
# TODO: so, clearly we need a better way to distinguish the two
|
# TODO: so, clearly we need a better way to distinguish the two
|
||||||
|
|
|
@ -100,27 +100,27 @@
|
||||||
<h3 class="block is-size-3">Web Libraries</h3>
|
<h3 class="block is-size-3">Web Libraries</h3>
|
||||||
<div class="block" style="padding-left: 2rem;">
|
<div class="block" style="padding-left: 2rem;">
|
||||||
|
|
||||||
<b-table :data="weblibs">
|
<${b}-table :data="weblibs">
|
||||||
|
|
||||||
<b-table-column field="title"
|
<${b}-table-column field="title"
|
||||||
label="Name"
|
label="Name"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.title }}
|
{{ props.row.title }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
|
|
||||||
<b-table-column field="configured_version"
|
<${b}-table-column field="configured_version"
|
||||||
label="Version"
|
label="Version"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.configured_version || props.row.default_version }}
|
{{ props.row.configured_version || props.row.default_version }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
|
|
||||||
<b-table-column field="configured_url"
|
<${b}-table-column field="configured_url"
|
||||||
label="URL Override"
|
label="URL Override"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.configured_url }}
|
{{ props.row.configured_url }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
|
|
||||||
<b-table-column field="live_url"
|
<${b}-table-column field="live_url"
|
||||||
label="Effective (Live) URL"
|
label="Effective (Live) URL"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
<span v-if="props.row.modified"
|
<span v-if="props.row.modified"
|
||||||
|
@ -130,19 +130,23 @@
|
||||||
<span v-if="!props.row.modified">
|
<span v-if="!props.row.modified">
|
||||||
{{ props.row.live_url }}
|
{{ props.row.live_url }}
|
||||||
</span>
|
</span>
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
|
|
||||||
<b-table-column field="actions"
|
<${b}-table-column field="actions"
|
||||||
label="Actions"
|
label="Actions"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
<a href="#"
|
<a href="#"
|
||||||
@click.prevent="editWebLibraryInit(props.row)">
|
@click.prevent="editWebLibraryInit(props.row)">
|
||||||
<i class="fas fa-edit"></i>
|
% if request.use_oruga:
|
||||||
|
<o-icon icon="edit" />
|
||||||
|
% else:
|
||||||
|
<i class="fas fa-edit"></i>
|
||||||
|
% endif
|
||||||
Edit
|
Edit
|
||||||
</a>
|
</a>
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
|
|
||||||
</b-table>
|
</${b}-table>
|
||||||
|
|
||||||
% for weblib in weblibs:
|
% for weblib in weblibs:
|
||||||
${h.hidden('tailbone.libver.{}'.format(weblib['key']), **{':value': "simpleSettings['tailbone.libver.{}']".format(weblib['key'])})}
|
${h.hidden('tailbone.libver.{}'.format(weblib['key']), **{':value': "simpleSettings['tailbone.libver.{}']".format(weblib['key'])})}
|
||||||
|
@ -175,14 +179,14 @@
|
||||||
</b-field>
|
</b-field>
|
||||||
|
|
||||||
<b-field label="Override URL">
|
<b-field label="Override URL">
|
||||||
<b-input v-model="editWebLibraryURL">
|
<b-input v-model="editWebLibraryURL"
|
||||||
</b-input>
|
expanded />
|
||||||
</b-field>
|
</b-field>
|
||||||
|
|
||||||
<b-field label="Effective URL (as of last page load)">
|
<b-field label="Effective URL (as of last page load)">
|
||||||
<b-input v-model="editWebLibraryRecord.live_url"
|
<b-input v-model="editWebLibraryRecord.live_url"
|
||||||
disabled>
|
disabled
|
||||||
</b-input>
|
expanded />
|
||||||
</b-field>
|
</b-field>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -28,10 +28,11 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<b-collapse class="panel" open>
|
<${b}-collapse class="panel" open>
|
||||||
|
|
||||||
<template #trigger="props">
|
<template #trigger="props">
|
||||||
<div class="panel-heading"
|
<div class="panel-heading"
|
||||||
|
style="cursor: pointer;"
|
||||||
role="button">
|
role="button">
|
||||||
|
|
||||||
## TODO: for some reason buefy will "reuse" the icon
|
## TODO: for some reason buefy will "reuse" the icon
|
||||||
|
@ -57,30 +58,31 @@
|
||||||
|
|
||||||
<div class="panel-block">
|
<div class="panel-block">
|
||||||
<div style="width: 100%;">
|
<div style="width: 100%;">
|
||||||
<b-table :data="configFiles">
|
<${b}-table :data="configFiles">
|
||||||
|
|
||||||
<b-table-column field="priority"
|
<${b}-table-column field="priority"
|
||||||
label="Priority"
|
label="Priority"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.priority }}
|
{{ props.row.priority }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
|
|
||||||
<b-table-column field="path"
|
<${b}-table-column field="path"
|
||||||
label="File Path"
|
label="File Path"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.path }}
|
{{ props.row.path }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
|
|
||||||
</b-table>
|
</${b}-table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</b-collapse>
|
</${b}-collapse>
|
||||||
|
|
||||||
<b-collapse class="panel"
|
<${b}-collapse class="panel"
|
||||||
:open="false">
|
:open="false">
|
||||||
|
|
||||||
<template #trigger="props">
|
<template #trigger="props">
|
||||||
<div class="panel-heading"
|
<div class="panel-heading"
|
||||||
|
style="cursor: pointer;"
|
||||||
role="button">
|
role="button">
|
||||||
|
|
||||||
## TODO: for some reason buefy will "reuse" the icon
|
## TODO: for some reason buefy will "reuse" the icon
|
||||||
|
@ -109,7 +111,7 @@
|
||||||
${parent.render_grid_component()}
|
${parent.render_grid_component()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</b-collapse>
|
</${b}-collapse>
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="modify_this_page_vars()">
|
<%def name="modify_this_page_vars()">
|
||||||
|
|
|
@ -48,7 +48,12 @@
|
||||||
${h.hidden('profiles', **{':value': 'JSON.stringify(profilesData)'})}
|
${h.hidden('profiles', **{':value': 'JSON.stringify(profilesData)'})}
|
||||||
|
|
||||||
<b-notification type="is-warning"
|
<b-notification type="is-warning"
|
||||||
:active.sync="showConfigFilesNote">
|
% if request.use_oruga:
|
||||||
|
v-model:active="showConfigFilesNote"
|
||||||
|
% else:
|
||||||
|
:active.sync="showConfigFilesNote"
|
||||||
|
% endif
|
||||||
|
>
|
||||||
## TODO: should link to some ratman page here, yes?
|
## TODO: should link to some ratman page here, yes?
|
||||||
<p class="block">
|
<p class="block">
|
||||||
This tool works by modifying settings in the DB. It
|
This tool works by modifying settings in the DB. It
|
||||||
|
@ -101,52 +106,52 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<b-table :data="filteredProfilesData"
|
<${b}-table :data="filteredProfilesData"
|
||||||
:row-class="(row, i) => row.enabled ? null : 'has-background-warning'">
|
:row-class="(row, i) => row.enabled ? null : 'has-background-warning'">
|
||||||
<b-table-column field="key"
|
<${b}-table-column field="key"
|
||||||
label="Watcher Key"
|
label="Watcher Key"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.key }}
|
{{ props.row.key }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="watcher_spec"
|
<${b}-table-column field="watcher_spec"
|
||||||
label="Watcher Spec"
|
label="Watcher Spec"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.watcher_spec }}
|
{{ props.row.watcher_spec }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="watcher_dbkey"
|
<${b}-table-column field="watcher_dbkey"
|
||||||
label="DB Key"
|
label="DB Key"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.watcher_dbkey }}
|
{{ props.row.watcher_dbkey }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="watcher_delay"
|
<${b}-table-column field="watcher_delay"
|
||||||
label="Loop Delay"
|
label="Loop Delay"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.watcher_delay }} sec
|
{{ props.row.watcher_delay }} sec
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="watcher_retry_attempts"
|
<${b}-table-column field="watcher_retry_attempts"
|
||||||
label="Attempts / Delay"
|
label="Attempts / Delay"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.watcher_retry_attempts }} / {{ props.row.watcher_retry_delay }} sec
|
{{ props.row.watcher_retry_attempts }} / {{ props.row.watcher_retry_delay }} sec
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="watcher_default_runas"
|
<${b}-table-column field="watcher_default_runas"
|
||||||
label="Default Runas"
|
label="Default Runas"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.watcher_default_runas }}
|
{{ props.row.watcher_default_runas }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column label="Consumers"
|
<${b}-table-column label="Consumers"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ consumerShortList(props.row) }}
|
{{ consumerShortList(props.row) }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
## <b-table-column field="notes" label="Notes">
|
## <${b}-table-column field="notes" label="Notes">
|
||||||
## TODO
|
## TODO
|
||||||
## ## {{ props.row.notes }}
|
## ## {{ props.row.notes }}
|
||||||
## </b-table-column>
|
## </${b}-table-column>
|
||||||
<b-table-column field="enabled"
|
<${b}-table-column field="enabled"
|
||||||
label="Enabled"
|
label="Enabled"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.enabled ? "Yes" : "No" }}
|
{{ props.row.enabled ? "Yes" : "No" }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column label="Actions"
|
<${b}-table-column label="Actions"
|
||||||
v-slot="props"
|
v-slot="props"
|
||||||
v-if="useProfileSettings">
|
v-if="useProfileSettings">
|
||||||
<a href="#"
|
<a href="#"
|
||||||
|
@ -162,14 +167,14 @@
|
||||||
<i class="fas fa-trash"></i>
|
<i class="fas fa-trash"></i>
|
||||||
Delete
|
Delete
|
||||||
</a>
|
</a>
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<template slot="empty">
|
<template #empty>
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<div class="content has-text-grey has-text-centered">
|
<div class="content has-text-grey has-text-centered">
|
||||||
<p>
|
<p>
|
||||||
<b-icon
|
<b-icon
|
||||||
pack="fas"
|
pack="fas"
|
||||||
icon="fas fa-sad-tear"
|
icon="sad-tear"
|
||||||
size="is-large">
|
size="is-large">
|
||||||
</b-icon>
|
</b-icon>
|
||||||
</p>
|
</p>
|
||||||
|
@ -177,7 +182,7 @@
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
</b-table>
|
</${b}-table>
|
||||||
|
|
||||||
<b-modal :active.sync="editProfileShowDialog">
|
<b-modal :active.sync="editProfileShowDialog">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
|
@ -199,12 +204,12 @@
|
||||||
|
|
||||||
</b-field>
|
</b-field>
|
||||||
|
|
||||||
<b-field grouped>
|
<b-field grouped expanded>
|
||||||
|
|
||||||
<b-field label="Watcher Spec"
|
<b-field label="Watcher Spec"
|
||||||
:type="editingProfileWatcherSpec ? null : 'is-danger'"
|
:type="editingProfileWatcherSpec ? null : 'is-danger'"
|
||||||
expanded>
|
expanded>
|
||||||
<b-input v-model="editingProfileWatcherSpec">
|
<b-input v-model="editingProfileWatcherSpec" expanded>
|
||||||
</b-input>
|
</b-input>
|
||||||
</b-field>
|
</b-field>
|
||||||
|
|
||||||
|
@ -293,19 +298,19 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<b-table :data="editingProfilePendingWatcherKwargs"
|
<${b}-table :data="editingProfilePendingWatcherKwargs"
|
||||||
style="margin-left: 1rem;">
|
style="margin-left: 1rem;">
|
||||||
<b-table-column field="key"
|
<${b}-table-column field="key"
|
||||||
label="Key"
|
label="Key"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.key }}
|
{{ props.row.key }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="value"
|
<${b}-table-column field="value"
|
||||||
label="Value"
|
label="Value"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.value }}
|
{{ props.row.value }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column label="Actions"
|
<${b}-table-column label="Actions"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
<a href="#"
|
<a href="#"
|
||||||
@click.prevent="editProfileWatcherKwarg(props.row)">
|
@click.prevent="editProfileWatcherKwarg(props.row)">
|
||||||
|
@ -319,14 +324,14 @@
|
||||||
<i class="fas fa-trash"></i>
|
<i class="fas fa-trash"></i>
|
||||||
Delete
|
Delete
|
||||||
</a>
|
</a>
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<template slot="empty">
|
<template #empty>
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<div class="content has-text-grey has-text-centered">
|
<div class="content has-text-grey has-text-centered">
|
||||||
<p>
|
<p>
|
||||||
<b-icon
|
<b-icon
|
||||||
pack="fas"
|
pack="fas"
|
||||||
icon="fas fa-sad-tear"
|
icon="sad-tear"
|
||||||
size="is-large">
|
size="is-large">
|
||||||
</b-icon>
|
</b-icon>
|
||||||
</p>
|
</p>
|
||||||
|
@ -334,7 +339,7 @@
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
</b-table>
|
</${b}-table>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -350,19 +355,19 @@
|
||||||
</b-checkbox>
|
</b-checkbox>
|
||||||
</b-field>
|
</b-field>
|
||||||
|
|
||||||
<b-table :data="editingProfilePendingConsumers"
|
<${b}-table :data="editingProfilePendingConsumers"
|
||||||
v-if="!editingProfileWatcherConsumesSelf"
|
v-if="!editingProfileWatcherConsumesSelf"
|
||||||
:row-class="(row, i) => row.enabled ? null : 'has-background-warning'">
|
:row-class="(row, i) => row.enabled ? null : 'has-background-warning'">
|
||||||
<b-table-column field="key"
|
<${b}-table-column field="key"
|
||||||
label="Consumer"
|
label="Consumer"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.key }}
|
{{ props.row.key }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column style="white-space: nowrap;"
|
<${b}-table-column style="white-space: nowrap;"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.consumer_delay }} / {{ props.row.consumer_retry_attempts }} / {{ props.row.consumer_retry_delay }}
|
{{ props.row.consumer_delay }} / {{ props.row.consumer_retry_attempts }} / {{ props.row.consumer_retry_delay }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column label="Actions"
|
<${b}-table-column label="Actions"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
<a href="#"
|
<a href="#"
|
||||||
class="grid-action"
|
class="grid-action"
|
||||||
|
@ -377,14 +382,14 @@
|
||||||
<i class="fas fa-trash"></i>
|
<i class="fas fa-trash"></i>
|
||||||
Delete
|
Delete
|
||||||
</a>
|
</a>
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<template slot="empty">
|
<template #empty>
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<div class="content has-text-grey has-text-centered">
|
<div class="content has-text-grey has-text-centered">
|
||||||
<p>
|
<p>
|
||||||
<b-icon
|
<b-icon
|
||||||
pack="fas"
|
pack="fas"
|
||||||
icon="fas fa-sad-tear"
|
icon="sad-tear"
|
||||||
size="is-large">
|
size="is-large">
|
||||||
</b-icon>
|
</b-icon>
|
||||||
</p>
|
</p>
|
||||||
|
@ -392,7 +397,7 @@
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
</b-table>
|
</${b}-table>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -526,7 +531,8 @@
|
||||||
expanded>
|
expanded>
|
||||||
<b-input name="supervisor_process_name"
|
<b-input name="supervisor_process_name"
|
||||||
v-model="supervisorProcessName"
|
v-model="supervisorProcessName"
|
||||||
@input="settingsNeedSaved = true">
|
@input="settingsNeedSaved = true"
|
||||||
|
expanded>
|
||||||
</b-input>
|
</b-input>
|
||||||
</b-field>
|
</b-field>
|
||||||
|
|
||||||
|
@ -535,7 +541,8 @@
|
||||||
expanded>
|
expanded>
|
||||||
<b-input name="restart_command"
|
<b-input name="restart_command"
|
||||||
v-model="restartCommand"
|
v-model="restartCommand"
|
||||||
@input="settingsNeedSaved = true">
|
@input="settingsNeedSaved = true"
|
||||||
|
expanded>
|
||||||
</b-input>
|
</b-input>
|
||||||
</b-field>
|
</b-field>
|
||||||
|
|
||||||
|
|
|
@ -47,79 +47,79 @@
|
||||||
</div>
|
</div>
|
||||||
</b-field>
|
</b-field>
|
||||||
|
|
||||||
<b-field label="Watcher Status">
|
<h3 class="is-size-3">Watcher Status</h3>
|
||||||
<b-table :data="watchers">
|
|
||||||
<b-table-column field="key"
|
<${b}-table :data="watchers">
|
||||||
|
<${b}-table-column field="key"
|
||||||
label="Watcher"
|
label="Watcher"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.key }}
|
{{ props.row.key }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="spec"
|
<${b}-table-column field="spec"
|
||||||
label="Spec"
|
label="Spec"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.spec }}
|
{{ props.row.spec }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="dbkey"
|
<${b}-table-column field="dbkey"
|
||||||
label="DB Key"
|
label="DB Key"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.dbkey }}
|
{{ props.row.dbkey }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="delay"
|
<${b}-table-column field="delay"
|
||||||
label="Delay"
|
label="Delay"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.delay }} second(s)
|
{{ props.row.delay }} second(s)
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="lastrun"
|
<${b}-table-column field="lastrun"
|
||||||
label="Last Watched"
|
label="Last Watched"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
<span v-html="props.row.lastrun"></span>
|
<span v-html="props.row.lastrun"></span>
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="status"
|
<${b}-table-column field="status"
|
||||||
label="Status"
|
label="Status"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
<span :class="props.row.status == 'okay' ? 'has-background-success' : 'has-background-warning'">
|
<span :class="props.row.status == 'okay' ? 'has-background-success' : 'has-background-warning'">
|
||||||
{{ props.row.status }}
|
{{ props.row.status }}
|
||||||
</span>
|
</span>
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
</b-table>
|
</${b}-table>
|
||||||
</b-field>
|
|
||||||
|
|
||||||
<b-field label="Consumer Status">
|
<h3 class="is-size-3">Consumer Status</h3>
|
||||||
<b-table :data="consumers">
|
|
||||||
<b-table-column field="key"
|
<${b}-table :data="consumers">
|
||||||
|
<${b}-table-column field="key"
|
||||||
label="Consumer"
|
label="Consumer"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.key }}
|
{{ props.row.key }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="spec"
|
<${b}-table-column field="spec"
|
||||||
label="Spec"
|
label="Spec"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.spec }}
|
{{ props.row.spec }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="dbkey"
|
<${b}-table-column field="dbkey"
|
||||||
label="DB Key"
|
label="DB Key"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.dbkey }}
|
{{ props.row.dbkey }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="delay"
|
<${b}-table-column field="delay"
|
||||||
label="Delay"
|
label="Delay"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.delay }} second(s)
|
{{ props.row.delay }} second(s)
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="changes"
|
<${b}-table-column field="changes"
|
||||||
label="Pending Changes"
|
label="Pending Changes"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.changes }}
|
{{ props.row.changes }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="status"
|
<${b}-table-column field="status"
|
||||||
label="Status"
|
label="Status"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
<span :class="props.row.status == 'okay' ? 'has-background-success' : 'has-background-warning'">
|
<span :class="props.row.status == 'okay' ? 'has-background-success' : 'has-background-warning'">
|
||||||
{{ props.row.status }}
|
{{ props.row.status }}
|
||||||
</span>
|
</span>
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
</b-table>
|
</${b}-table>
|
||||||
</b-field>
|
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="modify_this_page_vars()">
|
<%def name="modify_this_page_vars()">
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
## -*- coding: utf-8; -*-
|
## -*- coding: utf-8; -*-
|
||||||
|
|
||||||
|
<% request.register_component(form.component, form.component_studly) %>
|
||||||
|
|
||||||
<script type="text/x-template" id="${form.component}-template">
|
<script type="text/x-template" id="${form.component}-template">
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -106,55 +106,68 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<b-table
|
<${b}-table
|
||||||
:data="new_table.columns">
|
:data="new_table.columns">
|
||||||
|
|
||||||
<b-table-column field="name"
|
<${b}-table-column field="name"
|
||||||
label="Name"
|
label="Name"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.name }}
|
{{ props.row.name }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
|
|
||||||
<b-table-column field="data_type"
|
<${b}-table-column field="data_type"
|
||||||
label="Data Type"
|
label="Data Type"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.data_type }}
|
{{ props.row.data_type }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
|
|
||||||
<b-table-column field="nullable"
|
<${b}-table-column field="nullable"
|
||||||
label="Nullable"
|
label="Nullable"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.nullable }}
|
{{ props.row.nullable }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
|
|
||||||
<b-table-column field="description"
|
<${b}-table-column field="description"
|
||||||
label="Description"
|
label="Description"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.description }}
|
{{ props.row.description }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
|
|
||||||
<b-table-column field="actions"
|
<${b}-table-column field="actions"
|
||||||
label="Actions"
|
label="Actions"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
<a href="#" class="grid-action"
|
<a href="#" class="grid-action"
|
||||||
@click.prevent="editColumnRow(props.row)">
|
@click.prevent="editColumnRow(props)">
|
||||||
<i class="fas fa-edit"></i>
|
% if request.use_oruga:
|
||||||
|
<o-icon icon="edit" />
|
||||||
|
% else:
|
||||||
|
<i class="fas fa-edit"></i>
|
||||||
|
% endif
|
||||||
Edit
|
Edit
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
||||||
<a href="#" class="grid-action has-text-danger"
|
<a href="#" class="grid-action has-text-danger"
|
||||||
@click.prevent="deleteColumn(props.index)">
|
@click.prevent="deleteColumn(props.index)">
|
||||||
<i class="fas fa-trash"></i>
|
% if request.use_oruga:
|
||||||
|
<o-icon icon="trash" />
|
||||||
|
% else:
|
||||||
|
<i class="fas fa-trash"></i>
|
||||||
|
% endif
|
||||||
Delete
|
Delete
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
|
|
||||||
</b-table>
|
</${b}-table>
|
||||||
|
|
||||||
<b-modal has-modal-card
|
<${b}-modal has-modal-card
|
||||||
:active.sync="showingEditColumn">
|
% if request.use_oruga:
|
||||||
|
v-model:active="showingEditColumn"
|
||||||
|
% else:
|
||||||
|
:active.sync="showingEditColumn"
|
||||||
|
% endif
|
||||||
|
>
|
||||||
<div class="modal-card">
|
<div class="modal-card">
|
||||||
|
|
||||||
<header class="modal-card-head">
|
<header class="modal-card-head">
|
||||||
|
@ -197,7 +210,7 @@
|
||||||
</b-button>
|
</b-button>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</b-modal>
|
</${b}-modal>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</b-field>
|
</b-field>
|
||||||
|
@ -318,6 +331,7 @@
|
||||||
|
|
||||||
ThisPageData.showingEditColumn = false
|
ThisPageData.showingEditColumn = false
|
||||||
ThisPageData.editingColumn = null
|
ThisPageData.editingColumn = null
|
||||||
|
ThisPageData.editingColumnIndex = null
|
||||||
ThisPageData.editingColumnName = null
|
ThisPageData.editingColumnName = null
|
||||||
ThisPageData.editingColumnDataType = null
|
ThisPageData.editingColumnDataType = null
|
||||||
ThisPageData.editingColumnNullable = null
|
ThisPageData.editingColumnNullable = null
|
||||||
|
@ -325,6 +339,7 @@
|
||||||
|
|
||||||
ThisPage.methods.addColumn = function(column) {
|
ThisPage.methods.addColumn = function(column) {
|
||||||
this.editingColumn = null
|
this.editingColumn = null
|
||||||
|
this.editingColumnIndex = null
|
||||||
this.editingColumnName = null
|
this.editingColumnName = null
|
||||||
this.editingColumnDataType = null
|
this.editingColumnDataType = null
|
||||||
this.editingColumnNullable = true
|
this.editingColumnNullable = true
|
||||||
|
@ -332,8 +347,10 @@
|
||||||
this.showingEditColumn = true
|
this.showingEditColumn = true
|
||||||
}
|
}
|
||||||
|
|
||||||
ThisPage.methods.editColumnRow = function(column) {
|
ThisPage.methods.editColumnRow = function(props) {
|
||||||
|
const column = props.row
|
||||||
this.editingColumn = column
|
this.editingColumn = column
|
||||||
|
this.editingColumnIndex = props.index
|
||||||
this.editingColumnName = column.name
|
this.editingColumnName = column.name
|
||||||
this.editingColumnDataType = column.data_type
|
this.editingColumnDataType = column.data_type
|
||||||
this.editingColumnNullable = column.nullable
|
this.editingColumnNullable = column.nullable
|
||||||
|
@ -343,7 +360,7 @@
|
||||||
|
|
||||||
ThisPage.methods.saveColumn = function() {
|
ThisPage.methods.saveColumn = function() {
|
||||||
if (this.editingColumn) {
|
if (this.editingColumn) {
|
||||||
column = this.editingColumn
|
column = this.new_table.columns[this.editingColumnIndex]
|
||||||
} else {
|
} else {
|
||||||
column = {}
|
column = {}
|
||||||
this.new_table.columns.push(column)
|
this.new_table.columns.push(column)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
## -*- coding: utf-8; -*-
|
## -*- coding: utf-8; -*-
|
||||||
<b-table
|
<${b}-table
|
||||||
:data="${data_prop}"
|
:data="${data_prop}"
|
||||||
icon-pack="fas"
|
icon-pack="fas"
|
||||||
striped
|
striped
|
||||||
|
@ -21,7 +21,7 @@
|
||||||
>
|
>
|
||||||
|
|
||||||
% for i, column in enumerate(grid_columns):
|
% for i, column in enumerate(grid_columns):
|
||||||
<b-table-column field="${column['field']}"
|
<${b}-table-column field="${column['field']}"
|
||||||
% if not empty_labels:
|
% if not empty_labels:
|
||||||
label="${column['label']}"
|
label="${column['label']}"
|
||||||
% elif i > 0:
|
% elif i > 0:
|
||||||
|
@ -50,11 +50,11 @@
|
||||||
% else:
|
% else:
|
||||||
<span v-html="props.row.${column['field']}"></span>
|
<span v-html="props.row.${column['field']}"></span>
|
||||||
% endif
|
% endif
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
% endfor
|
% endfor
|
||||||
|
|
||||||
% if grid.main_actions or grid.more_actions:
|
% if grid.main_actions or grid.more_actions:
|
||||||
<b-table-column field="actions"
|
<${b}-table-column field="actions"
|
||||||
label="Actions"
|
label="Actions"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
% for action in grid.main_actions:
|
% for action in grid.main_actions:
|
||||||
|
@ -68,12 +68,16 @@
|
||||||
@click.prevent="${action.click_handler}"
|
@click.prevent="${action.click_handler}"
|
||||||
% endif
|
% endif
|
||||||
>
|
>
|
||||||
<i class="fas fa-${action.icon}"></i>
|
% if request.use_oruga:
|
||||||
|
<o-icon icon="${action.icon}" />
|
||||||
|
% else:
|
||||||
|
<i class="fas fa-${action.icon}"></i>
|
||||||
|
% endif
|
||||||
${action.label}
|
${action.label}
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
% endfor
|
% endfor
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
% endif
|
% endif
|
||||||
|
|
||||||
<template #empty>
|
<template #empty>
|
||||||
|
@ -99,4 +103,4 @@
|
||||||
</template>
|
</template>
|
||||||
% endif
|
% endif
|
||||||
|
|
||||||
</b-table>
|
</${b}-table>
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
## -*- coding: utf-8; -*-
|
## -*- coding: utf-8; -*-
|
||||||
|
|
||||||
|
<% request.register_component(grid.component, grid.component_studly) %>
|
||||||
|
|
||||||
<script type="text/x-template" id="${grid.component}-template">
|
<script type="text/x-template" id="${grid.component}-template">
|
||||||
<div>
|
<div>
|
||||||
|
|
||||||
|
@ -38,7 +40,7 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<b-table
|
<${b}-table
|
||||||
:data="visibleData"
|
:data="visibleData"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:row-class="getRowClass"
|
:row-class="getRowClass"
|
||||||
|
@ -51,7 +53,11 @@
|
||||||
:checkable="checkable"
|
:checkable="checkable"
|
||||||
|
|
||||||
% if grid.checkboxes:
|
% if grid.checkboxes:
|
||||||
:checked-rows.sync="checkedRows"
|
% if request.use_oruga:
|
||||||
|
v-model:checked-rows="checkedRows"
|
||||||
|
% else:
|
||||||
|
:checked-rows.sync="checkedRows"
|
||||||
|
% endif
|
||||||
% if grid.clicking_row_checks_box:
|
% if grid.clicking_row_checks_box:
|
||||||
@click="rowClick"
|
@click="rowClick"
|
||||||
% endif
|
% endif
|
||||||
|
@ -111,7 +117,7 @@
|
||||||
:narrowed="true">
|
:narrowed="true">
|
||||||
|
|
||||||
% for column in grid_columns:
|
% for column in grid_columns:
|
||||||
<b-table-column field="${column['field']}"
|
<${b}-table-column field="${column['field']}"
|
||||||
label="${column['label']}"
|
label="${column['label']}"
|
||||||
v-slot="props"
|
v-slot="props"
|
||||||
:sortable="${json.dumps(column['sortable'])}"
|
:sortable="${json.dumps(column['sortable'])}"
|
||||||
|
@ -132,11 +138,11 @@
|
||||||
% else:
|
% else:
|
||||||
<span v-html="props.row.${column['field']}"></span>
|
<span v-html="props.row.${column['field']}"></span>
|
||||||
% endif
|
% endif
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
% endfor
|
% endfor
|
||||||
|
|
||||||
% if grid.main_actions or grid.more_actions:
|
% if grid.main_actions or grid.more_actions:
|
||||||
<b-table-column field="actions"
|
<${b}-table-column field="actions"
|
||||||
label="Actions"
|
label="Actions"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
## TODO: we do not currently differentiate for "main vs. more"
|
## TODO: we do not currently differentiate for "main vs. more"
|
||||||
|
@ -152,12 +158,17 @@
|
||||||
target="${action.target}"
|
target="${action.target}"
|
||||||
% endif
|
% endif
|
||||||
>
|
>
|
||||||
${action.render_icon()|n}
|
% if request.use_oruga:
|
||||||
${action.render_label()|n}
|
<o-icon icon="${action.icon}" />
|
||||||
|
<span>${action.render_label()|n}</span>
|
||||||
|
% else:
|
||||||
|
${action.render_icon()|n}
|
||||||
|
${action.render_label()|n}
|
||||||
|
% endif
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
% endfor
|
% endfor
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
% endif
|
% endif
|
||||||
|
|
||||||
<template #empty>
|
<template #empty>
|
||||||
|
@ -183,7 +194,11 @@
|
||||||
size="is-small"
|
size="is-small"
|
||||||
@click="copyDirectLink()"
|
@click="copyDirectLink()"
|
||||||
title="Copy link to clipboard">
|
title="Copy link to clipboard">
|
||||||
<span><i class="fa fa-share-alt"></i></span>
|
% if request.use_oruga:
|
||||||
|
<o-icon icon="share-alt" />
|
||||||
|
% else:
|
||||||
|
<span><i class="fa fa-share-alt"></i></span>
|
||||||
|
% endif
|
||||||
</b-button>
|
</b-button>
|
||||||
% else:
|
% else:
|
||||||
<div></div>
|
<div></div>
|
||||||
|
@ -213,7 +228,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
</b-table>
|
</${b}-table>
|
||||||
|
|
||||||
## dummy input field needed for sharing links on *insecure* sites
|
## dummy input field needed for sharing links on *insecure* sites
|
||||||
% if request.scheme == 'http':
|
% if request.scheme == 'http':
|
||||||
|
@ -523,6 +538,12 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
perPageUpdated(value) {
|
perPageUpdated(value) {
|
||||||
|
|
||||||
|
// nb. buefy passes value, oruga passes event
|
||||||
|
if (value.target) {
|
||||||
|
value = event.target.value
|
||||||
|
}
|
||||||
|
|
||||||
this.loadAsyncData({
|
this.loadAsyncData({
|
||||||
pagesize: value,
|
pagesize: value,
|
||||||
})
|
})
|
||||||
|
@ -530,6 +551,11 @@
|
||||||
|
|
||||||
onSort(field, order, event) {
|
onSort(field, order, event) {
|
||||||
|
|
||||||
|
// nb. buefy passes field name, oruga passes object
|
||||||
|
if (field.field) {
|
||||||
|
field = field.field
|
||||||
|
}
|
||||||
|
|
||||||
if (event.ctrlKey) {
|
if (event.ctrlKey) {
|
||||||
|
|
||||||
// engage or enhance multi-column sorting
|
// engage or enhance multi-column sorting
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="make_grid_filter_numeric_value_component()">
|
<%def name="make_grid_filter_numeric_value_component()">
|
||||||
|
<% request.register_component('grid-filter-numeric-value', 'GridFilterNumericValue') %>
|
||||||
<script type="text/x-template" id="grid-filter-numeric-value-template">
|
<script type="text/x-template" id="grid-filter-numeric-value-template">
|
||||||
<div class="level">
|
<div class="level">
|
||||||
<div class="level-left">
|
<div class="level-left">
|
||||||
|
@ -95,13 +96,14 @@
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="make_grid_filter_date_value_component()">
|
<%def name="make_grid_filter_date_value_component()">
|
||||||
|
<% request.register_component('grid-filter-date-value', 'GridFilterDateValue') %>
|
||||||
<script type="text/x-template" id="grid-filter-date-value-template">
|
<script type="text/x-template" id="grid-filter-date-value-template">
|
||||||
<div class="level">
|
<div class="level">
|
||||||
<div class="level-left">
|
<div class="level-left">
|
||||||
<div class="level-item">
|
<div class="level-item">
|
||||||
<tailbone-datepicker v-model="startDate"
|
<tailbone-datepicker v-model="startDate"
|
||||||
ref="startDate"
|
ref="startDate"
|
||||||
@input="startDateChanged">
|
@${'update:model-value' if request.use_oruga else 'input'}="startDateChanged">
|
||||||
</tailbone-datepicker>
|
</tailbone-datepicker>
|
||||||
</div>
|
</div>
|
||||||
<div v-show="dateRange"
|
<div v-show="dateRange"
|
||||||
|
@ -112,7 +114,7 @@
|
||||||
class="level-item">
|
class="level-item">
|
||||||
<tailbone-datepicker v-model="endDate"
|
<tailbone-datepicker v-model="endDate"
|
||||||
ref="endDate"
|
ref="endDate"
|
||||||
@input="endDateChanged">
|
@${'update:model-value' if request.use_oruga else 'input'}="endDateChanged">
|
||||||
</tailbone-datepicker>
|
</tailbone-datepicker>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -123,25 +125,26 @@
|
||||||
const GridFilterDateValue = {
|
const GridFilterDateValue = {
|
||||||
template: '#grid-filter-date-value-template',
|
template: '#grid-filter-date-value-template',
|
||||||
props: {
|
props: {
|
||||||
value: String,
|
${'modelValue' if request.use_oruga else 'value'}: String,
|
||||||
dateRange: Boolean,
|
dateRange: Boolean,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
let startDate = null
|
let startDate = null
|
||||||
let endDate = null
|
let endDate = null
|
||||||
if (this.value) {
|
let value = this.${'modelValue' if request.use_oruga else 'value'}
|
||||||
|
if (value) {
|
||||||
|
|
||||||
if (this.dateRange) {
|
if (this.dateRange) {
|
||||||
let values = this.value.split('|')
|
let values = value.split('|')
|
||||||
if (values.length == 2) {
|
if (values.length == 2) {
|
||||||
startDate = this.parseDate(values[0])
|
startDate = this.parseDate(values[0])
|
||||||
endDate = this.parseDate(values[1])
|
endDate = this.parseDate(values[1])
|
||||||
} else { // no end date specified?
|
} else { // no end date specified?
|
||||||
startDate = this.parseDate(this.value)
|
startDate = this.parseDate(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
} else { // not a range, so start date only
|
} else { // not a range, so start date only
|
||||||
startDate = this.parseDate(this.value)
|
startDate = this.parseDate(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,11 +182,11 @@
|
||||||
if (this.dateRange) {
|
if (this.dateRange) {
|
||||||
value += '|' + this.formatDate(this.endDate)
|
value += '|' + this.formatDate(this.endDate)
|
||||||
}
|
}
|
||||||
this.$emit('input', value)
|
this.$emit("${'update:modelValue' if request.use_oruga else 'input'}", value)
|
||||||
},
|
},
|
||||||
endDateChanged(value) {
|
endDateChanged(value) {
|
||||||
value = this.formatDate(this.startDate) + '|' + this.formatDate(value)
|
value = this.formatDate(this.startDate) + '|' + this.formatDate(value)
|
||||||
this.$emit('input', value)
|
this.$emit("${'update:modelValue' if request.use_oruga else 'input'}", value)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -194,6 +197,7 @@
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="make_grid_filter_component()">
|
<%def name="make_grid_filter_component()">
|
||||||
|
<% request.register_component('grid-filter', 'GridFilter') %>
|
||||||
<script type="text/x-template" id="grid-filter-template">
|
<script type="text/x-template" id="grid-filter-template">
|
||||||
<div class="filter"
|
<div class="filter"
|
||||||
v-show="filter.visible"
|
v-show="filter.visible"
|
||||||
|
|
|
@ -6,54 +6,58 @@
|
||||||
|
|
||||||
<h3 class="is-size-3">Designated Handlers</h3>
|
<h3 class="is-size-3">Designated Handlers</h3>
|
||||||
|
|
||||||
<b-table :data="handlersData"
|
<${b}-table :data="handlersData"
|
||||||
narrowed
|
narrowed
|
||||||
icon-pack="fas"
|
icon-pack="fas"
|
||||||
:default-sort="['host_title', 'asc']">
|
:default-sort="['host_title', 'asc']">
|
||||||
<b-table-column field="host_title"
|
<${b}-table-column field="host_title"
|
||||||
label="Data Source"
|
label="Data Source"
|
||||||
v-slot="props"
|
v-slot="props"
|
||||||
sortable>
|
sortable>
|
||||||
{{ props.row.host_title }}
|
{{ props.row.host_title }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="local_title"
|
<${b}-table-column field="local_title"
|
||||||
label="Data Target"
|
label="Data Target"
|
||||||
v-slot="props"
|
v-slot="props"
|
||||||
sortable>
|
sortable>
|
||||||
{{ props.row.local_title }}
|
{{ props.row.local_title }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="direction"
|
<${b}-table-column field="direction"
|
||||||
label="Direction"
|
label="Direction"
|
||||||
v-slot="props"
|
v-slot="props"
|
||||||
sortable>
|
sortable>
|
||||||
{{ props.row.direction_display }}
|
{{ props.row.direction_display }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="handler_spec"
|
<${b}-table-column field="handler_spec"
|
||||||
label="Handler Spec"
|
label="Handler Spec"
|
||||||
v-slot="props"
|
v-slot="props"
|
||||||
sortable>
|
sortable>
|
||||||
{{ props.row.handler_spec }}
|
{{ props.row.handler_spec }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="cmd"
|
<${b}-table-column field="cmd"
|
||||||
label="Command"
|
label="Command"
|
||||||
v-slot="props"
|
v-slot="props"
|
||||||
sortable>
|
sortable>
|
||||||
{{ props.row.command }} {{ props.row.subcommand }}
|
{{ props.row.command }} {{ props.row.subcommand }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="runas"
|
<${b}-table-column field="runas"
|
||||||
label="Default Runas"
|
label="Default Runas"
|
||||||
v-slot="props"
|
v-slot="props"
|
||||||
sortable>
|
sortable>
|
||||||
{{ props.row.default_runas }}
|
{{ props.row.default_runas }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column label="Actions"
|
<${b}-table-column label="Actions"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
<a href="#" class="grid-action"
|
<a href="#" class="grid-action"
|
||||||
@click.prevent="editHandler(props.row)">
|
@click.prevent="editHandler(props.row)">
|
||||||
|
% if request.use_oruga:
|
||||||
|
<o-icon icon="edit" />
|
||||||
|
% else:
|
||||||
<i class="fas fa-edit"></i>
|
<i class="fas fa-edit"></i>
|
||||||
|
% endif
|
||||||
Edit
|
Edit
|
||||||
</a>
|
</a>
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<template slot="empty">
|
<template slot="empty">
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<div class="content has-text-grey has-text-centered">
|
<div class="content has-text-grey has-text-centered">
|
||||||
|
@ -68,7 +72,7 @@
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
</b-table>
|
</${b}-table>
|
||||||
|
|
||||||
<b-modal :active.sync="editHandlerShowDialog">
|
<b-modal :active.sync="editHandlerShowDialog">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
|
|
|
@ -22,48 +22,56 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="block" style="padding-left: 2rem; display: flex;">
|
<div class="block" style="padding-left: 2rem; display: flex;">
|
||||||
|
|
||||||
<b-table :data="overnightTasks">
|
<${b}-table :data="overnightTasks">
|
||||||
<!-- <b-table-column field="key" -->
|
<!-- <${b}-table-column field="key" -->
|
||||||
<!-- label="Key" -->
|
<!-- label="Key" -->
|
||||||
<!-- sortable> -->
|
<!-- sortable> -->
|
||||||
<!-- {{ props.row.key }} -->
|
<!-- {{ props.row.key }} -->
|
||||||
<!-- </b-table-column> -->
|
<!-- </${b}-table-column> -->
|
||||||
<b-table-column field="key"
|
<${b}-table-column field="key"
|
||||||
label="Key"
|
label="Key"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.key }}
|
{{ props.row.key }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="description"
|
<${b}-table-column field="description"
|
||||||
label="Description"
|
label="Description"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.description }}
|
{{ props.row.description }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="class_name"
|
<${b}-table-column field="class_name"
|
||||||
label="Class Name"
|
label="Class Name"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.class_name }}
|
{{ props.row.class_name }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="script"
|
<${b}-table-column field="script"
|
||||||
label="Script"
|
label="Script"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.script }}
|
{{ props.row.script }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column label="Actions"
|
<${b}-table-column label="Actions"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
<a href="#"
|
<a href="#"
|
||||||
@click.prevent="overnightTaskEdit(props.row)">
|
@click.prevent="overnightTaskEdit(props.row)">
|
||||||
<i class="fas fa-edit"></i>
|
% if request.use_oruga:
|
||||||
|
<o-icon icon="edit" />
|
||||||
|
% else:
|
||||||
|
<i class="fas fa-edit"></i>
|
||||||
|
% endif
|
||||||
Edit
|
Edit
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a href="#"
|
<a href="#"
|
||||||
class="has-text-danger"
|
class="has-text-danger"
|
||||||
@click.prevent="overnightTaskDelete(props.row)">
|
@click.prevent="overnightTaskDelete(props.row)">
|
||||||
<i class="fas fa-trash"></i>
|
% if request.use_oruga:
|
||||||
|
<o-icon icon="trash" />
|
||||||
|
% else:
|
||||||
|
<i class="fas fa-trash"></i>
|
||||||
|
% endif
|
||||||
Delete
|
Delete
|
||||||
</a>
|
</a>
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
</b-table>
|
</${b}-table>
|
||||||
|
|
||||||
<b-modal has-modal-card
|
<b-modal has-modal-card
|
||||||
:active.sync="overnightTaskShowDialog">
|
:active.sync="overnightTaskShowDialog">
|
||||||
|
@ -139,48 +147,56 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="block" style="padding-left: 2rem; display: flex;">
|
<div class="block" style="padding-left: 2rem; display: flex;">
|
||||||
|
|
||||||
<b-table :data="backfillTasks">
|
<${b}-table :data="backfillTasks">
|
||||||
<b-table-column field="key"
|
<${b}-table-column field="key"
|
||||||
label="Key"
|
label="Key"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.key }}
|
{{ props.row.key }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="description"
|
<${b}-table-column field="description"
|
||||||
label="Description"
|
label="Description"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.description }}
|
{{ props.row.description }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="script"
|
<${b}-table-column field="script"
|
||||||
label="Script"
|
label="Script"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.script }}
|
{{ props.row.script }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="forward"
|
<${b}-table-column field="forward"
|
||||||
label="Orientation"
|
label="Orientation"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.forward ? "Forward" : "Backward" }}
|
{{ props.row.forward ? "Forward" : "Backward" }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="target_date"
|
<${b}-table-column field="target_date"
|
||||||
label="Target Date"
|
label="Target Date"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.target_date }}
|
{{ props.row.target_date }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column label="Actions"
|
<${b}-table-column label="Actions"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
<a href="#"
|
<a href="#"
|
||||||
@click.prevent="backfillTaskEdit(props.row)">
|
@click.prevent="backfillTaskEdit(props.row)">
|
||||||
<i class="fas fa-edit"></i>
|
% if request.use_oruga:
|
||||||
|
<o-icon icon="edit" />
|
||||||
|
% else:
|
||||||
|
<i class="fas fa-edit"></i>
|
||||||
|
% endif
|
||||||
Edit
|
Edit
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a href="#"
|
<a href="#"
|
||||||
class="has-text-danger"
|
class="has-text-danger"
|
||||||
@click.prevent="backfillTaskDelete(props.row)">
|
@click.prevent="backfillTaskDelete(props.row)">
|
||||||
<i class="fas fa-trash"></i>
|
% if request.use_oruga:
|
||||||
|
<o-icon icon="trash" />
|
||||||
|
% else:
|
||||||
|
<i class="fas fa-trash"></i>
|
||||||
|
% endif
|
||||||
Delete
|
Delete
|
||||||
</a>
|
</a>
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
</b-table>
|
</${b}-table>
|
||||||
|
|
||||||
<b-modal has-modal-card
|
<b-modal has-modal-card
|
||||||
:active.sync="backfillTaskShowDialog">
|
:active.sync="backfillTaskShowDialog">
|
||||||
|
|
|
@ -53,25 +53,25 @@
|
||||||
|
|
||||||
<h3 class="block is-size-3">Overnight Tasks</h3>
|
<h3 class="block is-size-3">Overnight Tasks</h3>
|
||||||
|
|
||||||
<b-table :data="overnightTasks" hoverable>
|
<${b}-table :data="overnightTasks" hoverable>
|
||||||
<b-table-column field="description"
|
<${b}-table-column field="description"
|
||||||
label="Description"
|
label="Description"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.description }}
|
{{ props.row.description }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="script"
|
<${b}-table-column field="script"
|
||||||
label="Command"
|
label="Command"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.script || props.row.class_name }}
|
{{ props.row.script || props.row.class_name }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="last_date"
|
<${b}-table-column field="last_date"
|
||||||
label="Last Date"
|
label="Last Date"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
<span :class="overnightTextClass(props.row)">
|
<span :class="overnightTextClass(props.row)">
|
||||||
{{ props.row.last_date || "never!" }}
|
{{ props.row.last_date || "never!" }}
|
||||||
</span>
|
</span>
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column label="Actions"
|
<${b}-table-column label="Actions"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
<b-button type="is-primary"
|
<b-button type="is-primary"
|
||||||
icon-pack="fas"
|
icon-pack="fas"
|
||||||
|
@ -128,11 +128,11 @@
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</b-modal>
|
</b-modal>
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<template #empty>
|
<template #empty>
|
||||||
<p class="block">No tasks defined.</p>
|
<p class="block">No tasks defined.</p>
|
||||||
</template>
|
</template>
|
||||||
</b-table>
|
</${b}-table>
|
||||||
|
|
||||||
% endif
|
% endif
|
||||||
|
|
||||||
|
@ -140,35 +140,35 @@
|
||||||
|
|
||||||
<h3 class="block is-size-3">Backfill Tasks</h3>
|
<h3 class="block is-size-3">Backfill Tasks</h3>
|
||||||
|
|
||||||
<b-table :data="backfillTasks" hoverable>
|
<${b}-table :data="backfillTasks" hoverable>
|
||||||
<b-table-column field="description"
|
<${b}-table-column field="description"
|
||||||
label="Description"
|
label="Description"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.description }}
|
{{ props.row.description }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="script"
|
<${b}-table-column field="script"
|
||||||
label="Script"
|
label="Script"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.script }}
|
{{ props.row.script }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="forward"
|
<${b}-table-column field="forward"
|
||||||
label="Orientation"
|
label="Orientation"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.forward ? "Forward" : "Backward" }}
|
{{ props.row.forward ? "Forward" : "Backward" }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="last_date"
|
<${b}-table-column field="last_date"
|
||||||
label="Last Date"
|
label="Last Date"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
<span :class="backfillTextClass(props.row)">
|
<span :class="backfillTextClass(props.row)">
|
||||||
{{ props.row.last_date }}
|
{{ props.row.last_date }}
|
||||||
</span>
|
</span>
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="target_date"
|
<${b}-table-column field="target_date"
|
||||||
label="Target Date"
|
label="Target Date"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
{{ props.row.target_date }}
|
{{ props.row.target_date }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column label="Actions"
|
<${b}-table-column label="Actions"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
<b-button type="is-primary"
|
<b-button type="is-primary"
|
||||||
icon-pack="fas"
|
icon-pack="fas"
|
||||||
|
@ -176,11 +176,11 @@
|
||||||
@click="backfillTaskLaunch(props.row)">
|
@click="backfillTaskLaunch(props.row)">
|
||||||
Launch
|
Launch
|
||||||
</b-button>
|
</b-button>
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<template #empty>
|
<template #empty>
|
||||||
<p class="block">No tasks defined.</p>
|
<p class="block">No tasks defined.</p>
|
||||||
</template>
|
</template>
|
||||||
</b-table>
|
</${b}-table>
|
||||||
|
|
||||||
<b-modal has-modal-card
|
<b-modal has-modal-card
|
||||||
:active.sync="backfillTaskShowLaunchDialog">
|
:active.sync="backfillTaskShowLaunchDialog">
|
||||||
|
|
|
@ -177,6 +177,8 @@
|
||||||
|
|
||||||
Vue.component('merge-buttons', MergeButtons)
|
Vue.component('merge-buttons', MergeButtons)
|
||||||
|
|
||||||
|
<% request.register_component('merge-buttons', 'MergeButtons') %>
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,11 @@
|
||||||
<b-button title=""Touch" this record to trigger sync"
|
<b-button title=""Touch" this record to trigger sync"
|
||||||
@click="touchRecord()"
|
@click="touchRecord()"
|
||||||
:disabled="touchSubmitting">
|
:disabled="touchSubmitting">
|
||||||
<span><i class="fa fa-hand-pointer"></i></span>
|
% if request.use_oruga:
|
||||||
|
<o-icon icon="hand-pointer" />
|
||||||
|
% else:
|
||||||
|
<span><i class="fa fa-hand-pointer"></i></span>
|
||||||
|
% endif
|
||||||
</b-button>
|
</b-button>
|
||||||
% endif
|
% endif
|
||||||
% if expose_versions:
|
% if expose_versions:
|
||||||
|
@ -112,7 +116,11 @@
|
||||||
<p class="block">
|
<p class="block">
|
||||||
<a href="${master.get_action_url('versions', instance)}"
|
<a href="${master.get_action_url('versions', instance)}"
|
||||||
target="_blank">
|
target="_blank">
|
||||||
<i class="fas fa-external-link-alt"></i>
|
% if request.use_oruga:
|
||||||
|
<o-icon icon="external-link-alt" />
|
||||||
|
% else:
|
||||||
|
<i class="fas fa-external-link-alt"></i>
|
||||||
|
% endif
|
||||||
View as separate page
|
View as separate page
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
@ -122,7 +130,13 @@
|
||||||
@view-revision="viewRevision">
|
@view-revision="viewRevision">
|
||||||
</versions-grid>
|
</versions-grid>
|
||||||
|
|
||||||
<b-modal :active.sync="viewVersionShowDialog" :width="1200">
|
<${b}-modal :width="1200"
|
||||||
|
% if request.use_oruga:
|
||||||
|
v-model:active="viewVersionShowDialog"
|
||||||
|
% else:
|
||||||
|
:active.sync="viewVersionShowDialog"
|
||||||
|
% endif
|
||||||
|
>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<div style="display: flex; flex-direction: column; gap: 1.5rem;">
|
<div style="display: flex; flex-direction: column; gap: 1.5rem;">
|
||||||
|
@ -169,7 +183,11 @@
|
||||||
<div>
|
<div>
|
||||||
<a :href="viewVersionData.url"
|
<a :href="viewVersionData.url"
|
||||||
target="_blank">
|
target="_blank">
|
||||||
<i class="fas fa-external-link-alt"></i>
|
% if request.use_oruga:
|
||||||
|
<o-icon icon="external-link-alt" />
|
||||||
|
% else:
|
||||||
|
<i class="fas fa-external-link-alt"></i>
|
||||||
|
% endif
|
||||||
View as separate page
|
View as separate page
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -212,10 +230,14 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<b-loading :active.sync="viewVersionLoading" :is-full-page="false"></b-loading>
|
% if request.use_oruga:
|
||||||
|
<o-loading v-model:active="viewVersionLoading" :is-full-page="false" />
|
||||||
|
% else:
|
||||||
|
<b-loading :active.sync="viewVersionLoading" :is-full-page="false"></b-loading>
|
||||||
|
% endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</b-modal>
|
</${b}-modal>
|
||||||
</div>
|
</div>
|
||||||
% endif
|
% endif
|
||||||
</%def>
|
</%def>
|
||||||
|
|
|
@ -72,6 +72,7 @@
|
||||||
ThisPage.data = function() { return ThisPageData }
|
ThisPage.data = function() { return ThisPageData }
|
||||||
|
|
||||||
Vue.component('this-page', ThisPage)
|
Vue.component('this-page', ThisPage)
|
||||||
|
<% request.register_component('this-page', 'ThisPage') %>
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</%def>
|
</%def>
|
||||||
|
|
|
@ -6,10 +6,8 @@
|
||||||
|
|
||||||
% if help_url or help_markdown:
|
% if help_url or help_markdown:
|
||||||
|
|
||||||
<b-field>
|
% if request.use_oruga:
|
||||||
<p class="control">
|
<o-button icon-left="question-circle"
|
||||||
<b-button icon-pack="fas"
|
|
||||||
icon-left="question-circle"
|
|
||||||
% if help_markdown:
|
% if help_markdown:
|
||||||
@click="displayInit()"
|
@click="displayInit()"
|
||||||
% elif help_url:
|
% elif help_url:
|
||||||
|
@ -18,57 +16,117 @@
|
||||||
% endif
|
% endif
|
||||||
>
|
>
|
||||||
Help
|
Help
|
||||||
</b-button>
|
</o-button>
|
||||||
</p>
|
|
||||||
% if can_edit_help:
|
% if can_edit_help:
|
||||||
## TODO: this dropdown is duplicated, below
|
## TODO: this dropdown is duplicated, below
|
||||||
<b-dropdown aria-role="list" position="is-bottom-left">
|
<o-dropdown position="bottom-left"
|
||||||
<template #trigger="{ active }">
|
## TODO: why does click not work here?!
|
||||||
<b-button>
|
:triggers="['click', 'hover']">
|
||||||
<span><i class="fa fa-cog"></i></span>
|
<template #trigger>
|
||||||
</b-button>
|
<o-button>
|
||||||
</template>
|
<o-icon icon="cog" />
|
||||||
<b-dropdown-item aria-role="listitem"
|
</o-button>
|
||||||
@click="configureInit()">
|
</template>
|
||||||
Edit Page Help
|
<o-dropdown-item label="Edit Page Help"
|
||||||
</b-dropdown-item>
|
@click="configureInit()" />
|
||||||
<b-dropdown-item aria-role="listitem"
|
<o-dropdown-item label="Edit Fields Help"
|
||||||
@click="configureFieldsInit()">
|
@click="configureFieldsInit()" />
|
||||||
Edit Fields Help
|
</o-dropdown>
|
||||||
</b-dropdown-item>
|
% endif
|
||||||
</b-dropdown>
|
|
||||||
% endif
|
% else:
|
||||||
</b-field>
|
## buefy
|
||||||
|
<b-field>
|
||||||
|
<p class="control">
|
||||||
|
<b-button icon-pack="fas"
|
||||||
|
icon-left="question-circle"
|
||||||
|
% if help_markdown:
|
||||||
|
@click="displayInit()"
|
||||||
|
% elif help_url:
|
||||||
|
tag="a" href="${help_url}"
|
||||||
|
target="_blank"
|
||||||
|
% endif
|
||||||
|
>
|
||||||
|
Help
|
||||||
|
</b-button>
|
||||||
|
</p>
|
||||||
|
% if can_edit_help:
|
||||||
|
## TODO: this dropdown is duplicated, below
|
||||||
|
<b-dropdown aria-role="list" position="is-bottom-left">
|
||||||
|
<template #trigger="{ active }">
|
||||||
|
<b-button>
|
||||||
|
<span><i class="fa fa-cog"></i></span>
|
||||||
|
</b-button>
|
||||||
|
</template>
|
||||||
|
<b-dropdown-item aria-role="listitem"
|
||||||
|
@click="configureInit()">
|
||||||
|
Edit Page Help
|
||||||
|
</b-dropdown-item>
|
||||||
|
<b-dropdown-item aria-role="listitem"
|
||||||
|
@click="configureFieldsInit()">
|
||||||
|
Edit Fields Help
|
||||||
|
</b-dropdown-item>
|
||||||
|
</b-dropdown>
|
||||||
|
% endif
|
||||||
|
</b-field>
|
||||||
|
% endif:
|
||||||
|
|
||||||
% elif can_edit_help:
|
% elif can_edit_help:
|
||||||
|
|
||||||
<b-field>
|
## TODO: this dropdown is duplicated, above
|
||||||
<p class="control">
|
% if request.use_oruga:
|
||||||
## TODO: this dropdown is duplicated, above
|
<o-dropdown position="bottom-left"
|
||||||
<b-dropdown aria-role="list" position="is-bottom-left">
|
## TODO: why does click not work here?!
|
||||||
<template #trigger="{ active }">
|
:triggers="['click', 'hover']">
|
||||||
<b-button>
|
<template #trigger>
|
||||||
<span><i class="fa fa-question-circle"></i></span>
|
<o-button>
|
||||||
<span><i class="fa fa-cog"></i></span>
|
<o-icon icon="question-circle" />
|
||||||
</b-button>
|
<o-icon icon="cog" />
|
||||||
|
</o-button>
|
||||||
</template>
|
</template>
|
||||||
<b-dropdown-item aria-role="listitem"
|
<o-dropdown-item label="Edit Page Help"
|
||||||
@click="configureInit()">
|
@click="configureInit()" />
|
||||||
Edit Page Help
|
<o-dropdown-item label="Edit Fields Help"
|
||||||
</b-dropdown-item>
|
@click="configureFieldsInit()" />
|
||||||
<b-dropdown-item aria-role="listitem"
|
</o-dropdown>
|
||||||
@click="configureFieldsInit()">
|
% else:
|
||||||
Edit Fields Help
|
<b-field>
|
||||||
</b-dropdown-item>
|
<p class="control">
|
||||||
</b-dropdown>
|
<b-dropdown aria-role="list" position="is-bottom-left">
|
||||||
</p>
|
<template #trigger>
|
||||||
</b-field>
|
<b-button>
|
||||||
|
% if request.use_oruga:
|
||||||
|
<o-icon icon="question-circle" />
|
||||||
|
<o-icon icon="cog" />
|
||||||
|
% else:
|
||||||
|
<span><i class="fa fa-question-circle"></i></span>
|
||||||
|
<span><i class="fa fa-cog"></i></span>
|
||||||
|
% endif
|
||||||
|
</b-button>
|
||||||
|
</template>
|
||||||
|
<b-dropdown-item aria-role="listitem"
|
||||||
|
@click="configureInit()">
|
||||||
|
Edit Page Help
|
||||||
|
</b-dropdown-item>
|
||||||
|
<b-dropdown-item aria-role="listitem"
|
||||||
|
@click="configureFieldsInit()">
|
||||||
|
Edit Fields Help
|
||||||
|
</b-dropdown-item>
|
||||||
|
</b-dropdown>
|
||||||
|
</p>
|
||||||
|
</b-field>
|
||||||
|
% endif
|
||||||
% endif
|
% endif
|
||||||
|
|
||||||
% if help_markdown:
|
% if help_markdown:
|
||||||
<b-modal has-modal-card
|
<${b}-modal has-modal-card
|
||||||
:active.sync="displayShowDialog">
|
% if request.use_oruga:
|
||||||
|
v-model:active="displayShowDialog"
|
||||||
|
% else:
|
||||||
|
:active.sync="displayShowDialog"
|
||||||
|
% endif
|
||||||
|
>
|
||||||
<div class="modal-card">
|
<div class="modal-card">
|
||||||
|
|
||||||
<header class="modal-card-head">
|
<header class="modal-card-head">
|
||||||
|
@ -94,14 +152,23 @@
|
||||||
</b-button>
|
</b-button>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</b-modal>
|
</${b}-modal>
|
||||||
% endif
|
% endif
|
||||||
|
|
||||||
% if can_edit_help:
|
% if can_edit_help:
|
||||||
|
|
||||||
<b-modal has-modal-card
|
<${b}-modal has-modal-card
|
||||||
:active.sync="configureShowDialog">
|
% if request.use_oruga:
|
||||||
<div class="modal-card">
|
v-model:active="configureShowDialog"
|
||||||
|
% else:
|
||||||
|
:active.sync="configureShowDialog"
|
||||||
|
% endif
|
||||||
|
>
|
||||||
|
<div class="modal-card"
|
||||||
|
% if request.use_oruga:
|
||||||
|
style="margin: auto;"
|
||||||
|
% endif
|
||||||
|
>
|
||||||
|
|
||||||
<header class="modal-card-head">
|
<header class="modal-card-head">
|
||||||
<p class="modal-card-title">Configure Help</p>
|
<p class="modal-card-title">Configure Help</p>
|
||||||
|
@ -155,7 +222,7 @@
|
||||||
</b-button>
|
</b-button>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</b-modal>
|
</${b}-modal>
|
||||||
|
|
||||||
% endif
|
% endif
|
||||||
|
|
||||||
|
@ -237,6 +304,7 @@
|
||||||
PageHelp.data = function() { return PageHelpData }
|
PageHelp.data = function() { return PageHelpData }
|
||||||
|
|
||||||
Vue.component('page-help', PageHelp)
|
Vue.component('page-help', PageHelp)
|
||||||
|
<% request.register_component('page-help', 'PageHelp') %>
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</%def>
|
</%def>
|
||||||
|
|
|
@ -242,6 +242,8 @@
|
||||||
|
|
||||||
Vue.component('find-principals', FindPrincipals)
|
Vue.component('find-principals', FindPrincipals)
|
||||||
|
|
||||||
|
<% request.register_component('find-principals', 'FindPrincipals') %>
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
|
|
@ -102,6 +102,8 @@
|
||||||
|
|
||||||
Vue.component('email-preview-tools', EmailPreviewTools)
|
Vue.component('email-preview-tools', EmailPreviewTools)
|
||||||
|
|
||||||
|
<% request.register_component('email-preview-tools', 'EmailPreviewTools') %>
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
|
1141
tailbone/templates/themes/butterball/base.mako
Normal file
1141
tailbone/templates/themes/butterball/base.mako
Normal file
File diff suppressed because it is too large
Load diff
679
tailbone/templates/themes/butterball/buefy-components.mako
Normal file
679
tailbone/templates/themes/butterball/buefy-components.mako
Normal file
|
@ -0,0 +1,679 @@
|
||||||
|
|
||||||
|
<%def name="make_buefy_components()">
|
||||||
|
${self.make_b_autocomplete_component()}
|
||||||
|
${self.make_b_button_component()}
|
||||||
|
${self.make_b_checkbox_component()}
|
||||||
|
${self.make_b_collapse_component()}
|
||||||
|
${self.make_b_datepicker_component()}
|
||||||
|
${self.make_b_dropdown_component()}
|
||||||
|
${self.make_b_dropdown_item_component()}
|
||||||
|
${self.make_b_field_component()}
|
||||||
|
${self.make_b_icon_component()}
|
||||||
|
${self.make_b_input_component()}
|
||||||
|
${self.make_b_loading_component()}
|
||||||
|
${self.make_b_modal_component()}
|
||||||
|
${self.make_b_notification_component()}
|
||||||
|
${self.make_b_select_component()}
|
||||||
|
${self.make_b_steps_component()}
|
||||||
|
${self.make_b_step_item_component()}
|
||||||
|
${self.make_b_table_component()}
|
||||||
|
${self.make_b_table_column_component()}
|
||||||
|
${self.make_once_button_component()}
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="make_b_autocomplete_component()">
|
||||||
|
<script type="text/x-template" id="b-autocomplete-template">
|
||||||
|
<o-autocomplete v-model="buefyValue"
|
||||||
|
:data="data"
|
||||||
|
:field="field"
|
||||||
|
:open-on-focus="openOnFocus"
|
||||||
|
:keep-first="keepFirst"
|
||||||
|
:clearable="clearable"
|
||||||
|
:clear-on-select="clearOnSelect"
|
||||||
|
:formatter="customFormatter"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
@update:model-value="buefyValueUpdated"
|
||||||
|
ref="autocomplete">
|
||||||
|
</o-autocomplete>
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
const BAutocomplete = {
|
||||||
|
template: '#b-autocomplete-template',
|
||||||
|
props: {
|
||||||
|
modelValue: String,
|
||||||
|
data: Array,
|
||||||
|
field: String,
|
||||||
|
openOnFocus: Boolean,
|
||||||
|
keepFirst: Boolean,
|
||||||
|
clearable: Boolean,
|
||||||
|
clearOnSelect: Boolean,
|
||||||
|
customFormatter: null,
|
||||||
|
placeholder: String,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
buefyValue: this.modelValue,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
modelValue(to, from) {
|
||||||
|
if (this.buefyValue != to) {
|
||||||
|
this.buefyValue = to
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
focus() {
|
||||||
|
const input = this.$refs.autocomplete.$el.querySelector('input')
|
||||||
|
input.focus()
|
||||||
|
},
|
||||||
|
buefyValueUpdated(value) {
|
||||||
|
this.$emit('update:modelValue', value)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<% request.register_component('b-autocomplete', 'BAutocomplete') %>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="make_b_button_component()">
|
||||||
|
<script type="text/x-template" id="b-button-template">
|
||||||
|
<o-button :variant="variant"
|
||||||
|
:size="orugaSize"
|
||||||
|
:native-type="nativeType"
|
||||||
|
:tag="tag"
|
||||||
|
:href="href"
|
||||||
|
:icon-left="iconLeft">
|
||||||
|
<slot />
|
||||||
|
</o-button>
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
const BButton = {
|
||||||
|
template: '#b-button-template',
|
||||||
|
props: {
|
||||||
|
type: String,
|
||||||
|
nativeType: String,
|
||||||
|
tag: String,
|
||||||
|
href: String,
|
||||||
|
size: String,
|
||||||
|
iconPack: String, // ignored
|
||||||
|
iconLeft: String,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
orugaSize() {
|
||||||
|
if (this.size) {
|
||||||
|
return this.size.replace(/^is-/, '')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
variant() {
|
||||||
|
if (this.type) {
|
||||||
|
return this.type.replace(/^is-/, '')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<% request.register_component('b-button', 'BButton') %>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="make_b_checkbox_component()">
|
||||||
|
<script type="text/x-template" id="b-checkbox-template">
|
||||||
|
<o-checkbox v-model="orugaValue"
|
||||||
|
@update:model-value="orugaValueUpdated"
|
||||||
|
:name="name"
|
||||||
|
:native-value="nativeValue">
|
||||||
|
<slot />
|
||||||
|
</o-checkbox>
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
const BCheckbox = {
|
||||||
|
template: '#b-checkbox-template',
|
||||||
|
props: {
|
||||||
|
modelValue: null,
|
||||||
|
name: String,
|
||||||
|
nativeValue: null,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
orugaValue: this.modelValue,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
modelValue(to, from) {
|
||||||
|
this.orugaValue = to
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
orugaValueUpdated(value) {
|
||||||
|
this.$emit('update:modelValue', value)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<% request.register_component('b-checkbox', 'BCheckbox') %>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="make_b_collapse_component()">
|
||||||
|
<script type="text/x-template" id="b-collapse-template">
|
||||||
|
<o-collapse :open="open">
|
||||||
|
<slot name="trigger" />
|
||||||
|
<slot />
|
||||||
|
</o-collapse>
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
const BCollapse = {
|
||||||
|
template: '#b-collapse-template',
|
||||||
|
props: {
|
||||||
|
open: Boolean,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<% request.register_component('b-collapse', 'BCollapse') %>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="make_b_datepicker_component()">
|
||||||
|
<script type="text/x-template" id="b-datepicker-template">
|
||||||
|
<o-datepicker :name="name"
|
||||||
|
v-model="buefyValue"
|
||||||
|
@update:model-value="buefyValueUpdated"
|
||||||
|
:value="value"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:date-formatter="dateFormatter"
|
||||||
|
:date-parser="dateParser"
|
||||||
|
:disabled="disabled"
|
||||||
|
:editable="editable"
|
||||||
|
:icon="icon"
|
||||||
|
:close-on-click="false">
|
||||||
|
</o-datepicker>
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
const BDatepicker = {
|
||||||
|
template: '#b-datepicker-template',
|
||||||
|
props: {
|
||||||
|
dateFormatter: null,
|
||||||
|
dateParser: null,
|
||||||
|
disabled: Boolean,
|
||||||
|
editable: Boolean,
|
||||||
|
icon: String,
|
||||||
|
// iconPack: String, // ignored
|
||||||
|
modelValue: Date,
|
||||||
|
name: String,
|
||||||
|
placeholder: String,
|
||||||
|
value: null,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
buefyValue: this.modelValue,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
modelValue(to, from) {
|
||||||
|
if (this.buefyValue != to) {
|
||||||
|
this.buefyValue = to
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
buefyValueUpdated(value) {
|
||||||
|
if (this.modelValue != value) {
|
||||||
|
this.$emit('update:modelValue', value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<% request.register_component('b-datepicker', 'BDatepicker') %>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="make_b_dropdown_component()">
|
||||||
|
<script type="text/x-template" id="b-dropdown-template">
|
||||||
|
<o-dropdown :position="buefyPosition"
|
||||||
|
:triggers="triggers">
|
||||||
|
<slot name="trigger" />
|
||||||
|
<slot />
|
||||||
|
</o-dropdown>
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
const BDropdown = {
|
||||||
|
template: '#b-dropdown-template',
|
||||||
|
props: {
|
||||||
|
position: String,
|
||||||
|
triggers: Array,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
buefyPosition() {
|
||||||
|
if (this.position) {
|
||||||
|
return this.position.replace(/^is-/, '')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<% request.register_component('b-dropdown', 'BDropdown') %>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="make_b_dropdown_item_component()">
|
||||||
|
<script type="text/x-template" id="b-dropdown-item-template">
|
||||||
|
<o-dropdown-item :label="label">
|
||||||
|
<slot />
|
||||||
|
</o-dropdown-item>
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
const BDropdownItem = {
|
||||||
|
template: '#b-dropdown-item-template',
|
||||||
|
props: {
|
||||||
|
label: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<% request.register_component('b-dropdown-item', 'BDropdownItem') %>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="make_b_field_component()">
|
||||||
|
<script type="text/x-template" id="b-field-template">
|
||||||
|
<o-field :grouped="grouped"
|
||||||
|
:label="label"
|
||||||
|
:horizontal="horizontal"
|
||||||
|
:expanded="expanded"
|
||||||
|
:variant="variant">
|
||||||
|
<slot />
|
||||||
|
</o-field>
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
const BField = {
|
||||||
|
template: '#b-field-template',
|
||||||
|
props: {
|
||||||
|
expanded: Boolean,
|
||||||
|
grouped: Boolean,
|
||||||
|
horizontal: Boolean,
|
||||||
|
label: String,
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
variant() {
|
||||||
|
if (this.type) {
|
||||||
|
return this.type.replace(/^is-/, '')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<% request.register_component('b-field', 'BField') %>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="make_b_icon_component()">
|
||||||
|
<script type="text/x-template" id="b-icon-template">
|
||||||
|
<o-icon :icon="icon"
|
||||||
|
:size="orugaSize" />
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
const BIcon = {
|
||||||
|
template: '#b-icon-template',
|
||||||
|
props: {
|
||||||
|
icon: String,
|
||||||
|
size: String,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
orugaSize() {
|
||||||
|
if (this.size) {
|
||||||
|
return this.size.replace(/^is-/, '')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<% request.register_component('b-icon', 'BIcon') %>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="make_b_input_component()">
|
||||||
|
<script type="text/x-template" id="b-input-template">
|
||||||
|
<o-input :type="type"
|
||||||
|
:disabled="disabled"
|
||||||
|
v-model="buefyValue"
|
||||||
|
@update:modelValue="val => $emit('update:modelValue', val)"
|
||||||
|
autocomplete="off"
|
||||||
|
ref="input">
|
||||||
|
<slot />
|
||||||
|
</o-input>
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
const BInput = {
|
||||||
|
template: '#b-input-template',
|
||||||
|
props: {
|
||||||
|
modelValue: null,
|
||||||
|
type: String,
|
||||||
|
disabled: Boolean,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
buefyValue: this.modelValue
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
modelValue(to, from) {
|
||||||
|
if (this.buefyValue != to) {
|
||||||
|
this.buefyValue = to
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
focus() {
|
||||||
|
if (this.type == 'textarea') {
|
||||||
|
// TODO: this does not work right
|
||||||
|
this.$refs.input.$el.querySelector('textarea').focus()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<% request.register_component('b-input', 'BInput') %>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="make_b_loading_component()">
|
||||||
|
<script type="text/x-template" id="b-loading-template">
|
||||||
|
<o-loading>
|
||||||
|
<slot />
|
||||||
|
</o-loading>
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
const BLoading = {
|
||||||
|
template: '#b-loading-template',
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<% request.register_component('b-loading', 'BLoading') %>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="make_b_modal_component()">
|
||||||
|
<script type="text/x-template" id="b-modal-template">
|
||||||
|
<o-modal v-model:active="trueActive"
|
||||||
|
@update:active="activeChanged">
|
||||||
|
<slot />
|
||||||
|
</o-modal>
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
const BModal = {
|
||||||
|
template: '#b-modal-template',
|
||||||
|
props: {
|
||||||
|
active: Boolean,
|
||||||
|
hasModalCard: Boolean, // nb. this is ignored
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
trueActive: this.active,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
active(to, from) {
|
||||||
|
this.trueActive = to
|
||||||
|
},
|
||||||
|
trueActive(to, from) {
|
||||||
|
if (this.active != to) {
|
||||||
|
this.tellParent(to)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
|
||||||
|
tellParent(active) {
|
||||||
|
// TODO: this does not work properly
|
||||||
|
this.$emit('update:active', active)
|
||||||
|
},
|
||||||
|
|
||||||
|
activeChanged(active) {
|
||||||
|
this.tellParent(active)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<% request.register_component('b-modal', 'BModal') %>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="make_b_notification_component()">
|
||||||
|
<script type="text/x-template" id="b-notification-template">
|
||||||
|
<o-notification :variant="variant"
|
||||||
|
:closable="closable">
|
||||||
|
<slot />
|
||||||
|
</o-notification>
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
const BNotification = {
|
||||||
|
template: '#b-notification-template',
|
||||||
|
props: {
|
||||||
|
type: String,
|
||||||
|
closable: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
variant() {
|
||||||
|
if (this.type) {
|
||||||
|
return this.type.replace(/^is-/, '')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<% request.register_component('b-notification', 'BNotification') %>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="make_b_select_component()">
|
||||||
|
<script type="text/x-template" id="b-select-template">
|
||||||
|
<o-select :name="name"
|
||||||
|
v-model="orugaValue"
|
||||||
|
@update:model-value="orugaValueUpdated"
|
||||||
|
:expanded="expanded"
|
||||||
|
:multiple="multiple"
|
||||||
|
:size="orugaSize"
|
||||||
|
:native-size="nativeSize">
|
||||||
|
<slot />
|
||||||
|
</o-select>
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
const BSelect = {
|
||||||
|
template: '#b-select-template',
|
||||||
|
props: {
|
||||||
|
expanded: Boolean,
|
||||||
|
modelValue: null,
|
||||||
|
multiple: Boolean,
|
||||||
|
name: String,
|
||||||
|
nativeSize: null,
|
||||||
|
size: null,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
orugaValue: this.modelValue,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
modelValue(to, from) {
|
||||||
|
this.orugaValue = to
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
orugaSize() {
|
||||||
|
if (this.size) {
|
||||||
|
return this.size.replace(/^is-/, '')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
orugaValueUpdated(value) {
|
||||||
|
this.$emit('update:modelValue', value)
|
||||||
|
this.$emit('input', value)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<% request.register_component('b-select', 'BSelect') %>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="make_b_steps_component()">
|
||||||
|
<script type="text/x-template" id="b-steps-template">
|
||||||
|
<o-steps v-model="orugaValue"
|
||||||
|
@update:model-value="orugaValueUpdated"
|
||||||
|
:animated="animated"
|
||||||
|
:rounded="rounded"
|
||||||
|
:has-navigation="hasNavigation"
|
||||||
|
:vertical="vertical">
|
||||||
|
<slot />
|
||||||
|
</o-steps>
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
const BSteps = {
|
||||||
|
template: '#b-steps-template',
|
||||||
|
props: {
|
||||||
|
modelValue: null,
|
||||||
|
animated: Boolean,
|
||||||
|
rounded: Boolean,
|
||||||
|
hasNavigation: Boolean,
|
||||||
|
vertical: Boolean,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
orugaValue: this.modelValue,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
modelValue(to, from) {
|
||||||
|
this.orugaValue = to
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
orugaValueUpdated(value) {
|
||||||
|
this.$emit('update:modelValue', value)
|
||||||
|
this.$emit('input', value)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<% request.register_component('b-steps', 'BSteps') %>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="make_b_step_item_component()">
|
||||||
|
<script type="text/x-template" id="b-step-item-template">
|
||||||
|
<o-step-item :step="step"
|
||||||
|
:value="value"
|
||||||
|
:label="label"
|
||||||
|
:clickable="clickable">
|
||||||
|
<slot />
|
||||||
|
</o-step-item>
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
const BStepItem = {
|
||||||
|
template: '#b-step-item-template',
|
||||||
|
props: {
|
||||||
|
step: null,
|
||||||
|
value: null,
|
||||||
|
label: String,
|
||||||
|
clickable: Boolean,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<% request.register_component('b-step-item', 'BStepItem') %>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="make_b_table_component()">
|
||||||
|
<script type="text/x-template" id="b-table-template">
|
||||||
|
<o-table :data="data">
|
||||||
|
<slot />
|
||||||
|
</o-table>
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
const BTable = {
|
||||||
|
template: '#b-table-template',
|
||||||
|
props: {
|
||||||
|
data: Array,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<% request.register_component('b-table', 'BTable') %>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="make_b_table_column_component()">
|
||||||
|
<script type="text/x-template" id="b-table-column-template">
|
||||||
|
<o-table-column :field="field"
|
||||||
|
:label="label"
|
||||||
|
v-slot="props">
|
||||||
|
## TODO: this does not seem to really work for us...
|
||||||
|
<slot :props="props" />
|
||||||
|
</o-table-column>
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
const BTableColumn = {
|
||||||
|
template: '#b-table-column-template',
|
||||||
|
props: {
|
||||||
|
field: String,
|
||||||
|
label: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<% request.register_component('b-table-column', 'BTableColumn') %>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="make_once_button_component()">
|
||||||
|
<script type="text/x-template" id="once-button-template">
|
||||||
|
<b-button :type="type"
|
||||||
|
:native-type="nativeType"
|
||||||
|
:tag="tag"
|
||||||
|
:href="href"
|
||||||
|
:title="title"
|
||||||
|
:disabled="buttonDisabled"
|
||||||
|
@click="clicked"
|
||||||
|
icon-pack="fas"
|
||||||
|
:icon-left="iconLeft">
|
||||||
|
{{ buttonText }}
|
||||||
|
</b-button>
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
const OnceButton = {
|
||||||
|
template: '#once-button-template',
|
||||||
|
props: {
|
||||||
|
type: String,
|
||||||
|
nativeType: String,
|
||||||
|
tag: String,
|
||||||
|
href: String,
|
||||||
|
text: String,
|
||||||
|
title: String,
|
||||||
|
iconLeft: String,
|
||||||
|
working: String,
|
||||||
|
workingText: String,
|
||||||
|
disabled: Boolean,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentText: null,
|
||||||
|
currentDisabled: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
buttonText: function() {
|
||||||
|
return this.currentText || this.text
|
||||||
|
},
|
||||||
|
buttonDisabled: function() {
|
||||||
|
if (this.currentDisabled !== null) {
|
||||||
|
return this.currentDisabled
|
||||||
|
}
|
||||||
|
return this.disabled
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
|
||||||
|
clicked(event) {
|
||||||
|
this.currentDisabled = true
|
||||||
|
if (this.workingText) {
|
||||||
|
this.currentText = this.workingText
|
||||||
|
} else if (this.working) {
|
||||||
|
this.currentText = this.working + ", please wait..."
|
||||||
|
} else {
|
||||||
|
this.currentText = "Working, please wait..."
|
||||||
|
}
|
||||||
|
// this.$nextTick(function() {
|
||||||
|
// this.$emit('click', event)
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<% request.register_component('once-button', 'OnceButton') %>
|
||||||
|
</%def>
|
32
tailbone/templates/themes/butterball/buefy-plugin.mako
Normal file
32
tailbone/templates/themes/butterball/buefy-plugin.mako
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
|
||||||
|
<%def name="make_buefy_plugin()">
|
||||||
|
<script>
|
||||||
|
|
||||||
|
const BuefyPlugin = {
|
||||||
|
install(app, options) {
|
||||||
|
app.config.globalProperties.$buefy = {
|
||||||
|
|
||||||
|
toast: {
|
||||||
|
open(options) {
|
||||||
|
|
||||||
|
let variant = null
|
||||||
|
if (options.type) {
|
||||||
|
variant = options.type.replace(/^is-/, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
const opts = {
|
||||||
|
duration: options.duration,
|
||||||
|
message: options.message,
|
||||||
|
position: 'top',
|
||||||
|
variant,
|
||||||
|
}
|
||||||
|
|
||||||
|
const oruga = app.config.globalProperties.$oruga
|
||||||
|
oruga.notification.open(opts)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</%def>
|
382
tailbone/templates/themes/butterball/field-components.mako
Normal file
382
tailbone/templates/themes/butterball/field-components.mako
Normal file
|
@ -0,0 +1,382 @@
|
||||||
|
## -*- coding: utf-8; -*-
|
||||||
|
|
||||||
|
<%def name="make_field_components()">
|
||||||
|
${self.make_tailbone_autocomplete_component()}
|
||||||
|
${self.make_tailbone_datepicker_component()}
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="make_tailbone_autocomplete_component()">
|
||||||
|
<% request.register_component('tailbone-autocomplete', 'TailboneAutocomplete') %>
|
||||||
|
<script type="text/x-template" id="tailbone-autocomplete-template">
|
||||||
|
<div>
|
||||||
|
|
||||||
|
<o-button v-if="modelValue"
|
||||||
|
style="width: 100%; justify-content: left;"
|
||||||
|
@click="clearSelection(true)"
|
||||||
|
expanded>
|
||||||
|
{{ internalLabel }} (click to change #1)
|
||||||
|
</o-button>
|
||||||
|
|
||||||
|
<o-autocomplete ref="autocompletex"
|
||||||
|
v-show="!modelValue"
|
||||||
|
v-model="orugaValue"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:data="filteredData"
|
||||||
|
:field="field"
|
||||||
|
:formatter="customFormatter"
|
||||||
|
@input="inputChanged"
|
||||||
|
@select="optionSelected"
|
||||||
|
keep-first
|
||||||
|
open-on-focus
|
||||||
|
expanded
|
||||||
|
:clearable="clearable"
|
||||||
|
:clear-on-select="clearOnSelect">
|
||||||
|
<template #default="{ option }">
|
||||||
|
{{ option.label }}
|
||||||
|
</template>
|
||||||
|
</o-autocomplete>
|
||||||
|
|
||||||
|
<input type="hidden" :name="name" :value="modelValue" />
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
const TailboneAutocomplete = {
|
||||||
|
template: '#tailbone-autocomplete-template',
|
||||||
|
|
||||||
|
props: {
|
||||||
|
|
||||||
|
// this is the "input" field name essentially. primarily
|
||||||
|
// is useful for "traditional" tailbone forms; it normally
|
||||||
|
// is not used otherwise. it is passed as-is to the oruga
|
||||||
|
// autocomplete component `name` prop
|
||||||
|
name: String,
|
||||||
|
|
||||||
|
// static data set; used when serviceUrl is not provided
|
||||||
|
data: Array,
|
||||||
|
|
||||||
|
// the url from which search results are to be obtained. the
|
||||||
|
// url should expect a GET request with a query string with a
|
||||||
|
// single `term` parameter, and return results as a JSON array
|
||||||
|
// containing objects with `value` and `label` properties.
|
||||||
|
serviceUrl: String,
|
||||||
|
|
||||||
|
// callers do not specify this directly but rather by way of
|
||||||
|
// the `v-model` directive. this component will emit `input`
|
||||||
|
// events when the value changes
|
||||||
|
modelValue: String,
|
||||||
|
|
||||||
|
// callers may set an initial label if needed. this is useful
|
||||||
|
// in cases where the autocomplete needs to "already have a
|
||||||
|
// value" on page load. for instance when a user fills out
|
||||||
|
// the autocomplete field, but leaves other required fields
|
||||||
|
// blank and submits the form; page will re-load showing
|
||||||
|
// errors but the autocomplete field should remain "set" -
|
||||||
|
// normally it is only given a "value" (e.g. uuid) but this
|
||||||
|
// allows for the "label" to display correctly as well
|
||||||
|
initialLabel: String,
|
||||||
|
|
||||||
|
// while the `initialLabel` above is useful for setting the
|
||||||
|
// *initial* label (of course), it cannot be used to
|
||||||
|
// arbitrarily update the label during the component's life.
|
||||||
|
// if you do need to *update* the label after initial page
|
||||||
|
// load, then you should set `assignedLabel` instead. one
|
||||||
|
// place this happens is in /custorders/create page, where
|
||||||
|
// product autocomplete shows some results, and user clicks
|
||||||
|
// one, but then handler logic can forcibly "swap" the
|
||||||
|
// selection, causing *different* product data to come back
|
||||||
|
// from the server, and autocomplete label should be updated
|
||||||
|
// to match. this feels a bit awkward still but does work..
|
||||||
|
assignedLabel: String,
|
||||||
|
|
||||||
|
// simple placeholder text for the input box
|
||||||
|
placeholder: String,
|
||||||
|
|
||||||
|
// these are passed as-is to <o-autocomplete>
|
||||||
|
clearable: Boolean,
|
||||||
|
clearOnSelect: Boolean,
|
||||||
|
customFormatter: null,
|
||||||
|
expanded: Boolean,
|
||||||
|
field: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
|
||||||
|
const internalLabel = this.assignedLabel || this.initialLabel
|
||||||
|
|
||||||
|
// we want to track the "currently selected option" - which
|
||||||
|
// should normally be `null` to begin with, unless we were
|
||||||
|
// given a value, in which case we use `initialLabel` to
|
||||||
|
// complete the option
|
||||||
|
let selected = null
|
||||||
|
if (this.modelValue) {
|
||||||
|
selected = {
|
||||||
|
value: this.modelValue,
|
||||||
|
label: internalLabel,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
|
||||||
|
// this contains the search results; its contents may
|
||||||
|
// change over time as new searches happen. the
|
||||||
|
// "currently selected option" should be one of these,
|
||||||
|
// unless it is null
|
||||||
|
fetchedData: [],
|
||||||
|
|
||||||
|
// this tracks our "currently selected option" - per above
|
||||||
|
selected,
|
||||||
|
|
||||||
|
// since we are wrapping a component which also makes
|
||||||
|
// use of the "value" paradigm, we must separate the
|
||||||
|
// concerns. so we use our own `modelValue` prop to
|
||||||
|
// interact with the caller, but then we use this
|
||||||
|
// `orugaValue` data point to communicate with the
|
||||||
|
// oruga autocomplete component. note that
|
||||||
|
// `this.modelValue` will always be either a uuid or
|
||||||
|
// null, whereas `this.orugaValue` may be raw text as
|
||||||
|
// entered by the user.
|
||||||
|
// orugaValue: this.modelValue,
|
||||||
|
orugaValue: null,
|
||||||
|
|
||||||
|
// this stores the "internal" label for the button
|
||||||
|
internalLabel,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
|
||||||
|
filteredData() {
|
||||||
|
|
||||||
|
// do not filter if data comes from backend
|
||||||
|
if (this.serviceUrl) {
|
||||||
|
return this.fetchedData
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.orugaValue || !this.orugaValue.length) {
|
||||||
|
return this.data
|
||||||
|
}
|
||||||
|
|
||||||
|
const terms = []
|
||||||
|
for (let term of this.orugaValue.toLowerCase().split(' ')) {
|
||||||
|
term = term.trim()
|
||||||
|
if (term) {
|
||||||
|
terms.push(term)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!terms.length) {
|
||||||
|
return this.data
|
||||||
|
}
|
||||||
|
|
||||||
|
// all terms must match
|
||||||
|
return this.data.filter((option) => {
|
||||||
|
const label = option.label.toLowerCase()
|
||||||
|
for (const term of terms) {
|
||||||
|
if (label.indexOf(term) < 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
|
||||||
|
assignedLabel(to, from) {
|
||||||
|
// update button label when caller changes it
|
||||||
|
this.internalLabel = to
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
|
||||||
|
inputChanged(entry) {
|
||||||
|
if (this.serviceUrl) {
|
||||||
|
this.getAsyncData(entry)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// fetch new search results from the server. this is
|
||||||
|
// invoked via the `@input` event from oruga autocomplete
|
||||||
|
// component.
|
||||||
|
getAsyncData(entry) {
|
||||||
|
|
||||||
|
// since the `@input` event from oruga component does
|
||||||
|
// not "self-regulate" in any way (?), we skip the
|
||||||
|
// search unless we have at least 3 characters of
|
||||||
|
// input from user
|
||||||
|
if (entry.length < 3) {
|
||||||
|
this.fetchedData = []
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// and perform the search
|
||||||
|
this.$http.get(this.serviceUrl + '?term=' + encodeURIComponent(entry))
|
||||||
|
.then(({ data }) => {
|
||||||
|
this.fetchedData = data
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.fetchedData = []
|
||||||
|
throw error
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// this method is invoked via the `@select` event of the
|
||||||
|
// oruga autocomplete component. the `option` received
|
||||||
|
// will be one of:
|
||||||
|
// - object with (at least) `value` and `label` keys
|
||||||
|
// - simple string (e.g. when data set is static)
|
||||||
|
// - null
|
||||||
|
optionSelected(option) {
|
||||||
|
|
||||||
|
this.selected = option
|
||||||
|
this.internalLabel = option?.label || option
|
||||||
|
|
||||||
|
// reset the internal value for oruga autocomplete
|
||||||
|
// component. note that this value will normally hold
|
||||||
|
// either the raw text entered by the user, or a uuid.
|
||||||
|
// we will not be needing either of those b/c they are
|
||||||
|
// not visible to user once selection is made, and if
|
||||||
|
// the selection is cleared we want user to start over
|
||||||
|
// anyway
|
||||||
|
this.orugaValue = null
|
||||||
|
|
||||||
|
// here is where we alert callers to the new value
|
||||||
|
if (option) {
|
||||||
|
this.$emit('newLabel', option.label)
|
||||||
|
}
|
||||||
|
const value = option?.[this.field || 'value'] || option
|
||||||
|
this.$emit('update:modelValue', value)
|
||||||
|
// this.$emit('select', option)
|
||||||
|
// this.$emit('input', value)
|
||||||
|
},
|
||||||
|
|
||||||
|
## // set selection to the given option, which should a simple
|
||||||
|
## // object with (at least) `value` and `label` properties
|
||||||
|
## setSelection(option) {
|
||||||
|
## this.$refs.autocomplete.setSelected(option)
|
||||||
|
## },
|
||||||
|
|
||||||
|
// clear the field of any value, i.e. set the "currently
|
||||||
|
// selected option" to null. this is invoked when you click
|
||||||
|
// the button, which is visible while the field has a value.
|
||||||
|
// but callers can invoke it directly as well.
|
||||||
|
clearSelection(focus) {
|
||||||
|
|
||||||
|
this.$emit('update:modelValue', null)
|
||||||
|
this.$emit('input', null)
|
||||||
|
this.$emit('newLabel', null)
|
||||||
|
this.internalLabel = null
|
||||||
|
this.selected = null
|
||||||
|
this.orugaValue = null
|
||||||
|
|
||||||
|
## // clear selection for the oruga autocomplete component
|
||||||
|
## this.$refs.autocomplete.setSelected(null)
|
||||||
|
|
||||||
|
// maybe set focus to our (autocomplete) component
|
||||||
|
if (focus) {
|
||||||
|
this.$nextTick(function() {
|
||||||
|
this.focus()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// set focus to this component, which will just set focus
|
||||||
|
// to the oruga autocomplete component
|
||||||
|
focus() {
|
||||||
|
// TODO: why is this ref null?!
|
||||||
|
if (this.$refs.autocompletex) {
|
||||||
|
this.$refs.autocompletex.focus()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// returns the "raw" user input from the underlying oruga
|
||||||
|
// autocomplete component
|
||||||
|
getUserInput() {
|
||||||
|
return this.orugaValue
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="make_tailbone_datepicker_component()">
|
||||||
|
<% request.register_component('tailbone-datepicker', 'TailboneDatepicker') %>
|
||||||
|
<script type="text/x-template" id="tailbone-datepicker-template">
|
||||||
|
<o-datepicker placeholder="Click to select ..."
|
||||||
|
icon="calendar-alt"
|
||||||
|
:date-formatter="formatDate"
|
||||||
|
:date-parser="parseDate"
|
||||||
|
v-model="orugaValue"
|
||||||
|
@update:model-value="orugaValueUpdated"
|
||||||
|
:disabled="disabled"
|
||||||
|
ref="trueDatePicker">
|
||||||
|
</o-datepicker>
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
const TailboneDatepicker = {
|
||||||
|
template: '#tailbone-datepicker-template',
|
||||||
|
|
||||||
|
props: {
|
||||||
|
modelValue: Date,
|
||||||
|
disabled: Boolean,
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
orugaValue: this.parseDate(this.modelValue),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
modelValue(to, from) {
|
||||||
|
if (this.orugaValue != to) {
|
||||||
|
this.orugaValue = to
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
|
||||||
|
formatDate(date) {
|
||||||
|
if (date === null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
// just need to convert to simple ISO date format here, seems
|
||||||
|
// like there should be a more obvious way to do that?
|
||||||
|
var year = date.getFullYear()
|
||||||
|
var month = date.getMonth() + 1
|
||||||
|
var day = date.getDate()
|
||||||
|
month = month < 10 ? '0' + month : month
|
||||||
|
day = day < 10 ? '0' + day : day
|
||||||
|
return year + '-' + month + '-' + day
|
||||||
|
},
|
||||||
|
|
||||||
|
parseDate(value) {
|
||||||
|
if (typeof(value) == 'object') {
|
||||||
|
// nb. we are assuming it is a Date here
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
if (value) {
|
||||||
|
// note, this assumes classic YYYY-MM-DD (i.e. ISO?) format
|
||||||
|
const parts = value.split('-')
|
||||||
|
return new Date(parts[0], parseInt(parts[1]) - 1, parts[2])
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
|
||||||
|
orugaValueUpdated(date) {
|
||||||
|
this.$emit('update:modelValue', date)
|
||||||
|
},
|
||||||
|
|
||||||
|
focus() {
|
||||||
|
this.$refs.trueDatePicker.focus()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</%def>
|
100
tailbone/templates/themes/butterball/http-plugin.mako
Normal file
100
tailbone/templates/themes/butterball/http-plugin.mako
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
|
||||||
|
<%def name="make_http_plugin()">
|
||||||
|
<script>
|
||||||
|
|
||||||
|
const HttpPlugin = {
|
||||||
|
|
||||||
|
install(app, options) {
|
||||||
|
app.config.globalProperties.$http = {
|
||||||
|
|
||||||
|
get(url, options) {
|
||||||
|
if (options === undefined) {
|
||||||
|
options = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.params) {
|
||||||
|
// convert params to query string
|
||||||
|
const data = new URLSearchParams()
|
||||||
|
for (let [key, value] of Object.entries(options.params)) {
|
||||||
|
// nb. all values get converted to string here, so
|
||||||
|
// fallback to empty string to avoid null value
|
||||||
|
// from being interpreted as "null" string
|
||||||
|
if (value === null) {
|
||||||
|
value = ''
|
||||||
|
}
|
||||||
|
data.append(key, value)
|
||||||
|
}
|
||||||
|
// TODO: this should be smarter in case query string already exists
|
||||||
|
url += '?' + data.toString()
|
||||||
|
// params is not a valid arg for options to fetch()
|
||||||
|
delete options.params
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
fetch(url, options).then(response => {
|
||||||
|
// original response does not contain 'data'
|
||||||
|
// attribute, so must use a "mock" response
|
||||||
|
// which does contain everything
|
||||||
|
response.json().then(json => {
|
||||||
|
resolve({
|
||||||
|
data: json,
|
||||||
|
headers: response.headers,
|
||||||
|
ok: response.ok,
|
||||||
|
redirected: response.redirected,
|
||||||
|
status: response.status,
|
||||||
|
statusText: response.statusText,
|
||||||
|
type: response.type,
|
||||||
|
url: response.url,
|
||||||
|
})
|
||||||
|
}, json => {
|
||||||
|
reject(response)
|
||||||
|
})
|
||||||
|
}, response => {
|
||||||
|
reject(response)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
post(url, params, options) {
|
||||||
|
|
||||||
|
if (params) {
|
||||||
|
|
||||||
|
// attach params as json
|
||||||
|
options.body = JSON.stringify(params)
|
||||||
|
|
||||||
|
// and declare content-type
|
||||||
|
options.headers = new Headers(options.headers)
|
||||||
|
options.headers.append('Content-Type', 'application/json')
|
||||||
|
}
|
||||||
|
|
||||||
|
options.method = 'POST'
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
fetch(url, options).then(response => {
|
||||||
|
// original response does not contain 'data'
|
||||||
|
// attribute, so must use a "mock" response
|
||||||
|
// which does contain everything
|
||||||
|
response.json().then(json => {
|
||||||
|
resolve({
|
||||||
|
data: json,
|
||||||
|
headers: response.headers,
|
||||||
|
ok: response.ok,
|
||||||
|
redirected: response.redirected,
|
||||||
|
status: response.status,
|
||||||
|
statusText: response.statusText,
|
||||||
|
type: response.type,
|
||||||
|
url: response.url,
|
||||||
|
})
|
||||||
|
}, json => {
|
||||||
|
reject(response)
|
||||||
|
})
|
||||||
|
}, response => {
|
||||||
|
reject(response)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</%def>
|
244
tailbone/templates/themes/butterball/progress.mako
Normal file
244
tailbone/templates/themes/butterball/progress.mako
Normal file
|
@ -0,0 +1,244 @@
|
||||||
|
## -*- coding: utf-8; -*-
|
||||||
|
<%namespace name="base_meta" file="/base_meta.mako" />
|
||||||
|
<%namespace file="/base.mako" import="core_javascript" />
|
||||||
|
<%namespace file="/base.mako" import="core_styles" />
|
||||||
|
<%namespace file="/http-plugin.mako" import="make_http_plugin" />
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
|
||||||
|
${base_meta.favicon()}
|
||||||
|
<title>${initial_msg or "Working"}...</title>
|
||||||
|
${core_javascript()}
|
||||||
|
${core_styles()}
|
||||||
|
${self.extra_styles()}
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="app" style="height: 100%; display: flex; flex-direction: column; justify-content: space-between;">
|
||||||
|
<whole-page></whole-page>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
${make_http_plugin()}
|
||||||
|
${self.make_whole_page_component()}
|
||||||
|
${self.modify_whole_page_vars()}
|
||||||
|
${self.make_whole_page_app()}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
<%def name="extra_styles()"></%def>
|
||||||
|
|
||||||
|
<%def name="make_whole_page_component()">
|
||||||
|
<script type="text/x-template" id="whole-page-template">
|
||||||
|
<section class="hero is-fullheight">
|
||||||
|
<div class="hero-body">
|
||||||
|
<div class="container">
|
||||||
|
|
||||||
|
<div style="display: flex; flex-direction: column; justify-content: center;">
|
||||||
|
<div style="margin: auto; display: flex; gap: 1rem; align-items: end;">
|
||||||
|
|
||||||
|
<div style="display: flex; flex-direction: column; gap: 1rem;">
|
||||||
|
|
||||||
|
<div style="display: flex; gap: 3rem;">
|
||||||
|
<span>{{ progressMessage }} ... {{ totalDisplay }}</span>
|
||||||
|
<span>{{ percentageDisplay }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="display: flex; gap: 1rem; align-items: center;">
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<progress class="progress is-large"
|
||||||
|
style="width: 400px;"
|
||||||
|
:max="progressMax"
|
||||||
|
:value="progressValue" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
% if can_cancel:
|
||||||
|
<o-button v-show="canCancel"
|
||||||
|
@click="cancelProgress()"
|
||||||
|
:disabled="cancelingProgress"
|
||||||
|
icon-left="ban">
|
||||||
|
{{ cancelingProgress ? "Canceling, please wait..." : "Cancel" }}
|
||||||
|
</o-button>
|
||||||
|
% endif
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
${self.after_progress()}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
const WholePage = {
|
||||||
|
template: '#whole-page-template',
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
|
||||||
|
percentageDisplay() {
|
||||||
|
if (!this.progressMax) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const percent = this.progressValue / this.progressMax
|
||||||
|
return percent.toLocaleString(undefined, {
|
||||||
|
style: 'percent',
|
||||||
|
minimumFractionDigits: 0})
|
||||||
|
},
|
||||||
|
|
||||||
|
totalDisplay() {
|
||||||
|
|
||||||
|
% if can_cancel:
|
||||||
|
if (!this.stillInProgress && !this.cancelingProgress) {
|
||||||
|
return "done!"
|
||||||
|
}
|
||||||
|
% else:
|
||||||
|
if (!this.stillInProgress) {
|
||||||
|
return "done!"
|
||||||
|
}
|
||||||
|
% endif
|
||||||
|
|
||||||
|
if (this.progressMaxDisplay) {
|
||||||
|
return `(${'$'}{this.progressMaxDisplay} total)`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
|
||||||
|
// fetch first progress data, one second from now
|
||||||
|
setTimeout(() => {
|
||||||
|
this.updateProgress()
|
||||||
|
}, 1000)
|
||||||
|
|
||||||
|
// custom logic if applicable
|
||||||
|
this.mountedCustom()
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
|
||||||
|
mountedCustom() {},
|
||||||
|
|
||||||
|
updateProgress() {
|
||||||
|
|
||||||
|
this.$http.get(this.progressURL).then(response => {
|
||||||
|
|
||||||
|
if (response.data.error) {
|
||||||
|
// errors stop the show, we redirect to "cancel" page
|
||||||
|
location.href = '${cancel_url}'
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (response.data.complete || response.data.maximum) {
|
||||||
|
this.progressMessage = response.data.message
|
||||||
|
this.progressMaxDisplay = response.data.maximum_display
|
||||||
|
|
||||||
|
if (response.data.complete) {
|
||||||
|
this.progressValue = this.progressMax
|
||||||
|
this.stillInProgress = false
|
||||||
|
% if can_cancel:
|
||||||
|
this.canCancel = false
|
||||||
|
% endif
|
||||||
|
|
||||||
|
location.href = response.data.success_url
|
||||||
|
|
||||||
|
} else {
|
||||||
|
this.progressValue = response.data.value
|
||||||
|
this.progressMax = response.data.maximum
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// custom logic if applicable
|
||||||
|
this.updateProgressCustom(response)
|
||||||
|
|
||||||
|
if (this.stillInProgress) {
|
||||||
|
|
||||||
|
// fetch progress data again, in one second from now
|
||||||
|
setTimeout(() => {
|
||||||
|
this.updateProgress()
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
updateProgressCustom(response) {},
|
||||||
|
|
||||||
|
% if can_cancel:
|
||||||
|
|
||||||
|
cancelProgress() {
|
||||||
|
|
||||||
|
if (confirm("Do you really wish to cancel this operation?")) {
|
||||||
|
|
||||||
|
this.cancelingProgress = true
|
||||||
|
this.stillInProgress = false
|
||||||
|
|
||||||
|
let params = {cancel_msg: ${json.dumps(cancel_msg)|n}}
|
||||||
|
this.$http.get(this.cancelURL, {params: params}).then(response => {
|
||||||
|
location.href = ${json.dumps(cancel_url)|n}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
% endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const WholePageData = {
|
||||||
|
|
||||||
|
progressURL: '${url('progress', key=progress.key, _query={'sessiontype': progress.session.type})}',
|
||||||
|
progressMessage: "${(initial_msg or "Working").replace('"', '\\"')} (please wait)",
|
||||||
|
progressMax: null,
|
||||||
|
progressMaxDisplay: null,
|
||||||
|
progressValue: null,
|
||||||
|
stillInProgress: true,
|
||||||
|
|
||||||
|
% if can_cancel:
|
||||||
|
canCancel: true,
|
||||||
|
cancelURL: '${url('progress.cancel', key=progress.key, _query={'sessiontype': progress.session.type})}',
|
||||||
|
cancelingProgress: false,
|
||||||
|
% endif
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="after_progress()"></%def>
|
||||||
|
|
||||||
|
<%def name="modify_whole_page_vars()"></%def>
|
||||||
|
|
||||||
|
<%def name="make_whole_page_app()">
|
||||||
|
<script type="module">
|
||||||
|
import {createApp} from 'vue'
|
||||||
|
import {Oruga} from '@oruga-ui/oruga-next'
|
||||||
|
import {bulmaConfig} from '@oruga-ui/theme-bulma'
|
||||||
|
import { library } from "@fortawesome/fontawesome-svg-core"
|
||||||
|
import { fas } from "@fortawesome/free-solid-svg-icons"
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"
|
||||||
|
library.add(fas)
|
||||||
|
|
||||||
|
const app = createApp()
|
||||||
|
|
||||||
|
app.component('vue-fontawesome', FontAwesomeIcon)
|
||||||
|
|
||||||
|
WholePage.data = () => { return WholePageData }
|
||||||
|
app.component('whole-page', WholePage)
|
||||||
|
|
||||||
|
app.use(Oruga, {
|
||||||
|
...bulmaConfig,
|
||||||
|
iconComponent: 'vue-fontawesome',
|
||||||
|
iconPack: 'fas',
|
||||||
|
})
|
||||||
|
|
||||||
|
app.use(HttpPlugin)
|
||||||
|
|
||||||
|
app.mount('#app')
|
||||||
|
</script>
|
||||||
|
</%def>
|
|
@ -7,31 +7,35 @@
|
||||||
<h3 class="is-size-3">Upgradable Systems</h3>
|
<h3 class="is-size-3">Upgradable Systems</h3>
|
||||||
<div class="block" style="padding-left: 2rem; display: flex;">
|
<div class="block" style="padding-left: 2rem; display: flex;">
|
||||||
|
|
||||||
<b-table :data="upgradeSystems"
|
<${b}-table :data="upgradeSystems"
|
||||||
sortable>
|
sortable>
|
||||||
<b-table-column field="key"
|
<${b}-table-column field="key"
|
||||||
label="Key"
|
label="Key"
|
||||||
v-slot="props"
|
v-slot="props"
|
||||||
sortable>
|
sortable>
|
||||||
{{ props.row.key }}
|
{{ props.row.key }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="label"
|
<${b}-table-column field="label"
|
||||||
label="Label"
|
label="Label"
|
||||||
v-slot="props"
|
v-slot="props"
|
||||||
sortable>
|
sortable>
|
||||||
{{ props.row.label }}
|
{{ props.row.label }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column field="command"
|
<${b}-table-column field="command"
|
||||||
label="Command"
|
label="Command"
|
||||||
v-slot="props"
|
v-slot="props"
|
||||||
sortable>
|
sortable>
|
||||||
{{ props.row.command }}
|
{{ props.row.command }}
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
<b-table-column label="Actions"
|
<${b}-table-column label="Actions"
|
||||||
v-slot="props">
|
v-slot="props">
|
||||||
<a href="#"
|
<a href="#"
|
||||||
@click.prevent="upgradeSystemEdit(props.row)">
|
@click.prevent="upgradeSystemEdit(props.row)">
|
||||||
<i class="fas fa-edit"></i>
|
% if request.use_oruga:
|
||||||
|
<o-icon icon="edit" />
|
||||||
|
% else:
|
||||||
|
<i class="fas fa-edit"></i>
|
||||||
|
% endif
|
||||||
Edit
|
Edit
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
@ -39,11 +43,15 @@
|
||||||
v-if="props.row.key != 'rattail'"
|
v-if="props.row.key != 'rattail'"
|
||||||
class="has-text-danger"
|
class="has-text-danger"
|
||||||
@click.prevent="updateSystemDelete(props.row)">
|
@click.prevent="updateSystemDelete(props.row)">
|
||||||
<i class="fas fa-trash"></i>
|
% if request.use_oruga:
|
||||||
|
<o-icon icon="trash" />
|
||||||
|
% else:
|
||||||
|
<i class="fas fa-trash"></i>
|
||||||
|
% endif
|
||||||
Delete
|
Delete
|
||||||
</a>
|
</a>
|
||||||
</b-table-column>
|
</${b}-table-column>
|
||||||
</b-table>
|
</${b}-table>
|
||||||
|
|
||||||
<div style="margin-left: 1rem;">
|
<div style="margin-left: 1rem;">
|
||||||
<b-button type="is-primary"
|
<b-button type="is-primary"
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
################################################################################
|
################################################################################
|
||||||
#
|
#
|
||||||
# Rattail -- Retail Software Framework
|
# Rattail -- Retail Software Framework
|
||||||
# Copyright © 2010-2023 Lance Edgar
|
# Copyright © 2010-2024 Lance Edgar
|
||||||
#
|
#
|
||||||
# This file is part of Rattail.
|
# This file is part of Rattail.
|
||||||
#
|
#
|
||||||
|
@ -31,7 +31,6 @@ import warnings
|
||||||
import humanize
|
import humanize
|
||||||
import markdown
|
import markdown
|
||||||
|
|
||||||
from rattail.time import timezone, make_utc
|
|
||||||
from rattail.files import resource_path
|
from rattail.files import resource_path
|
||||||
|
|
||||||
import colander
|
import colander
|
||||||
|
@ -161,6 +160,25 @@ def get_libver(request, key, fallback=True, default_only=False):
|
||||||
elif key == 'fontawesome':
|
elif key == 'fontawesome':
|
||||||
return '5.3.1'
|
return '5.3.1'
|
||||||
|
|
||||||
|
elif key == 'bb_vue':
|
||||||
|
# TODO: iiuc vue 3.4 does not work with oruga yet
|
||||||
|
return '3.3.11'
|
||||||
|
|
||||||
|
elif key == 'bb_oruga':
|
||||||
|
return '0.8.8'
|
||||||
|
|
||||||
|
elif key in ('bb_oruga_bulma', 'bb_oruga_bulma_css'):
|
||||||
|
return '0.3.0'
|
||||||
|
|
||||||
|
elif key == 'bb_fontawesome_svg_core':
|
||||||
|
return '6.5.2'
|
||||||
|
|
||||||
|
elif key == 'bb_free_solid_svg_icons':
|
||||||
|
return '6.5.2'
|
||||||
|
|
||||||
|
elif key == 'bb_vue_fontawesome':
|
||||||
|
return '3.0.6'
|
||||||
|
|
||||||
|
|
||||||
def get_liburl(request, key, fallback=True):
|
def get_liburl(request, key, fallback=True):
|
||||||
"""
|
"""
|
||||||
|
@ -192,6 +210,27 @@ def get_liburl(request, key, fallback=True):
|
||||||
elif key == 'fontawesome':
|
elif key == 'fontawesome':
|
||||||
return 'https://use.fontawesome.com/releases/v{}/js/all.js'.format(version)
|
return 'https://use.fontawesome.com/releases/v{}/js/all.js'.format(version)
|
||||||
|
|
||||||
|
elif key == 'bb_vue':
|
||||||
|
return f'https://unpkg.com/vue@{version}/dist/vue.esm-browser.js'
|
||||||
|
|
||||||
|
elif key == 'bb_oruga':
|
||||||
|
return f'https://unpkg.com/@oruga-ui/oruga-next@{version}/dist/esm/index.mjs'
|
||||||
|
|
||||||
|
elif key == 'bb_oruga_bulma':
|
||||||
|
return f'https://unpkg.com/@oruga-ui/theme-bulma@{version}/dist/bulma.mjs'
|
||||||
|
|
||||||
|
elif key == 'bb_oruga_bulma_css':
|
||||||
|
return f'https://unpkg.com/@oruga-ui/theme-bulma@{version}/dist/bulma.css'
|
||||||
|
|
||||||
|
elif key == 'bb_fontawesome_svg_core':
|
||||||
|
return f'https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-svg-core@{version}/+esm'
|
||||||
|
|
||||||
|
elif key == 'bb_free_solid_svg_icons':
|
||||||
|
return f'https://cdn.jsdelivr.net/npm/@fortawesome/free-solid-svg-icons@{version}/+esm'
|
||||||
|
|
||||||
|
elif key == 'bb_vue_fontawesome':
|
||||||
|
return f'https://cdn.jsdelivr.net/npm/@fortawesome/vue-fontawesome@{version}/+esm'
|
||||||
|
|
||||||
|
|
||||||
def pretty_datetime(config, value):
|
def pretty_datetime(config, value):
|
||||||
"""
|
"""
|
||||||
|
@ -214,10 +253,10 @@ def pretty_datetime(config, value):
|
||||||
value = app.make_utc(value, tzinfo=True)
|
value = app.make_utc(value, tzinfo=True)
|
||||||
|
|
||||||
# Calculate time diff using UTC.
|
# Calculate time diff using UTC.
|
||||||
time_ago = datetime.datetime.utcnow() - make_utc(value)
|
time_ago = datetime.datetime.utcnow() - app.make_utc(value)
|
||||||
|
|
||||||
# Convert value to local timezone.
|
# Convert value to local timezone.
|
||||||
local = timezone(config)
|
local = app.get_timezone()
|
||||||
value = local.normalize(value.astimezone(local))
|
value = local.normalize(value.astimezone(local))
|
||||||
|
|
||||||
return HTML.tag('span',
|
return HTML.tag('span',
|
||||||
|
@ -246,10 +285,10 @@ def raw_datetime(config, value, verbose=False, as_date=False):
|
||||||
value = app.make_utc(value, tzinfo=True)
|
value = app.make_utc(value, tzinfo=True)
|
||||||
|
|
||||||
# Calculate time diff using UTC.
|
# Calculate time diff using UTC.
|
||||||
time_ago = datetime.datetime.utcnow() - make_utc(value)
|
time_ago = datetime.datetime.utcnow() - app.make_utc(value)
|
||||||
|
|
||||||
# Convert value to local timezone.
|
# Convert value to local timezone.
|
||||||
local = timezone(config)
|
local = app.get_timezone()
|
||||||
value = local.normalize(value.astimezone(local))
|
value = local.normalize(value.astimezone(local))
|
||||||
|
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
|
@ -378,6 +417,18 @@ def get_effective_theme(rattail_config, theme=None, session=None):
|
||||||
return theme
|
return theme
|
||||||
|
|
||||||
|
|
||||||
|
def should_use_oruga(request):
|
||||||
|
"""
|
||||||
|
Returns a flag indicating whether or not the current theme
|
||||||
|
supports (and therefore should use) Oruga + Vue 3 as opposed to
|
||||||
|
the default of Buefy + Vue 2.
|
||||||
|
"""
|
||||||
|
theme = request.registry.settings['tailbone.theme']
|
||||||
|
if theme == 'butterball':
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def validate_email_address(address):
|
def validate_email_address(address):
|
||||||
"""
|
"""
|
||||||
Perform basic validation on the given email address. This leverages the
|
Perform basic validation on the given email address. This leverages the
|
||||||
|
|
|
@ -107,6 +107,13 @@ class AppInfoView(MasterView):
|
||||||
('buefy', "Buefy"),
|
('buefy', "Buefy"),
|
||||||
('buefy.css', "Buefy CSS"),
|
('buefy.css', "Buefy CSS"),
|
||||||
('fontawesome', "FontAwesome"),
|
('fontawesome', "FontAwesome"),
|
||||||
|
('bb_vue', "(BB) vue"),
|
||||||
|
('bb_oruga', "(BB) @oruga-ui/oruga-next"),
|
||||||
|
('bb_oruga_bulma', "(BB) @oruga-ui/theme-bulma (JS)"),
|
||||||
|
('bb_oruga_bulma_css', "(BB) @oruga-ui/theme-bulma (CSS)"),
|
||||||
|
('bb_fontawesome_svg_core', "(BB) @fortawesome/fontawesome-svg-core"),
|
||||||
|
('bb_free_solid_svg_icons', "(BB) @fortawesome/free-solid-svg-icons"),
|
||||||
|
('bb_vue_fontawesome', "(BB) @fortawesome/vue-fontawesome"),
|
||||||
])
|
])
|
||||||
|
|
||||||
for key in weblibs:
|
for key in weblibs:
|
||||||
|
@ -181,6 +188,41 @@ class AppInfoView(MasterView):
|
||||||
{'section': 'tailbone',
|
{'section': 'tailbone',
|
||||||
'option': 'liburl.fontawesome'},
|
'option': 'liburl.fontawesome'},
|
||||||
|
|
||||||
|
{'section': 'tailbone',
|
||||||
|
'option': 'libver.bb_vue'},
|
||||||
|
{'section': 'tailbone',
|
||||||
|
'option': 'liburl.bb_vue'},
|
||||||
|
|
||||||
|
{'section': 'tailbone',
|
||||||
|
'option': 'libver.bb_oruga'},
|
||||||
|
{'section': 'tailbone',
|
||||||
|
'option': 'liburl.bb_oruga'},
|
||||||
|
|
||||||
|
{'section': 'tailbone',
|
||||||
|
'option': 'libver.bb_oruga_bulma'},
|
||||||
|
{'section': 'tailbone',
|
||||||
|
'option': 'liburl.bb_oruga_bulma'},
|
||||||
|
|
||||||
|
{'section': 'tailbone',
|
||||||
|
'option': 'libver.bb_oruga_bulma_css'},
|
||||||
|
{'section': 'tailbone',
|
||||||
|
'option': 'liburl.bb_oruga_bulma_css'},
|
||||||
|
|
||||||
|
{'section': 'tailbone',
|
||||||
|
'option': 'libver.bb_fontawesome_svg_core'},
|
||||||
|
{'section': 'tailbone',
|
||||||
|
'option': 'liburl.bb_fontawesome_svg_core'},
|
||||||
|
|
||||||
|
{'section': 'tailbone',
|
||||||
|
'option': 'libver.bb_free_solid_svg_icons'},
|
||||||
|
{'section': 'tailbone',
|
||||||
|
'option': 'liburl.bb_free_solid_svg_icons'},
|
||||||
|
|
||||||
|
{'section': 'tailbone',
|
||||||
|
'option': 'libver.bb_vue_fontawesome'},
|
||||||
|
{'section': 'tailbone',
|
||||||
|
'option': 'liburl.bb_vue_fontawesome'},
|
||||||
|
|
||||||
# nb. these are no longer used (deprecated), but we keep
|
# nb. these are no longer used (deprecated), but we keep
|
||||||
# them defined here so the tool auto-deletes them
|
# them defined here so the tool auto-deletes them
|
||||||
{'section': 'tailbone',
|
{'section': 'tailbone',
|
||||||
|
|
Loading…
Reference in a new issue