Skip to content

Instantly share code, notes, and snippets.

@r-k-b
Last active July 24, 2017 03:57
Show Gist options
  • Select an option

  • Save r-k-b/731dbdc684e3366fd7d17140cd31a7e5 to your computer and use it in GitHub Desktop.

Select an option

Save r-k-b/731dbdc684e3366fd7d17140cd31a7e5 to your computer and use it in GitHub Desktop.
BC webapp field schema helper

BC webapp field schema helper

BC webapp fields are too obtuse ("What does CAT_Custom_48142 represent, again?"), and they're a pain to write out. Grab the select option HTML from the admin UI (under 'Edit Webapp Fields'), and paste the options in here.

A Pen by Robert K. Bell on CodePen.

License.

<textarea id="output"></textarea>
.container
h2
label(for="input") input
h2
label(for="js-output-a") js output (type A)
h2
label(for="js-output-b") js output (type B)
h2
label(for="html-output") html output
textarea#input
| <option value="-1,11,1,0,0">URL*</option>
| <option value="-2,1,1,0,0">Name*</option>
| <option value="-3,2,0,0,0">Weighting</option>
| <option value="-4,4,1,0,0">Release Date*</option>
| <option value="-5,4,1,0,0">Expiry Date*</option>
| <option value="-6,3,1,0,0">Enabled*</option>
| <option value="-7,9,0,0,0">Content</option>
| <option value="13850,1,1,-1,0,0">First Name*</option>
| <option value="13851,1,1,-1,0,0">Last Name*</option>
| <option value="13852,12,0,1251,0,0">Firm</option>
| <option value="13853,1,0,-1,0,0">Email</option>
| <option value="13854,1,0,-1,0,0">Phone Number</option>
| <option value="13093,6,1,1830,0,0">Position*</option>
| <option value="13196,6,0,1844,0,0">Austlaw Designation</option>
| <option value="13099,6,0,1831,0,0">Practice Area Categories</option>
| <option value="13100,1,0,-1,0,0">Other Area of Practice</option>
| <option value="13116,1,0,-1,0,0">DX Box No</option>
| <option value="13117,1,0,-1,0,0">DX Location</option>
| <option value="13118,1,0,-1,0,0">DX State</option>
| <option value="13091,1,0,-1,0,0">Special Dietary Requirements</option>
| <option value="14823,3,0,-1,0,0">Has Austlaw Designation</option>
| <option value="14824,8,0,-1,0,0">Photo</option>
| <option value="14826,1,0,-1,0,1">Firm Connections</option>
| <option value="14827,3,0,-1,0,1">Firm Administrator</option>
| <option value="14829,1,0,-1,0,0">Fax Number</option>
| <option value="14830,11,0,-1,0,0">Linkedin Profile</option>
| <option value="14831,1,0,-1,0,0">Skype Username</option>
| <option value="14832,9,0,-1,1,0">Member Profile</option>
| <option value="14834,6,0,49322,0,0">Specific Areas Of Expertise</option>
| <option value="14835,3,0,-1,0,0">Accredited Specialist</option>
| <option value="14836,9,0,-1,0,0">Accredited Specialist Description</option>
| <option value="14837,3,0,-1,0,0">Deleted</option>
textarea#js-output-a
textarea#js-output-b
textarea#html-output
// https://codepen.io/r-k-b/pen/OgYmwp?editors=0010
// https://gist.github.com/r-k-b/731dbdc684e3366fd7d17140cd31a7e5
// note on field name vs ID / LongID:
// - 'name' is how the field is shown by BC,
// - 'LongID' is how the field must be named when POSTing to BC
const config = {
showProximityFields: true,
}
const trim = s => s.trim();
const nameRE = /\>(.+)\</;
const valsRE = /value="(.+)"/;
const srcElem = document.getElementById("input");
const jsTypeAOutputElem = document.getElementById("js-output-a");
const jsTypeBOutputElem = document.getElementById("js-output-b");
const htmlOutputElem = document.getElementById("html-output");
const typeMap = {
"1": {
typeName: "Text (String)",
typeClass: 'text',
renderIDTag: false
},
"2": {
typeName: "Number",
typeClass: 'number',
renderIDTag: false
},
"3": {
typeName: "True/False (Boolean)",
typeClass: 'boolean-checkbox',
renderIDTag: false
},
"4": {
typeName: "DateTime",
typeClass: 'datetime',
renderIDTag: false
},
"5": {
typeName: "List (Dropdown List)",
typeClass: 'select',
renderIDTag: false
},
"6": {
typeName: "List (Checkbox List)",
typeClass: 'checkbox',
renderIDTag: false
},
"7": {
typeName: "List (Radio List)",
typeClass: 'radio',
renderIDTag: false
},
"8": {
typeName: "Media",
typeClass: 'media',
renderIDTag: false
},
"9": {
typeName: "Text (Multiline)",
typeClass: 'text',
renderIDTag: false
},
"10": {
typeName: "List (Listbox List)",
typeClass: 'listbox',
renderIDTag: false
},
"11": {
typeName: "Text (Hyperlink)",
typeClass: 'text',
renderIDTag: false
},
"12": {
typeName: "Data Source",
typeClass: 'select',
renderIDTag: true
}
};
// Only present when webapp is configured for 'Allow Proximity Search'
const itemAddressFields = [
{
name: 'address1',
fieldID: 'ItemAddress',
fieldIDLong: 'ItemAddress',
type: '1',
mandatory: undefined,
dataSourceID: undefined,
useWYSIWYG: undefined,
unknownProperty: undefined,
isEditable: true,
isCustom: false,
},
{
name: 'city',
fieldID: 'ItemCity',
fieldIDLong: 'ItemCity',
type: '1',
mandatory: undefined,
dataSourceID: undefined,
useWYSIWYG: undefined,
unknownProperty: undefined,
isEditable: true,
isCustom: false,
},
{
name: 'state',
fieldID: 'ItemState',
fieldIDLong: 'ItemState',
type: '1',
mandatory: undefined,
dataSourceID: undefined,
useWYSIWYG: undefined,
unknownProperty: undefined,
isEditable: true,
isCustom: false,
},
{
name: 'country',
fieldID: 'ItemCountry',
fieldIDLong: 'ItemCountry',
type: '5',
mandatory: undefined,
dataSourceID: undefined,
useWYSIWYG: undefined,
unknownProperty: undefined,
isEditable: true,
isCustom: false,
},
{
name: 'zipcode',
fieldID: 'ItemZip',
fieldIDLong: 'ItemZip',
type: '1',
mandatory: undefined,
dataSourceID: undefined,
useWYSIWYG: undefined,
unknownProperty: undefined,
isEditable: true,
isCustom: false,
},
]
const getTypeName = t => typeMap[t].typeName || "?";
const merge = defaultsObj => objA => {
return Object.assign({}, defaultsObj, objA)
}
const parse = s => {
const name = s.match(nameRE)[1].replace(/\*$/, "");
const vals = s.match(valsRE)[1].split(",");
const fieldID = vals[0]
const isCustom = fieldID > 0
const fieldIDLong = isCustom
? 'CAT_Custom_' + fieldID
: name
const mergeBase = merge({
name,
fieldID,
fieldIDLong,
type: vals[1],
mandatory: vals[2] === "1",
dataSourceID: vals[3],
useWYSIWYG: vals[4] === "1",
unknownProperty: vals[5],
isEditable: true,
isCustom,
})
switch (fieldID) {
case '-1': // URL
return mergeBase({
isEditable: false,
})
case '-2': // Name
return mergeBase({
fieldIDLong: 'ItemName'
})
case '-3': // Weighting
return mergeBase({
isEditable: false,
})
case '-4': // Release Date
return mergeBase({
isEditable: false,
})
case '-5': // Expiry Date
return mergeBase({
isEditable: false,
})
case '-6': // Enabled
return mergeBase({
isEditable: false,
})
case '-7': // Content
return mergeBase({
fieldIDLong: 'ItemDescription'
})
default:
return mergeBase({})
}
};
const scream = s => s.toUpperCase().replace(/\s/g, "_");
const stringifyObjectsTypeA = x => ` obj[${scream(x.name)}] = {
name: ${scream(x.name)},
fieldID: '${x.fieldID}',
fieldIDLong: '${x.fieldIDLong}',
type: '${x.type}',
typeName: '${getTypeName(x.type)}',
mandatory: ${x.mandatory},
dataSourceID: '${x.dataSourceID}',
useWYSIWYG: ${x.useWYSIWYG},
unknownProperty: '${x.unknownProperty}',
isEditable: ${x.isEditable},
isCustom: ${x.isCustom},
}`;
const stringifyObjectsTypeB = x => ` '${x.name}': {
name: '${x.name}',
fieldID: '${x.fieldID}',
fieldIDLong: '${x.fieldIDLong}',
type: '${x.type}', // '${getTypeName(x.type)}'
mandatory: ${x.mandatory},
dataSourceID: '${x.dataSourceID}',
useWYSIWYG: ${x.useWYSIWYG},
unknownProperty: '${x.unknownProperty}',
isEditable: ${x.isEditable},
isCustom: ${x.isCustom},
},`;
const stringifyDeclarations = x => `var ${scream(x.name)} = '${x.name}'`;
const showJSTypeAOutput = s => {
jsTypeAOutputElem.textContent = s;
};
const showJSTypeBOutput = s => {
jsTypeBOutputElem.textContent = s;
};
const showHTMLOutput = s => {
htmlOutputElem.textContent = s;
};
const renderJSTypeA = data => {
const declarations = data.map(stringifyDeclarations).join("\n");
const objects = data.map(stringifyObjectsTypeA).join("\n\n");
const result = `${declarations}
function getWebappSchema() {
var obj = {}
${objects}
return obj
}
`;
console.log({ result }, "js type-A");
return result
};
const renderJSTypeB = data => {
const objects = data.map(stringifyObjectsTypeB);
const result = `{\n${objects.join("\n\n")}\n}`;
console.log({ result }, "js type-B");
return result
};
const stringifyObjectsHTMLTypeA = field => {
const fieldType = typeMap[field.type]
const name = fieldType.renderIDTag
? field.name + '_id'
: field.name
return ` <div
data-field-type='${field.type}'
data-field-name='${name}'
class='${fieldType.typeClass}'
rel='${field.fieldIDLong}'
>{{ this["${name}"] }}</div>`
}
const renderHTMLTypeA = data => {
const objects = data
.filter(field => field.isEditable)
.map(stringifyObjectsHTMLTypeA)
const result = `${objects.join("\n\n")}\n`;
console.log({ result }, "html type-A");
return result
}
const run = () => {
try {
console.log("running...");
showJSTypeAOutput("running...");
showHTMLOutput("running...");
const srcText = srcElem.value;
if (srcText.trim() === "") {
throw new Error("empty input?");
}
const src = srcText.trim().split("\n").map(trim);
const explicitData = src.map(parse);
const data = config.showProximityFields
? explicitData.concat(itemAddressFields)
: explicitData
showJSTypeAOutput(renderJSTypeA(data));
showJSTypeBOutput(renderJSTypeB(data));
showHTMLOutput(renderHTMLTypeA(data));
} catch (error) {
showJSTypeAOutput(error.stack);
showJSTypeBOutput('');
showHTMLOutput('');
}
};
run();
srcElem.addEventListener("input", run);
* {
box-sizing: border-box;
}
.container {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: 2em auto;
min-height: 100vh;
}
h2 {
margin: 0;
}
textarea {
width: 100%;
font-family: monospace;
background-color: rgba(0, 0, 0, 0.05);
white-space: nowrap;
&#input {
background-color: rgba(0, 0, 0, 0.0);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment