quorlasocial/dashboard/server/onboarding.js
metatroncubeswdev 82814f3757 White-label workspace and getting-started checklist
- Settings → Workspace: agency name, logo, brand colour, support email, website
- Logo replaces Quorla's in the app (with 'Powered by Quorla'), sign-in page, client approval page and PDF reports;
  brand colour drives buttons, links and highlights; emails and reports use the agency name
- Getting started checklist on the Clients page for admins, checked against the real state
  (workspace, Meta, first client, team, platforms, email, library, first post, approval link); dismissible

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 10:28:23 -04:00

40 lines
3.2 KiB
JavaScript

// "Getting started" checklist for admins: each step is checked against the real state, not ticked by hand.
import fs from "node:fs";
import path from "node:path";
import { DATA_DIR } from "./config.js";
import * as users from "./users.js";
import * as clients from "./clients.js";
import * as notify from "./notify.js";
import * as meta from "../../connector/src/meta.js";
import * as sched from "../../connector/src/schedule.js";
import * as lib from "../../connector/src/library.js";
import * as extra from "../../connector/src/platforms/index.js";
const FILE = path.join(DATA_DIR, "onboarding.json");
const read = () => { try { return JSON.parse(fs.readFileSync(FILE, "utf8")); } catch { return {}; } };
export async function steps() {
let metaOk = false;
try { metaOk = (await meta.listAccounts()).length > 0; } catch { /* not connected */ }
const list = clients.listClients();
const posts = sched.listPosts({});
const portal = (() => { try { return Object.keys(JSON.parse(fs.readFileSync(path.join(DATA_DIR, "portal.json"), "utf8"))).length; } catch { return 0; } })();
const workspace = (() => { try { return JSON.parse(fs.readFileSync(path.join(DATA_DIR, "workspace.json"), "utf8")); } catch { return {}; } })();
const items = [
{ id: "workspace", title: "Name your workspace", text: "Your agency's name and logo appear in the app, on client approval pages and in reports.", done: Boolean(workspace.name), link: "/settings#workspace" },
{ id: "meta", title: "Connect Facebook and Instagram", text: "Add the Meta system user token to the server's .env (see the hosting guide). Every client's Page and Instagram account then shows up here.", done: metaOk, link: "/settings" },
{ id: "client", title: "Add your first client", text: "Pick their Facebook Page or Instagram account from the list.", done: list.length > 0, link: "/" },
{ id: "team", title: "Invite your team", text: "Add designers, editors and managers. Each gets their own login.", done: users.listUsers().length > 1, link: "/settings" },
{ id: "platforms", title: "Connect another platform", text: "TikTok, YouTube, Google Business Profile, LinkedIn and more, per client.", done: extra.vault.listConnections({}).length > 0, link: "/connections" },
{ id: "email", title: "Set up email", text: "So people get notified and clients get their approval emails and monthly reports.", done: notify.emailConfigured(), link: "/admin/health" },
{ id: "library", title: "Upload finished work", text: "Designers upload to the Content library; an admin reviews it.", done: lib.listItems({}).length > 0, link: "/library" },
{ id: "post", title: "Schedule and approve a post", text: "Create a draft, approve it, and Quorla publishes at the client's local time.", done: posts.some((p) => ["approved", "published", "partial"].includes(p.status)), link: "/publishing" },
{ id: "portal", title: "Give a client their approval link", text: "They approve posts or ask for changes without a login.", done: portal > 0, link: "/" },
];
return { dismissed: Boolean(read().dismissed), done: items.filter((s) => s.done).length, total: items.length, items };
}
export function dismiss(value = true) {
fs.writeFileSync(FILE, JSON.stringify({ ...read(), dismissed: Boolean(value), at: new Date().toISOString() }));
}