From db1b1683007c00dcd9d05d3a07e8f6ee158d4fcb Mon Sep 17 00:00:00 2001 From: jtricerolph Date: Tue, 7 Jul 2026 12:52:35 +0000 Subject: [PATCH] Workforce: add location_id config field + locations fetch endpoint Co-Authored-By: Claude Sonnet 4.6 --- src/integrations/schema.js | 2 +- src/routes/integrations.js | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/integrations/schema.js b/src/integrations/schema.js index fe47349..e99571b 100644 --- a/src/integrations/schema.js +++ b/src/integrations/schema.js @@ -29,7 +29,7 @@ export const INTEGRATIONS = { }, workforce: { name: 'Workforce', - configFields: ['base_url', 'sync_hours'], + configFields: ['base_url', 'sync_hours', 'location_id'], secretFields: ['bearer_token'], }, smtp: { diff --git a/src/routes/integrations.js b/src/routes/integrations.js index d7936d2..6f214cb 100644 --- a/src/routes/integrations.js +++ b/src/routes/integrations.js @@ -112,6 +112,31 @@ export async function integrationRoutes(app) { }) // POST /settings/api/integrations/newbook/sync-rooms + // GET /settings/api/integrations/workforce/locations — fetch location list from Tanda for the picker + app.get('/integrations/workforce/locations', { preHandler: requireAdmin }, async (req, reply) => { + const { rows: [row] } = await pool.query(`SELECT * FROM integrations WHERE slug = 'workforce'`) + if (!row?.enabled) return reply.status(400).send({ error: 'Workforce integration not enabled' }) + const creds = { ...row.config, ...decryptSecrets(row) } + if (!creds.bearer_token) return reply.status(400).send({ error: 'Workforce bearer token not configured' }) + const baseUrl = creds.base_url || 'https://my.tanda.co' + + const locations = [] + let page = 1 + while (true) { + const res = await fetch(`${baseUrl}/api/v2/locations?page=${page}&page_size=100`, { + headers: { Authorization: `Bearer ${creds.bearer_token}` }, + signal: AbortSignal.timeout(8000), + }) + if (!res.ok) return reply.status(502).send({ error: `Workforce API error (${res.status})` }) + const data = await res.json() + const items = Array.isArray(data) ? data : (data.locations ?? []) + locations.push(...items.map(l => ({ id: String(l.id), name: l.name, short_name: l.short_name ?? null }))) + if (items.length < 100) break + page++ + } + return locations + }) + app.post('/integrations/newbook/sync-rooms', { preHandler: requireAdmin }, async (req, reply) => { const { rows: [row] } = await pool.query(`SELECT * FROM integrations WHERE slug = 'newbook'`) if (!row?.enabled) return reply.status(400).send({ error: 'Newbook integration not enabled' })