mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-20 22:16:26 +00:00
feat(plugins): hero widget slot + Koffi reference plugin
Widget plugins can now declare capabilities.widget.slot 'hero' to render as a transparent, click-through overlay sitting on the boarding-pass bar's top edge (migration 157 persists capabilities; manifest validation server+SDK, feed exposes the slot, dashboard mounts hero frames above the pass). Sidebar stays the default slot. Replaces the trip-countdown example with Koffi, the TREK mascot: an animated suitcase with a 14-state behavior engine driven by real trip data — walking, waving, napping, trolley rolls, passport-stamp stickers, a split-flap luggage- tag countdown under 7 days, and sunglasses while the trip runs. Validated by the SDK suite like any author plugin; published as mauriceboe/trek-plugin-koffi in the registry. Migrations 156/157 follow the idempotent ALTER pattern (the reconciliation test re-runs everything from v135, so duplicate-column must stay non-fatal).
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
// Koffi — the TREK suitcase mascot (hero-slot widget).
|
||||
//
|
||||
// One route: GET /state?tripId= → { days, ongoing, title }. The mascot's mood is
|
||||
// driven by how close the trip is. The host membership-checks the trip against
|
||||
// the acting user, so Trekki can only ever see a trip that user can see.
|
||||
|
||||
const DAY_MS = 86400000;
|
||||
|
||||
function midnight(iso) {
|
||||
const d = new Date(iso + 'T00:00:00');
|
||||
return Number.isNaN(d.getTime()) ? null : d.getTime();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
routes: [
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/state',
|
||||
auth: true,
|
||||
async handler(req, ctx) {
|
||||
const json = { 'content-type': 'application/json' };
|
||||
const tripId = Number(req.query.tripId);
|
||||
if (!tripId) return { status: 200, headers: json, body: JSON.stringify({ days: null, ongoing: false, title: null }) };
|
||||
|
||||
const t = await ctx.trips.getById(tripId, req.user.id);
|
||||
if (!t) return { status: 200, headers: json, body: JSON.stringify({ days: null, ongoing: false, title: null }) };
|
||||
|
||||
const now = new Date();
|
||||
now.setHours(0, 0, 0, 0);
|
||||
const start = t.start_date ? midnight(t.start_date) : null;
|
||||
const end = t.end_date ? midnight(t.end_date) : null;
|
||||
|
||||
const ongoing = start !== null && start <= now.getTime() && (end === null || now.getTime() <= end);
|
||||
const days = !ongoing && start !== null && start >= now.getTime()
|
||||
? Math.round((start - now.getTime()) / DAY_MS)
|
||||
: null;
|
||||
|
||||
return { status: 200, headers: json, body: JSON.stringify({ days, ongoing, title: t.title ?? null }) };
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
Reference in New Issue
Block a user