PCO Plan 1.7.0: a Teams tile — who is scheduled, with photos #23
@@ -411,21 +411,35 @@
|
||||
"h": 2
|
||||
},
|
||||
"instanceSchema": {
|
||||
"columns": {
|
||||
"type": "select",
|
||||
"label": "Columns",
|
||||
"default": "auto",
|
||||
"options": [
|
||||
{
|
||||
"value": "auto",
|
||||
"label": "Fit the tile"
|
||||
},
|
||||
{
|
||||
"value": "1",
|
||||
"label": "One"
|
||||
},
|
||||
{
|
||||
"value": "2",
|
||||
"label": "Two"
|
||||
},
|
||||
{
|
||||
"value": "3",
|
||||
"label": "Three"
|
||||
}
|
||||
]
|
||||
},
|
||||
"teams": {
|
||||
"type": "multiselect",
|
||||
"label": "Teams",
|
||||
"group": "Teams",
|
||||
"optionsRoute": "/teams",
|
||||
"columns": 1,
|
||||
"allByDefault": true
|
||||
},
|
||||
"positions": {
|
||||
"type": "multiselect",
|
||||
"label": "Positions",
|
||||
"group": "Positions",
|
||||
"optionsRoute": "/positions",
|
||||
"columns": 2,
|
||||
"allByDefault": true
|
||||
"allByDefault": true,
|
||||
"optionsRoute": "/teams"
|
||||
},
|
||||
"headerTemplate": {
|
||||
"type": "template",
|
||||
@@ -484,12 +498,6 @@
|
||||
"default": false,
|
||||
"group": "Colors"
|
||||
},
|
||||
"headerGradient": {
|
||||
"type": "boolean",
|
||||
"label": "Header fade",
|
||||
"default": true,
|
||||
"group": "Colors"
|
||||
},
|
||||
"tintRows": {
|
||||
"type": "boolean",
|
||||
"label": "Tint rows too",
|
||||
@@ -497,23 +505,29 @@
|
||||
"group": "Colors",
|
||||
"showWhen": "tintTeams"
|
||||
},
|
||||
"headerGradient": {
|
||||
"type": "boolean",
|
||||
"label": "Header fade",
|
||||
"default": true,
|
||||
"group": "Colors"
|
||||
},
|
||||
"confirmedColor": {
|
||||
"type": "color",
|
||||
"label": "Confirmed",
|
||||
"default": "#2ee59a",
|
||||
"group": "Status colors"
|
||||
"group": "Colors"
|
||||
},
|
||||
"unconfirmedColor": {
|
||||
"type": "color",
|
||||
"label": "Unconfirmed",
|
||||
"default": "#f0b429",
|
||||
"group": "Status colors"
|
||||
"group": "Colors"
|
||||
},
|
||||
"declinedColor": {
|
||||
"type": "color",
|
||||
"label": "Declined",
|
||||
"default": "#f4433c",
|
||||
"group": "Status colors"
|
||||
"group": "Colors"
|
||||
},
|
||||
"showTopBar": {
|
||||
"type": "boolean",
|
||||
@@ -580,52 +594,16 @@
|
||||
"label": "Teams with nobody",
|
||||
"default": false,
|
||||
"group": "Show"
|
||||
},
|
||||
"columns": {
|
||||
"type": "select",
|
||||
"label": "Columns",
|
||||
"default": "auto",
|
||||
"group": "Show",
|
||||
"options": [
|
||||
{
|
||||
"value": "auto",
|
||||
"label": "Fit the tile"
|
||||
},
|
||||
{
|
||||
"value": "1",
|
||||
"label": "One"
|
||||
},
|
||||
{
|
||||
"value": "2",
|
||||
"label": "Two"
|
||||
},
|
||||
{
|
||||
"value": "3",
|
||||
"label": "Three"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"instanceGroups": {
|
||||
"Top bar": {
|
||||
"collapsed": false
|
||||
},
|
||||
"Teams": {
|
||||
"collapsed": true,
|
||||
"help": "Untick a team to leave it out of this tile."
|
||||
},
|
||||
"Positions": {
|
||||
"collapsed": true,
|
||||
"help": "Untick a position to leave it out. Positions follow the teams above."
|
||||
},
|
||||
"Colors": {
|
||||
"collapsed": false,
|
||||
"columns": 2
|
||||
},
|
||||
"Status colors": {
|
||||
"collapsed": true,
|
||||
"columns": 3
|
||||
},
|
||||
"Show": {
|
||||
"collapsed": true,
|
||||
"columns": 2
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
* GET /note-categories → { options } for a tile's "Item notes" categories:
|
||||
* the service type's item note categories, by name
|
||||
* GET /teams → { options } for a Teams tile's "Teams" switches
|
||||
* GET /positions → { options } for a Teams tile's "Positions" switches
|
||||
* GET /team-avatar/<planPersonId> → that person's photo, proxied + cached
|
||||
* GET /timers → snapshot for the Timers module (guide: "Offering
|
||||
* GET /timers/stream → SSE, `timers` events timers to the Timers module")
|
||||
@@ -821,24 +820,6 @@ module.exports = {
|
||||
return { options: state.teams.map((t) => ({ value: t.id, label: t.name })) };
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for a Teams tile's "Positions" switches. Positions are named,
|
||||
* not numbered, in Planning Center, so the name is the value; the team
|
||||
* name groups them so two "Leader" positions stay apart in the list.
|
||||
*/
|
||||
function positionOptions() {
|
||||
const seen = new Set();
|
||||
const options = [];
|
||||
for (const team of state.teams) {
|
||||
for (const m of team.members) {
|
||||
const value = m.position || '(no position)';
|
||||
if (seen.has(value)) continue;
|
||||
seen.add(value);
|
||||
options.push({ value, label: value, group: team.name });
|
||||
}
|
||||
}
|
||||
return { options };
|
||||
}
|
||||
|
||||
async function readPlan() {
|
||||
if (!pco.configured || !serviceTypeId) return;
|
||||
@@ -1589,7 +1570,6 @@ module.exports = {
|
||||
documentOptions,
|
||||
noteCategoryOptions,
|
||||
teamOptions,
|
||||
positionOptions,
|
||||
avatarUrlFor: (id) => teamPhotos.get(String(id)) || "",
|
||||
};
|
||||
|
||||
@@ -1684,19 +1664,15 @@ module.exports = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Options for a Teams tile's "Teams" and "Positions" switches. Like the
|
||||
* note categories, always a 200 with a list: an empty one simply means
|
||||
* there is nothing scheduled to choose from yet, which the gear window
|
||||
* says plainly.
|
||||
* Options for a Teams tile's "Teams" switches. Like the note
|
||||
* categories, always a 200 with a list: an empty one simply means there
|
||||
* is nothing scheduled to choose from yet, which the gear window says
|
||||
* plainly.
|
||||
*/
|
||||
'GET /teams': (req, res) => {
|
||||
json(res, 200, current ? current.teamOptions() : { options: [] });
|
||||
},
|
||||
|
||||
'GET /positions': (req, res) => {
|
||||
json(res, 200, current ? current.positionOptions() : { options: [] });
|
||||
},
|
||||
|
||||
/**
|
||||
* A scheduled person's photo, proxied from Planning Center and cached.
|
||||
* The tile asks by plan-person id and never sees the CDN URL; an id the
|
||||
|
||||
@@ -94,12 +94,10 @@ export default function createTeams({ root, moduleApi }) {
|
||||
function visibleTeams() {
|
||||
const teams = Array.isArray(state?.teams) ? state.teams : [];
|
||||
const wantTeams = chosen('teams');
|
||||
const wantPositions = chosen('positions');
|
||||
const out = [];
|
||||
for (const team of teams) {
|
||||
if (wantTeams && !wantTeams.includes(team.id)) continue;
|
||||
const members = team.members.filter((m) => {
|
||||
if (wantPositions && !wantPositions.includes(m.position || '(no position)')) return false;
|
||||
if (m.status === 'declined' && !on('showDeclined')) return false;
|
||||
if (m.status === 'unconfirmed' && !on('showUnconfirmed')) return false;
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user