1
14
15type TplBlock = { id: string; type: string; content: Record<string, unknown>; properties: Record<string, unknown>; parentId: string | null; position: string };
16type TplChild = { title: string; blocks: TplBlock[] };
17export interface BuiltinTemplateDef {
18 name: string;
19 icon: string;
20 category: string;
21 description: string;
22 variables?: { name: string; label: string; type: "text" | "number" | "date" | "select"; options?: string[]; required?: boolean; defaultValue?: string }[];
23 snapshot: { page: { title: string; icon: string | null; description: string | null }; blocks: TplBlock[]; children: TplChild[] };
24}
25
26let n = 0;
27const bid = (p: string) => `${p}_${(n++).toString(36).padStart(2, "0")}`;
28const P = (id: string, text: string) => ({ id, type: "PARAGRAPH", content: { text }, properties: {}, parentId: null, position: "a0" });
29const H1 = (id: string, text: string) => ({ id, type: "HEADING_1", content: { text }, properties: {}, parentId: null, position: "a0" });
30const H2 = (id: string, text: string) => ({ id, type: "HEADING_2", content: { text }, properties: {}, parentId: null, position: "a0" });
31const TODO = (id: string, text: string) => ({ id, type: "TODO", content: { text }, properties: {}, parentId: null, position: "a0" });
32const LIST = (id: string, text: string) => ({ id, type: "BULLETED_LIST", content: { text }, properties: {}, parentId: null, position: "a0" });
33const CALLOUT = (id: string, text: string) => ({ id, type: "CALLOUT", content: { text }, properties: {}, parentId: null, position: "a0" });
34
35function page(title: string, icon: string | null, blocks: TplBlock[], children: TplChild[] = []): BuiltinTemplateDef["snapshot"] {
36 return { page: { title, icon, description: null }, blocks, children };
37}
38
39
40export const BUILTIN_TEMPLATES: BuiltinTemplateDef[] = [
41 {
42 name: "Meeting Notes", icon: "🗓️", category: "Meetings",
43 description: "Agenda, decisions, action items for a meeting.",
44 variables: [
45 { name: "title", label: "Meeting title", type: "text", required: true },
46 { name: "date", label: "Date", type: "date" },
47 { name: "attendees", label: "Attendees", type: "text" }
48 ],
49 snapshot: page("{{title}}", "🗓️", [
50 H1(bid("m"), "Meeting Notes"),
51 P(bid("m"), "Date: {{date}}"),
52 P(bid("m"), "Attendees: {{attendees}}"),
53 H2(bid("m"), "Agenda"), LIST(bid("m"), "Topic 1"), LIST(bid("m"), "Topic 2"),
54 H2(bid("m"), "Decisions"), P(bid("m"), "-"),
55 H2(bid("m"), "Action items"), TODO(bid("m"), "[ ] Owner — task — due"),
56 H2(bid("m"), "Notes"), P(bid("m"), "-")
57 ])
58 },
59 {
60 name: "Project Plan", icon: "📋", category: "Planning",
61 description: "Goals, scope, milestones, owners, risks.",
62 variables: [{ name: "project", label: "Project name", type: "text", required: true }],
63 snapshot: page("{{project}} — Project Plan", "📋", [
64 H1(bid("p"), "{{project}}"),
65 H2(bid("p"), "Goals"), P(bid("p"), "What success looks like."),
66 H2(bid("p"), "Scope"), P(bid("p"), "In scope / out of scope."),
67 H2(bid("p"), "Milestones"), LIST(bid("p"), "M1 — date"), LIST(bid("p"), "M2 — date"),
68 H2(bid("p"), "Owners"), P(bid("p"), "Roles and accountabilities."),
69 H2(bid("p"), "Risks"), P(bid("p"), "Likelihood / impact / mitigation.")
70 ])
71 },
72 {
73 name: "Product Requirements Document", icon: "📄", category: "Product",
74 description: "PRD: problem, goals, requirements, non-goals, metrics.",
75 variables: [{ name: "feature", label: "Feature name", type: "text", required: true }],
76 snapshot: page("PRD: {{feature}}", "📄", [
77 H1(bid("r"), "{{feature}}"),
78 H2(bid("r"), "Problem"), P(bid("r"), "The problem we are solving and why now."),
79 H2(bid("r"), "Goals & non-goals"), LIST(bid("r"), "Goal 1"), LIST(bid("r"), "Non-goal 1"),
80 H2(bid("r"), "Requirements"), TODO(bid("r"), "[ ] Must-have 1"), TODO(bid("r"), "[ ] Nice-to-have 1"),
81 H2(bid("r"), "Success metrics"), P(bid("r"), "How we measure success."),
82 H2(bid("r"), "Open questions"), P(bid("r"), "-")
83 ])
84 },
85 {
86 name: "Technical Design Document", icon: "🛠️", category: "Engineering",
87 description: "System design: context, proposal, alternatives, risks.",
88 variables: [{ name: "system", label: "System / feature", type: "text", required: true }],
89 snapshot: page("Design: {{system}}", "🛠️", [
90 H1(bid("t"), "{{system}}"),
91 H2(bid("t"), "Context & constraints"), P(bid("t"), "-"),
92 H2(bid("t"), "Proposal"), P(bid("t"), "The design, data model, and API surface."),
93 H2(bid("t"), "Alternatives considered"), P(bid("t"), "Option A / Option B and trade-offs."),
94 H2(bid("t"), "Data & schema changes"), P(bid("t"), "Migrations, indexes, backfill."),
95 H2(bid("t"), "Risks & rollback"), P(bid("t"), "Failure modes, monitoring, revert plan.")
96 ])
97 },
98 {
99 name: "Architecture Decision Record", icon: "🏛️", category: "Engineering",
100 description: "ADR: context, decision, consequences (MADR-style).",
101 variables: [
102 { name: "decision", label: "Decision (one line)", type: "text", required: true },
103 { name: "status", label: "Status", type: "select", options: ["Proposed", "Accepted", "Deprecated", "Superseded"], defaultValue: "Proposed" }
104 ],
105 snapshot: page("ADR: {{decision}}", "🏛️", [
106 H1(bid("a"), "ADR: {{decision}}"),
107 CALLOUT(bid("a"), `Status: {{status}}`),
108 H2(bid("a"), "Context"), P(bid("a"), "The problem and the forces at play."),
109 H2(bid("a"), "Decision"), P(bid("a"), "We will …"),
110 H2(bid("a"), "Consequences"), P(bid("a"), "Positive / negative / neutral outcomes.")
111 ])
112 },
113 {
114 name: "Standard Operating Procedure", icon: "📑", category: "Operations",
115 description: "Repeatable SOP: purpose, scope, steps, owners.",
116 variables: [{ name: "task", label: "Procedure for", type: "text", required: true }],
117 snapshot: page("SOP: {{task}}", "📑", [
118 H1(bid("s"), "SOP: {{task}}"),
119 H2(bid("s"), "Purpose & scope"), P(bid("s"), "-"),
120 H2(bid("s"), "Prerequisites"), LIST(bid("s"), "Access / tools required"),
121 H2(bid("s"), "Steps"), TODO(bid("s"), "[ ] 1. "), TODO(bid("s"), "[ ] 2. "), TODO(bid("s"), "[ ] 3. "),
122 H2(bid("s"), "Owners & escalation"), P(bid("s"), "-")
123 ])
124 },
125 {
126 name: "Incident Report", icon: "🚨", category: "Incident Management",
127 description: "Live incident: status, impact, timeline, comms.",
128 variables: [
129 { name: "title", label: "Incident title", type: "text", required: true },
130 { name: "status", label: "Status", type: "select", options: ["Investigating", "Identified", "Monitoring", "Resolved"], defaultValue: "Investigating" }
131 ],
132 snapshot: page("{{title}}", "🚨", [
133 H1(bid("i"), "{{title}}"),
134 CALLOUT(bid("i"), `Status: {{status}}`),
135 H2(bid("i"), "Impact"), P(bid("i"), "Who/what is affected and how."),
136 H2(bid("i"), "Timeline"), LIST(bid("i"), "HH:MM — event"),
137 H2(bid("i"), "Actions taken"), LIST(bid("i"), "-"),
138 H2(bid("i"), "Communications"), P(bid("i"), "Customer / stakeholder updates.")
139 ])
140 },
141 {
142 name: "Postmortem", icon: "🔍", category: "Incident Management",
143 description: "Blameless postmortem: summary, timeline, root cause, actions.",
144 variables: [{ name: "incident", label: "Incident reference", type: "text", required: true }],
145 snapshot: page("Postmortem: {{incident}}", "🔍", [
146 H1(bid("pm"), "Postmortem: {{incident}}"),
147 H2(bid("pm"), "Summary"), P(bid("pm"), "What happened, in one paragraph."),
148 H2(bid("pm"), "Timeline"), LIST(bid("pm"), "HH:MM — event"),
149 H2(bid("pm"), "Root cause"), P(bid("pm"), "Why it happened (5 whys)."),
150 H2(bid("pm"), "Detection & response"), P(bid("pm"), "How we found it and how we responded."),
151 H2(bid("pm"), "Action items"), TODO(bid("pm"), "[ ] Owner — action — due"),
152 H2(bid("pm"), "What went well / poorly"), P(bid("pm"), "-")
153 ])
154 },
155 {
156 name: "Troubleshooting Guide", icon: "🩺", category: "Operations",
157 description: "Symptom → cause → fix runbook.",
158 variables: [{ name: "area", label: "System / area", type: "text", required: true }],
159 snapshot: page("Troubleshooting: {{area}}", "🩺", [
160 H1(bid("tr"), "Troubleshooting: {{area}}"),
161 H2(bid("tr"), "Symptoms"), LIST(bid("tr"), "-"),
162 H2(bid("tr"), "Diagnosis"), P(bid("tr"), "Commands / checks to run."),
163 H2(bid("tr"), "Known fixes"), LIST(bid("tr"), "-"),
164 H2(bid("tr"), "Escalation"), P(bid("tr"), "When and to whom to escalate.")
165 ])
166 },
167 {
168 name: "Release Notes", icon: "📦", category: "Product",
169 description: "Customer-facing release: highlights, changes, fixes.",
170 variables: [
171 { name: "version", label: "Version", type: "text", required: true },
172 { name: "date", label: "Release date", type: "date" }
173 ],
174 snapshot: page("Release {{version}}", "📦", [
175 H1(bid("rn"), "Release {{version}}"),
176 P(bid("rn"), "Released {{date}}"),
177 H2(bid("rn"), "Highlights"), LIST(bid("rn"), "-"),
178 H2(bid("rn"), "New features"), LIST(bid("rn"), "-"),
179 H2(bid("rn"), "Improvements & fixes"), LIST(bid("rn"), "-"),
180 H2(bid("rn"), "Known issues"), P(bid("rn"), "-")
181 ])
182 },
183 {
184 name: "Retrospective", icon: "🔄", category: "Meetings",
185 description: "What went well / poorly / actions for a team retro.",
186 variables: [{ name: "sprint", label: "Sprint / period", type: "text", required: true }],
187 snapshot: page("Retro: {{sprint}}", "🔄", [
188 H1(bid("ret"), "Retrospective — {{sprint}}"),
189 H2(bid("ret"), "What went well"), LIST(bid("ret"), "-"),
190 H2(bid("ret"), "What could improve"), LIST(bid("ret"), "-"),
191 H2(bid("ret"), "Action items"), TODO(bid("ret"), "[ ] Owner — action — due"),
192 H2(bid("ret"), "Parking lot"), P(bid("ret"), "-")
193 ])
194 },
195 {
196 name: "Weekly Status", icon: "📊", category: "Planning",
197 description: "Weekly status: progress, blockers, next week.",
198 variables: [{ name: "team", label: "Team / project", type: "text", required: true }],
199 snapshot: page("Weekly Status — {{team}}", "📊", [
200 H1(bid("ws"), "Weekly Status — {{team}}"),
201 H2(bid("ws"), "Progress this week"), LIST(bid("ws"), "-"),
202 H2(bid("ws"), "Blockers"), P(bid("ws"), "-"),
203 H2(bid("ws"), "Next week"), LIST(bid("ws"), "-"),
204 H2(bid("ws"), "Risks"), P(bid("ws"), "-")
205 ])
206 },
207 {
208 name: "Onboarding Guide", icon: "🧭", category: "People",
209 description: "New joiner checklist: setup, accounts, first week.",
210 variables: [{ name: "name", label: "New hire name", type: "text", required: true }],
211 snapshot: page("Onboarding: {{name}}", "🧭", [
212 H1(bid("on"), "Onboarding: {{name}}"),
213 H2(bid("on"), "Setup (day 1)"), TODO(bid("on"), "[ ] Accounts & access"), TODO(bid("on"), "[ ] Devices & tools"),
214 H2(bid("on"), "First week"), TODO(bid("on"), "[ ] Meet the team"), TODO(bid("on"), "[ ] Read the docs"),
215 H2(bid("on"), "30/60/90 goals"), P(bid("on"), "-")
216 ])
217 },
218 {
219 name: "Knowledge Base Article", icon: "📚", category: "Knowledge",
220 description: "KB article: overview, details, related, owner.",
221 variables: [
222 { name: "topic", label: "Topic", type: "text", required: true },
223 { name: "owner", label: "Owner", type: "text" }
224 ],
225 snapshot: page("{{topic}}", "📚", [
226 H1(bid("kb"), "{{topic}}"),
227 P(bid("kb"), `Owner: {{owner}}`),
228 H2(bid("kb"), "Overview"), P(bid("kb"), "What this covers and who it is for."),
229 H2(bid("kb"), "Details"), P(bid("kb"), "-"),
230 H2(bid("kb"), "Related"), LIST(bid("kb"), "-")
231 ])
232 },
233 {
234 name: "API Documentation", icon: "🔌", category: "Engineering",
235 description: "API reference: endpoints, params, errors, examples.",
236 variables: [{ name: "service", label: "Service name", type: "text", required: true }],
237 snapshot: page("API: {{service}}", "🔌", [
238 H1(bid("api"), "API: {{service}}"),
239 H2(bid("api"), "Base URL & auth"), P(bid("api"), "-"),
240 H2(bid("api"), "Endpoints"), LIST(bid("api"), "GET /…"), LIST(bid("api"), "POST /…"),
241 H2(bid("api"), "Parameters"), P(bid("api"), "Query / body fields."),
242 H2(bid("api"), "Errors"), P(bid("api"), "Codes and meanings."),
243 H2(bid("api"), "Examples"), P(bid("api"), "Request/response samples.")
244 ])
245 }
246];
247
248
256export interface BuiltinTemplatePrisma {
257 template: {
258 findMany(args: object): Promise<{ name: string }[]>;
259 create(args: object): Promise<unknown>;
260 };
261}
262
263export async function seedBuiltinTemplatesForWorkspace(
264 prisma: BuiltinTemplatePrisma,
265 workspaceId: string,
266 createdBy?: string
267): Promise<{ created: number; total: number }> {
268
269
270 const existing = await prisma.template.findMany({ where: { workspaceId, isBuiltin: true } });
271 const have = new Set(existing.map((r) => r.name));
272 let created = 0;
273 for (const def of BUILTIN_TEMPLATES) {
274 if (have.has(def.name)) continue;
275 await prisma.template.create({
276 data: {
277 workspaceId,
278 name: def.name,
279 icon: def.icon,
280 type: "page",
281 isBuiltin: true,
282 category: def.category,
283 description: def.description,
284 variables: def.variables ? (def.variables as object) : undefined,
285 snapshot: def.snapshot as object,
286 createdBy
287 }
288 });
289 created++;
290 }
291 return { created, total: BUILTIN_TEMPLATES.length };
292}
293