Make all of the DB methods async, and update the dependencies in order to make them work with the async DBs
This commit is contained in:
parent
8a2d946b63
commit
606d6e14ea
11 changed files with 179 additions and 165 deletions
|
|
@ -35,7 +35,7 @@ export class StatSidebar extends HandlebarsApplicationMixin(AbstractSidebarTab)
|
|||
const ctx = await super._prepareContext(options);
|
||||
const db = CONFIG.stats.db;
|
||||
|
||||
ctx.tableCount = db.getTables().length;
|
||||
ctx.tableCount = (await db.getTables()).length;
|
||||
|
||||
const controls = {
|
||||
openStats: { label: `View Stats`, action: `openStats` },
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ export class StatsViewer extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
const tables = new Set();
|
||||
const subtables = {};
|
||||
|
||||
for (const tableConfig of CONFIG.stats.db.getTables()) {
|
||||
for (const tableConfig of await CONFIG.stats.db.getTables()) {
|
||||
const [ table, subtable ] = tableConfig.name.split(`/`);
|
||||
tables.add(table);
|
||||
if (subtable?.length > 0) {
|
||||
|
|
@ -190,8 +190,8 @@ export class StatsViewer extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
_graphData = {};
|
||||
_privacySetting = `my`;
|
||||
async #prepareGraphContext(_ctx) {
|
||||
const table = CONFIG.stats.db.getTable(this.activeTableID);
|
||||
const userData = CONFIG.stats.db.getRows(
|
||||
const table = await CONFIG.stats.db.getTable(this.activeTableID);
|
||||
const userData = await CONFIG.stats.db.getRows(
|
||||
this.activeTableID,
|
||||
this._selectedUsers,
|
||||
this._privacySetting,
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ export class TableCreator extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
ui.notifications.error(`Cannot create a table without a name`);
|
||||
};
|
||||
|
||||
const existing = CONFIG.stats.db.getTable(name);
|
||||
const existing = await CONFIG.stats.db.getTable(name);
|
||||
if (existing) {
|
||||
ui.notifications.error(`A table with the name "${name}" already exists`);
|
||||
return;
|
||||
|
|
@ -128,11 +128,11 @@ export class TableCreator extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
return;
|
||||
};
|
||||
const size = Number(name.replace(`Dice/d`, ``));
|
||||
CONFIG.stats.db.createTable(createDiceTable(size));
|
||||
await CONFIG.stats.db.createTable(createDiceTable(size));
|
||||
return;
|
||||
};
|
||||
|
||||
CONFIG.stats.db.createTable({
|
||||
await CONFIG.stats.db.createTable({
|
||||
name,
|
||||
buckets: {
|
||||
type: this._type,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { BucketTypes } from "../utils/buckets.mjs";
|
||||
import { diceSizeSorter } from "../utils/sorters/diceSize.mjs";
|
||||
import { filePath } from "../consts.mjs";
|
||||
import { Logger } from "../utils/Logger.mjs";
|
||||
|
|
@ -42,23 +43,18 @@ export class TableManager extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
template: filePath(`templates/Apps/common/tableSelect.hbs`),
|
||||
},
|
||||
buckets: {
|
||||
template: filePath(`templates/Apps/TableManager/buckets/empty.hbs`),
|
||||
template: filePath(`templates/Apps/TableManager/buckets.hbs`),
|
||||
templates: [
|
||||
filePath(`templates/Apps/TableManager/buckets/empty.hbs`),
|
||||
...Object.values(BucketTypes).map(
|
||||
(bucketType) => filePath(`templates/Apps/TableManager/buckets/${bucketType}.hbs`),
|
||||
),
|
||||
],
|
||||
},
|
||||
submit: {
|
||||
template: filePath(`templates/Apps/TableManager/submit.hbs`),
|
||||
},
|
||||
};
|
||||
|
||||
_configureRenderOptions(options) {
|
||||
const table = CONFIG.stats.db.getTable(this.activeTableID);
|
||||
|
||||
let bucketType = table?.buckets?.type ?? `empty`;
|
||||
this.constructor.PARTS.buckets = {
|
||||
template: filePath(`templates/Apps/TableManager/buckets/${bucketType}.hbs`),
|
||||
};
|
||||
|
||||
super._configureRenderOptions(options);
|
||||
};
|
||||
// #endregion Options
|
||||
|
||||
// #region Selected Table
|
||||
|
|
@ -84,7 +80,7 @@ export class TableManager extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
async render({ userUpdated, ...opts } = {}) {
|
||||
if (userUpdated) {
|
||||
return;
|
||||
}
|
||||
};
|
||||
await super.render(opts);
|
||||
};
|
||||
|
||||
|
|
@ -140,7 +136,7 @@ export class TableManager extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
const tables = new Set();
|
||||
const subtables = {};
|
||||
|
||||
for (const tableConfig of CONFIG.stats.db.getTables()) {
|
||||
for (const tableConfig of await CONFIG.stats.db.getTables()) {
|
||||
const [ table, subtable ] = tableConfig.name.split(`/`);
|
||||
tables.add(table);
|
||||
if (subtable?.length > 0) {
|
||||
|
|
@ -167,35 +163,46 @@ export class TableManager extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
};
|
||||
|
||||
async #prepareBucketContext(ctx) {
|
||||
const table = CONFIG.stats.db.getTable(this.activeTableID);
|
||||
if (!table) { return };
|
||||
const type = table.buckets.type;
|
||||
const table = await CONFIG.stats.db.getTable(this.activeTableID);
|
||||
const type = table?.buckets?.type ?? `empty`;
|
||||
|
||||
const template = filePath(`templates/Apps/TableManager/buckets/${type}.hbs`);
|
||||
ctx.buckets = {
|
||||
locked: false,
|
||||
template,
|
||||
classes: ``,
|
||||
};
|
||||
|
||||
if (!table) {
|
||||
ctx.buckets.classes = `alert-box warning center`;
|
||||
return;
|
||||
};
|
||||
|
||||
const locked = this._selectedTable === `Dice` || table.buckets.locked;
|
||||
ctx.buckets.locked = locked;
|
||||
if (locked) {
|
||||
ctx.buckets.classes = `alert-box locked`;
|
||||
};
|
||||
|
||||
const capitalizedType = type[0].toUpperCase() + type.slice(1);
|
||||
if (!this[`_prepare${capitalizedType}Context`]) { return };
|
||||
this[`_prepare${capitalizedType}Context`](ctx, table);
|
||||
};
|
||||
|
||||
async _prepareNumberContext(ctx, table) {
|
||||
ctx.buckets = {
|
||||
min: table.buckets.min,
|
||||
max: table.buckets.max,
|
||||
step: table.buckets.step,
|
||||
};
|
||||
ctx.buckets.min = table.buckets.min;
|
||||
ctx.buckets.max = table.buckets.max;
|
||||
ctx.buckets.step = table.buckets.step;
|
||||
};
|
||||
|
||||
async _prepareRangeContext(ctx, table) {
|
||||
ctx.buckets = {
|
||||
locked: this._selectedTable === `Dice` || table.buckets.locked,
|
||||
min: table.buckets.min,
|
||||
max: table.buckets.max,
|
||||
step: table.buckets.step,
|
||||
};
|
||||
ctx.buckets.min = table.buckets.min;
|
||||
ctx.buckets.max = table.buckets.max;
|
||||
ctx.buckets.step = table.buckets.step;
|
||||
};
|
||||
|
||||
async _prepareStringContext(ctx, table) {
|
||||
ctx.buckets = {
|
||||
choices: [...table.buckets.choices],
|
||||
};
|
||||
ctx.buckets.choices = [...table.buckets.choices];
|
||||
};
|
||||
// #endregion Data Prep
|
||||
|
||||
|
|
@ -230,7 +237,7 @@ export class TableManager extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
ui.notifications.info(`Nothing to save`);
|
||||
return;
|
||||
}
|
||||
CONFIG.stats.db.updateTable(this.activeTableID, formData.object);
|
||||
await CONFIG.stats.db.updateTable(this.activeTableID, formData.object);
|
||||
};
|
||||
// #endregion Actions
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
/* eslint-disable no-unused-vars */
|
||||
import { validateBucketConfig } from "../buckets.mjs";
|
||||
|
||||
/*
|
||||
NOTE:
|
||||
|
|
@ -24,9 +25,11 @@ Default Subtables:
|
|||
tables that are parents to other tables.
|
||||
*/
|
||||
|
||||
const { deleteProperty, diffObject, expandObject, mergeObject } = foundry.utils;
|
||||
|
||||
export class Database {
|
||||
// MARK: Table Ops
|
||||
static createTable(tableConfig) {
|
||||
static async createTable(tableConfig) {
|
||||
if (!game.user.isGM) {
|
||||
ui.notifications.error(`You do not have the required permission to create a new table`);
|
||||
return false;
|
||||
|
|
@ -52,23 +55,61 @@ export class Database {
|
|||
|
||||
tables[name] = tableConfig;
|
||||
game.settings.set(__ID__, `tables`, tables);
|
||||
this.render();
|
||||
this.render({ tags: [`table`] });
|
||||
return true;
|
||||
};
|
||||
|
||||
/** @returns {Array<Table>} */
|
||||
static getTables() {
|
||||
static async getTables() {
|
||||
const tables = game.settings.get(__ID__, `tables`);
|
||||
return Object.values(tables) ?? [];
|
||||
};
|
||||
|
||||
static getTable(tableID) {
|
||||
static async getTable(tableID) {
|
||||
const tables = game.settings.get(__ID__, `tables`);
|
||||
if (!tables[tableID]) { return };
|
||||
return tables[tableID];
|
||||
};
|
||||
|
||||
static deleteTable(tableID) {
|
||||
static async updateTable(tableID, changes) {
|
||||
const table = this.getTable(tableID);
|
||||
if (!tables[tableID]) {
|
||||
ui.notifications.error(`Cannot update table that doesn't exist`);
|
||||
return false;
|
||||
};
|
||||
|
||||
// Bucket coercion in case called via the API
|
||||
deleteProperty(changes, `name`);
|
||||
deleteProperty(changes, `buckets.type`);
|
||||
|
||||
const diff = diffObject(
|
||||
table,
|
||||
expandObject(changes),
|
||||
{ inner: true, deletionKeys: true },
|
||||
);
|
||||
if (Object.keys(diff).length === 0) { return false };
|
||||
|
||||
const updated = mergeObject(
|
||||
table,
|
||||
diff,
|
||||
{ inplace: false, performDeletions: true },
|
||||
);
|
||||
|
||||
try {
|
||||
updated.buckets = validateBucketConfig(updated.buckets);
|
||||
} catch (e) {
|
||||
ui.notifications.error(e);
|
||||
return false;
|
||||
};
|
||||
|
||||
const tables = game.settings.get(__ID__, `tables`);
|
||||
tables[tableID] = updated;
|
||||
game.settings.set(__ID__, `tables`, tables);
|
||||
this.render({ tags: [`table`] });
|
||||
return true;
|
||||
};
|
||||
|
||||
static async deleteTable(tableID) {
|
||||
if (!game.user.isGM) {
|
||||
ui.notifications.error(`You do not have the required permission to delete a table`);
|
||||
return false;
|
||||
|
|
@ -86,23 +127,23 @@ export class Database {
|
|||
};
|
||||
|
||||
// MARK: Row Ops
|
||||
static createRow(table, userID, row, opts) {
|
||||
static async createRow(table, userID, row, opts) {
|
||||
throw new Error(`createRow() must be implemented`);
|
||||
};
|
||||
|
||||
static createRows(table, userID, rows, opts) {
|
||||
static async createRows(table, userID, rows, opts) {
|
||||
throw new Error(`createRows() must be implemented`);
|
||||
};
|
||||
|
||||
static getRows(tableID, userIDs, privacy = `none`) {
|
||||
static async getRows(tableID, userIDs, privacy = `none`) {
|
||||
throw new Error(`getRows() must be implemented`);
|
||||
};
|
||||
|
||||
static updateRow(table, userID, rowID, changes) {
|
||||
static async updateRow(table, userID, rowID, changes) {
|
||||
throw new Error(`updateRow() must be implemented`);
|
||||
};
|
||||
|
||||
static deleteRow(table, userID, rowID) {
|
||||
static async deleteRow(table, userID, rowID) {
|
||||
throw new Error(`deleteRow() must be implemented`);
|
||||
};
|
||||
|
||||
|
|
@ -137,7 +178,7 @@ export class Database {
|
|||
* Rerenders all of the applications that are displaying data from
|
||||
* this database
|
||||
*/
|
||||
static render(opts) {
|
||||
static async render(opts) {
|
||||
for (const app of this._apps.values()) {
|
||||
app.render(opts);
|
||||
};
|
||||
|
|
@ -148,9 +189,9 @@ export class Database {
|
|||
* Used to listen for changes from other clients and rerender the apps
|
||||
* as required in order to keep the data as up-to-date as possible.
|
||||
*/
|
||||
static registerListeners() {};
|
||||
static async registerListeners() {};
|
||||
|
||||
static unregisterListeners() {};
|
||||
static async unregisterListeners() {};
|
||||
};
|
||||
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { createDiceTable } from "./utils.mjs";
|
||||
import { Database } from "./Database.mjs";
|
||||
import { filterPrivateRows } from "../privacy.mjs";
|
||||
import { Logger } from "../Logger.mjs";
|
||||
|
|
@ -7,48 +8,9 @@ const { deleteProperty, diffObject, expandObject, mergeObject, randomID } = foun
|
|||
|
||||
export class MemoryDatabase extends Database {
|
||||
static #tables = {
|
||||
"Dice/d10": {
|
||||
name: `Dice/d10`,
|
||||
buckets: {
|
||||
type: `range`,
|
||||
locked: true,
|
||||
min: 1,
|
||||
max: 10,
|
||||
step: 1,
|
||||
},
|
||||
graph: {
|
||||
type: `bar`,
|
||||
stacked: true,
|
||||
},
|
||||
},
|
||||
"Dice/d20": {
|
||||
name: `Dice/d20`,
|
||||
buckets: {
|
||||
type: `range`,
|
||||
locked: true,
|
||||
min: 1,
|
||||
max: 20,
|
||||
step: 1,
|
||||
},
|
||||
graph: {
|
||||
type: `bar`,
|
||||
stacked: true,
|
||||
},
|
||||
},
|
||||
"Dice/d100": {
|
||||
name: `Dice/d100`,
|
||||
buckets: {
|
||||
type: `range`,
|
||||
locked: true,
|
||||
min: 1,
|
||||
max: 100,
|
||||
step: 1,
|
||||
},
|
||||
graph: {
|
||||
type: `bar`,
|
||||
stacked: true,
|
||||
},
|
||||
},
|
||||
"Dice/d10": createDiceTable(10),
|
||||
"Dice/d20": createDiceTable(20),
|
||||
"Dice/d100": createDiceTable(100),
|
||||
"Successes Number": {
|
||||
name: `Successes Number`,
|
||||
buckets: {
|
||||
|
|
@ -88,13 +50,13 @@ export class MemoryDatabase extends Database {
|
|||
|
||||
static #rows = {};
|
||||
|
||||
static createTable(tableConfig) {
|
||||
static async createTable(tableConfig) {
|
||||
this.#tables[tableConfig.name] = tableConfig;
|
||||
this.render();
|
||||
return true;
|
||||
};
|
||||
|
||||
static getTableNames() {
|
||||
static async getTableNames() {
|
||||
const tables = new Set();
|
||||
for (const tableID of Object.keys(this.#tables)) {
|
||||
const [ targetTable ] = tableID.split(`/`, 2);
|
||||
|
|
@ -103,7 +65,7 @@ export class MemoryDatabase extends Database {
|
|||
return Array.from(tables);
|
||||
};
|
||||
|
||||
static getSubtableNames(table) {
|
||||
static async getSubtableNames(table) {
|
||||
const subtables = new Set();
|
||||
for (const tableID of Object.keys(this.#tables)) {
|
||||
const [ targetTable, targetSubtable ] = tableID.split(`/`, 2);
|
||||
|
|
@ -115,16 +77,15 @@ export class MemoryDatabase extends Database {
|
|||
};
|
||||
|
||||
/** @returns {Array<Table>} */
|
||||
static getTables() {
|
||||
static async getTables() {
|
||||
return Object.values(this.#tables);
|
||||
};
|
||||
|
||||
static getTable(tableID) {
|
||||
static async getTable(tableID) {
|
||||
return this.#tables[tableID];
|
||||
};
|
||||
|
||||
static async updateTable(tableID, changes) {
|
||||
Logger.debug({tableID, changes});
|
||||
const table = this.getTable(tableID);
|
||||
if (!table) { return false };
|
||||
|
||||
|
|
@ -156,7 +117,7 @@ export class MemoryDatabase extends Database {
|
|||
return true;
|
||||
};
|
||||
|
||||
static createRow(table, userID, row, { rerender = true } = {}) {
|
||||
static async createRow(table, userID, row, { rerender = true } = {}) {
|
||||
if (!this.#tables[table]) { return };
|
||||
this.#rows[userID] ??= {};
|
||||
this.#rows[userID][table] ??= [];
|
||||
|
|
@ -172,7 +133,7 @@ export class MemoryDatabase extends Database {
|
|||
};
|
||||
};
|
||||
|
||||
static createRows(table, userID, rows, { rerender = true } = {}) {
|
||||
static async createRows(table, userID, rows, { rerender = true } = {}) {
|
||||
if (!this.#tables[table]) { return };
|
||||
this.#rows[userID] ??= {};
|
||||
this.#rows[userID][table] ??= [];
|
||||
|
|
@ -187,7 +148,7 @@ export class MemoryDatabase extends Database {
|
|||
};
|
||||
};
|
||||
|
||||
static getRows(tableID, userIDs, privacy = `none`) {
|
||||
static async getRows(tableID, userIDs, privacy = `none`) {
|
||||
if (userIDs.length === 0) {
|
||||
return {};
|
||||
};
|
||||
|
|
@ -207,7 +168,7 @@ export class MemoryDatabase extends Database {
|
|||
return datasets;
|
||||
};
|
||||
|
||||
static updateRow(table, userID, rowID, changes) {
|
||||
static async updateRow(table, userID, rowID, changes) {
|
||||
if (!this.#tables[table] || !this.#rows[userID]?.[table]) { return };
|
||||
let row = this.#rows[userID][table].find(row => row._id === rowID);
|
||||
if (!row) { return };
|
||||
|
|
@ -215,7 +176,7 @@ export class MemoryDatabase extends Database {
|
|||
this.render({ userUpdated: userID });
|
||||
};
|
||||
|
||||
static deleteRow(table, userID, rowID) {
|
||||
static async deleteRow(table, userID, rowID) {
|
||||
if (!this.#tables[table] || !this.#rows[userID]?.[table]) { return };
|
||||
let rowIndex = this.#rows[userID][table].findIndex(row => row._id === rowID);
|
||||
if (rowIndex === -1) { return };
|
||||
|
|
@ -227,7 +188,7 @@ export class MemoryDatabase extends Database {
|
|||
* Used to listen for changes from other clients and refresh the local DB as
|
||||
* required, so that the StatsTracker stays up to date.
|
||||
*/
|
||||
static registerListeners() {};
|
||||
static async registerListeners() {};
|
||||
|
||||
static unregisterListeners() {};
|
||||
static async unregisterListeners() {};
|
||||
};
|
||||
|
|
|
|||
9
public/templates/Apps/TableManager/buckets.hbs
Normal file
9
public/templates/Apps/TableManager/buckets.hbs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<div class="{{buckets.classes}}">
|
||||
{{#if buckets.locked}}
|
||||
<p class="center">
|
||||
This bucket configuration has been locked, preventing editing
|
||||
of the settings.
|
||||
</p>
|
||||
{{/if}}
|
||||
{{> (lookup buckets "template") }}
|
||||
</div>
|
||||
|
|
@ -1,7 +1,5 @@
|
|||
<div class="alert-box warning center">
|
||||
{{#if table}}
|
||||
<span class="large">Select a Subtable</span>
|
||||
{{else}}
|
||||
<span class="large">Select a Table</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{#if table}}
|
||||
<span class="large">Select a Subtable</span>
|
||||
{{else}}
|
||||
<span class="large">Select a Table</span>
|
||||
{{/if}}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
<div class="{{#if buckets.locked}}alert-box locked{{/if}}">
|
||||
<div
|
||||
class="{{#if buckets.locked}}alert-box locked{{/if}}"
|
||||
data-bucket-type="number"
|
||||
>
|
||||
{{#if buckets.locked}}
|
||||
<p class="center">
|
||||
This bucket configuration has been locked, preventing editing
|
||||
|
|
|
|||
|
|
@ -1,50 +1,42 @@
|
|||
<div class="{{#if buckets.locked}}alert-box locked{{/if}}">
|
||||
{{#if buckets.locked}}
|
||||
<p class="center">
|
||||
This bucket configuration has been locked, preventing editing
|
||||
of the settings.
|
||||
</p>
|
||||
{{/if}}
|
||||
<div class="input-group">
|
||||
<label for="{{meta.idp}}-min">
|
||||
Minimum
|
||||
</label>
|
||||
<input
|
||||
id="{{meta.idp}}-min"
|
||||
type="number"
|
||||
name="buckets.min"
|
||||
value="{{ buckets.min }}"
|
||||
required
|
||||
{{disabled buckets.locked}}
|
||||
>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label for="{{meta.idp}}-max">
|
||||
Maximum
|
||||
</label>
|
||||
<input
|
||||
id="{{meta.idp}}-max"
|
||||
type="number"
|
||||
name="buckets.max"
|
||||
value="{{ buckets.max }}"
|
||||
required
|
||||
{{disabled buckets.locked}}
|
||||
>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label for="{{meta.idp}}-step">
|
||||
Step
|
||||
</label>
|
||||
<input
|
||||
id="{{meta.idp}}-step"
|
||||
type="number"
|
||||
name="buckets.step"
|
||||
value="{{ buckets.step }}"
|
||||
required
|
||||
{{disabled buckets.locked}}
|
||||
>
|
||||
<p class="hint">
|
||||
The size of the step between values within the range.
|
||||
</p>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label for="{{meta.idp}}-min">
|
||||
Minimum
|
||||
</label>
|
||||
<input
|
||||
id="{{meta.idp}}-min"
|
||||
type="number"
|
||||
name="buckets.min"
|
||||
value="{{ buckets.min }}"
|
||||
required
|
||||
{{disabled buckets.locked}}
|
||||
>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label for="{{meta.idp}}-max">
|
||||
Maximum
|
||||
</label>
|
||||
<input
|
||||
id="{{meta.idp}}-max"
|
||||
type="number"
|
||||
name="buckets.max"
|
||||
value="{{ buckets.max }}"
|
||||
required
|
||||
{{disabled buckets.locked}}
|
||||
>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label for="{{meta.idp}}-step">
|
||||
Step
|
||||
</label>
|
||||
<input
|
||||
id="{{meta.idp}}-step"
|
||||
type="number"
|
||||
name="buckets.step"
|
||||
value="{{ buckets.step }}"
|
||||
required
|
||||
{{disabled buckets.locked}}
|
||||
>
|
||||
<p class="hint">
|
||||
The size of the step between values within the range.
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
<div class="{{#if buckets.locked}}alert-box locked{{/if}}">
|
||||
<div
|
||||
class="{{#if buckets.locked}}alert-box locked{{/if}}"
|
||||
data-bucket-type="string"
|
||||
>
|
||||
<div class="input-group">
|
||||
<label for="">
|
||||
Valid Options
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue