Skip to content

Commit c638f7e

Browse files
committed
Merge branch 'main' of github.com:devforth/adminforth
2 parents 3f48466 + 73ebffa commit c638f7e

26 files changed

Lines changed: 3049 additions & 49 deletions

‎README.md‎

Lines changed: 142 additions & 32 deletions
Large diffs are not rendered by default.

‎documentation/docusaurus.config.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ const config: Config = {
222222
label: 'API',
223223
},
224224
{ to: '/blog', label: 'Blog', position: 'left'},
225+
{ to: '/business/', label: 'For business', position: 'left'},
225226
{
226227
href: 'https://demo.adminforth.dev/',
227228
label: 'Live Demo',

‎documentation/src/openaiAds.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ type OutboundDestination = {
66
const OUTBOUND_DESTINATIONS = new Map<string, OutboundDestination>([
77
['github.com', {id: 'github', name: 'GitHub'}],
88
['demo.adminforth.dev', {id: 'live_demo', name: 'Live demo'}],
9+
['talk.devforth.io', {id: 'consultation', name: 'Consultation booking'}],
910
]);
1011

1112
declare global {
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// The brief builder of the business landing: examples, the data source labels and the build instructions
2+
// for the visitor's coding agent. No network, the page fills a template with the visitor's answers.
3+
4+
export type Source = 'spreadsheet' | 'app' | 'none' | 'unsure';
5+
6+
export type Brief = {manage: string; missed: string; team: string; source: Source; details: string};
7+
8+
export const EXAMPLES: Record<string, {label: string} & Brief> = {
9+
followups: {
10+
label: 'Customer follow-ups',
11+
manage: 'Customer requests for my maintenance business',
12+
missed: 'We send quotes and forget to follow up',
13+
team: 'Me and two managers; each manager handles their own requests',
14+
source: 'spreadsheet',
15+
details: 'A request needs a customer, owner, status, next action and due date.',
16+
},
17+
orders: {
18+
label: 'Order tracking',
19+
manage: 'Customer orders, from quote to delivery',
20+
missed: 'Nobody sees which orders are late until the customer calls',
21+
team: 'Me, a sales manager and two people in the warehouse; the warehouse only updates delivery status',
22+
source: 'spreadsheet',
23+
details: 'An order has a customer, items, total, status, delivery date and the person responsible.',
24+
},
25+
internal: {
26+
label: 'Internal requests',
27+
manage: 'Internal requests: equipment, access and purchases',
28+
missed: 'Requests sit in chat and nobody knows who approved what',
29+
team: 'Everyone can submit; two managers approve; finance sees approved purchases',
30+
source: 'none',
31+
details: 'A request has a requester, type, amount, approver, status and decision date.',
32+
},
33+
own: {label: 'My own process', manage: '', missed: '', team: '', source: 'unsure', details: ''},
34+
};
35+
36+
export const SOURCES: {value: Source; label: string; prompt: string}[] = [
37+
{value: 'spreadsheet', label: 'Spreadsheet', prompt: 'A spreadsheet. Start this example with synthetic data.'},
38+
{value: 'app', label: 'Existing app or database', prompt: 'An existing app or database. Start this example with synthetic data and describe how it would connect later.'},
39+
{value: 'none', label: 'Starting fresh', prompt: 'Nothing yet. Start with synthetic data.'},
40+
{value: 'unsure', label: 'Not sure', prompt: 'Not sure. Start this example with synthetic data.'},
41+
];
42+
43+
export const DAILY_PROMPTS = [
44+
'What needs my attention today?',
45+
'Which follow-ups are overdue, and whose?',
46+
'Add a request from Harbor Café: office maintenance quote. Owner Maya, next action: send the quote by Friday.',
47+
'Create a follow-up for Priya: call Brightside School tomorrow at 9.',
48+
'Which invoices are unpaid for more than 30 days?',
49+
];
50+
51+
export function buildPrompt(b: Brief, startUrl: string): string {
52+
const source = SOURCES.find((s) => s.value === b.source).prompt;
53+
return `Help me build a small business application with AdminForth. Work with me in
54+
plain language. I don't have a developer: tell me exactly what to do myself,
55+
step by step.
56+
57+
MY BUSINESS BRIEF
58+
BUSINESS_PROCESS: ${b.manage || 'Describe it with me before you start.'}
59+
CURRENT_PROBLEM: ${b.missed || 'Ask me what keeps getting missed.'}
60+
TEAM_ACCESS: ${b.team || 'Ask me who will use it and what each person may change.'}
61+
CURRENT_DATA: ${source}
62+
EXTRA_DETAILS: ${b.details || 'None yet. Suggest a sensible first set of fields and stages.'}
63+
64+
WHAT I WANT
65+
A web app on AdminForth that my agent and my team's agents work with through
66+
MCP, with sign-in, permissions and an audit log. I will mostly talk to it
67+
through my agent; screens are for checking. Keep the first version small and
68+
ready for more parts later (orders, jobs, stock, invoices).
69+
70+
START HERE
71+
Read ${startUrl} first, then
72+
https://adminforth.dev/docs/tutorial/gettingStarted/ and
73+
https://adminforth.dev/llms.txt. Use AdminForth as the framework: reuse
74+
its screens, forms, filters and access rules, don't build a separate stack.
75+
Use pnpm and Node.js 24. For a new local example use SQLite, the documented
76+
create-app flow and Prisma migrations. Read the generated AGENTS.md, CLAUDE.md
77+
and skills before changing anything. If something can't run here, tell me what
78+
I must do instead of pretending it works.
79+
80+
Show me a short plain-language outline (records, roles, the first operation)
81+
and ask only about decisions that change the data or the access rules.
82+
83+
BUILD
84+
1. Records and relationships for my process, named so my team understands
85+
them, with a small set of fictional sample records.
86+
2. Access rules on the server: I manage everything; every other person works
87+
only with what their role allows, including direct record requests, lists
88+
and relationships. A hidden button is not an access rule.
89+
3. The MCP plugin (https://adminforth.dev/docs/tutorial/Plugins/mcp/) so I can
90+
connect my agent, and the Audit Log plugin
91+
(https://adminforth.dev/docs/tutorial/Plugins/audit-log/) with its
92+
executed-by column, so every change names the person and the agent.
93+
4. No production data, emails or payments in this first version.
94+
95+
SHOW ME
96+
Run the app and give me the local address and sign-in. Connect my agent with
97+
the command from the Agents settings page, then do one operation through the
98+
agent: find a record that needs attention, add its next action, and show me the
99+
audit log entry. Show one action my agent is allowed and one a teammate's agent
100+
would be refused. Say which checks you actually performed.
101+
102+
Write five prompts for my daily work with this app, in the words of my process.
103+
Explain where the code and data are, how to reopen the app tomorrow, how to
104+
update AdminForth and its plugins later, and what remains before my team uses
105+
it: hosting, real data, backups, access review.
106+
`;
107+
}
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
import {EXAMPLES, type Brief} from './_brief';
2+
3+
export const LANDING_VERSION = 'business-2026-10-26-v1';
4+
5+
export type BusinessVariant = {
6+
id: string;
7+
campaign: string;
8+
label: string;
9+
heading: string;
10+
description: string;
11+
cta: string;
12+
recordType: string;
13+
records: {id: string; name: string; stage: string; owner: string; due: string; attention?: boolean}[];
14+
question: string;
15+
answer: string;
16+
operation: string;
17+
result: string;
18+
brief: Brief;
19+
prompts: string[];
20+
};
21+
22+
export const BUSINESS_VARIANTS: BusinessVariant[] = [
23+
{
24+
id: 'spreadsheet', campaign: 'business_sheet_to_app', label: 'Spreadsheet to business app',
25+
heading: 'Your business has outgrown the spreadsheet.',
26+
description: 'Turn your spreadsheet process into your own app. Your coding agent builds it with AdminForth: shared records, clear owners and next steps.',
27+
cta: 'Start my business app', recordType: 'Customer orders',
28+
records: [
29+
{id: 'ORD-1042', name: 'Harbor Café · equipment order', stage: 'Needs follow-up', owner: 'Maya', due: 'Yesterday', attention: true},
30+
{id: 'ORD-1043', name: 'Atlas Office · supplies', stage: 'Preparing', owner: 'Tom', due: 'Today'},
31+
{id: 'ORD-1044', name: 'Brightside School · replacement parts', stage: 'Ready to deliver', owner: 'Priya', due: 'Tomorrow'},
32+
],
33+
question: 'Which orders need a next action today?',
34+
answer: 'Harbor Café’s order is overdue. Maya owns it. No next action.',
35+
operation: 'Ask Maya to confirm the delivery date today.',
36+
result: 'Saved on the order. Everyone sees the same next step.',
37+
brief: {...EXAMPLES.orders, manage: 'The customer and order process we currently run in a spreadsheet'},
38+
prompts: ['Which orders need a next action today?', 'Show the customer, owner and current stage for ORD-1042.', 'Ask Maya to confirm the delivery date today.', 'Show orders with missing owners or dates.', 'Who last changed the stage of ORD-1042?'],
39+
},
40+
{
41+
id: 'followups', campaign: 'business_quote_followups', label: 'Customer follow-ups',
42+
heading: 'Quotes sent. Follow-ups forgotten?',
43+
description: 'A good quote should not die in a spreadsheet. Build your own follow-up app with AdminForth and your coding agent. Every quote gets an owner and a next step.',
44+
cta: 'Start my follow-up app', recordType: 'Quote follow-ups',
45+
records: [
46+
{id: 'REQ-1042', name: 'Harbor Café · maintenance quote', stage: 'Follow-up overdue', owner: 'Maya', due: 'Yesterday', attention: true},
47+
{id: 'REQ-1043', name: 'Atlas Office · service proposal', stage: 'Call scheduled', owner: 'Tom', due: 'Today'},
48+
{id: 'REQ-1044', name: 'Brightside School · inspection quote', stage: 'Awaiting reply', owner: 'Priya', due: 'Tomorrow'},
49+
],
50+
question: 'Which quotes have overdue follow-ups, and whose are they?',
51+
answer: 'Harbor Café’s quote is overdue for a follow-up. Owner: Maya.',
52+
operation: 'Maya: call Harbor Café today at 14:00.',
53+
result: 'The call and due time are saved on the quote.',
54+
brief: EXAMPLES.followups,
55+
prompts: ['Which quotes have overdue follow-ups, and whose are they?', 'Show quotes waiting for a reply with no next action.', 'Maya: call Harbor Café today at 14:00.', 'Which customers does Tom need to contact today?', 'Show the last change to REQ-1042.'],
56+
},
57+
{
58+
id: 'live_records', campaign: 'business_live_records', label: 'Live business records',
59+
heading: 'Stop uploading yesterday’s business data to AI.',
60+
description: 'Still exporting files for AI, then copying changes back? Build an AdminForth app your agent can read and update directly, with your team’s access rules.',
61+
cta: 'Start my connected app', recordType: 'Live business records',
62+
records: [
63+
{id: 'REQ-1042', name: 'Harbor Café · service request', stage: 'Next action missing', owner: 'Maya', due: 'Today', attention: true},
64+
{id: 'REQ-1043', name: 'Atlas Office · site visit', stage: 'Visit confirmed', owner: 'Tom', due: 'Tomorrow'},
65+
{id: 'REQ-1044', name: 'Brightside School · inspection', stage: 'Quote sent', owner: 'Priya', due: 'Thursday'},
66+
],
67+
question: 'Read my current requests. Which ones have no next action?',
68+
answer: 'Harbor Café’s request has no next action. Owner: Maya.',
69+
operation: 'Maya: confirm the visit time today.',
70+
result: 'Saved in your app. Your team and agent see the update.',
71+
brief: {...EXAMPLES.followups, missed: 'I keep exporting a spreadsheet to AI and copying changes back. I want my agent to read and update the same current records my team uses.', details: 'Build customer requests with owner, status, next action and due date. Connect my agent through MCP. Show a permitted update and an action refused by the server.'},
72+
prompts: ['Read my current requests. Which ones have no next action?', 'Show the current record for REQ-1042.', 'Maya: confirm the visit time today.', 'Read REQ-1042 again and show the saved next action.', 'Show the audit log for that update.'],
73+
},
74+
{
75+
id: 'repairs', campaign: 'business_repair_status', label: 'Repair workshop',
76+
heading: '“Is my repair ready?” Know before they call.',
77+
description: 'Stop searching spreadsheets when a customer calls. Build a repair tracker with AdminForth and your coding agent: what’s stuck, who owns it, what happens next.',
78+
cta: 'Start my repair tracker', recordType: 'Repairs',
79+
records: [
80+
{id: 'REP-1042', name: 'Espresso machine · pump replacement', stage: 'Waiting for part', owner: 'Maya', due: 'Yesterday', attention: true},
81+
{id: 'REP-1043', name: 'Office printer · paper feed', stage: 'In repair', owner: 'Tom', due: 'Today'},
82+
{id: 'REP-1044', name: 'Vacuum cleaner · motor check', stage: 'Ready to collect', owner: 'Priya', due: 'Tomorrow'},
83+
],
84+
question: 'Which repairs are late, and what is blocking them?',
85+
answer: 'Espresso machine: overdue, waiting for a pump. Owner: Maya.',
86+
operation: 'Maya: check the pump delivery today at 14:00.',
87+
result: 'The part check is saved on the repair, with a due time.',
88+
brief: {
89+
manage: 'Repair jobs in my workshop, from intake to collection',
90+
missed: 'Repair status and missing parts are spread across spreadsheets and messages. Customers call before we notice a job is late.',
91+
team: 'I manage the workshop; technicians update their assigned repairs; the front desk checks status and collection dates',
92+
source: 'spreadsheet',
93+
details: 'Each repair needs a customer, item, issue, technician, status, promised date, missing part and next action. Start with intake, diagnosis, waiting for parts, in repair and ready to collect.',
94+
},
95+
prompts: ['Which repairs are overdue, and what is blocking each one?', 'Show repairs waiting for parts, with their owners.', 'Set the next action for REP-1042: check the pump delivery today at 14:00.', 'Which repairs are ready for collection?', 'Show who changed the promised date of REP-1042.'],
96+
},
97+
{
98+
id: 'production', campaign: 'business_production_orders', label: 'Small production team',
99+
heading: 'Spot a late order before your customer does.',
100+
description: 'Sales promised Friday. Production is still waiting. Build one order tracker with AdminForth and your coding agent, so both teams see the same stages and dates.',
101+
cta: 'Start my order tracker', recordType: 'Production orders',
102+
records: [
103+
{id: 'ORD-2081', name: 'Oak display shelves · 12 units', stage: 'Waiting for finish', owner: 'Maya', due: 'Tomorrow', attention: true},
104+
{id: 'ORD-2082', name: 'Counter signs · 30 units', stage: 'In production', owner: 'Tom', due: 'Thursday'},
105+
{id: 'ORD-2083', name: 'Display stands · 8 units', stage: 'Ready to ship', owner: 'Priya', due: 'Today'},
106+
],
107+
question: 'Which orders are due tomorrow but not ready to ship?',
108+
answer: '12 oak shelves: due tomorrow, still waiting for finishing.',
109+
operation: 'Maya: confirm the finishing slot today at 11:00.',
110+
result: 'Sales and production can see the next action on the order.',
111+
brief: {
112+
manage: 'Customer orders through our small production team, from quote to shipment',
113+
missed: 'Sales promises a delivery date, but production stages live in separate spreadsheets. We notice delays when customers ask.',
114+
team: 'I see all orders; sales manages customer details and dates; production updates stages; dispatch updates shipment details',
115+
source: 'spreadsheet',
116+
details: 'Track customer, ordered items, quantity, production stage, owner, promised date, blocker, next action and shipment status. Begin with confirmed, materials, in production, finishing, ready to ship and shipped.',
117+
},
118+
prompts: ['Which orders are due tomorrow but not ready to ship?', 'Show orders with a blocker and no next action.', 'Add a finishing-slot check for ORD-2081, owned by Maya, due today at 11:00.', 'Which orders are ready for dispatch today?', 'Show the change history for the delivery date of ORD-2081.'],
119+
},
120+
{
121+
id: 'field_service', campaign: 'business_field_jobs', label: 'Field service team',
122+
heading: 'Job booked. But who’s going?',
123+
description: 'Jobs in messages. Assignments in a spreadsheet. Build one job tracker with AdminForth and your coding agent, so every visit has an owner and a next step.',
124+
cta: 'Start my job tracker', recordType: 'Service jobs',
125+
records: [
126+
{id: 'JOB-3017', name: 'Harbor Café · cold room inspection', stage: 'Needs assignment', owner: 'Unassigned', due: 'Tomorrow', attention: true},
127+
{id: 'JOB-3018', name: 'Brightside School · ventilation', stage: 'Visit scheduled', owner: 'Tom', due: 'Today'},
128+
{id: 'JOB-3019', name: 'Atlas Office · pump maintenance', stage: 'Follow-up needed', owner: 'Priya', due: 'Thursday'},
129+
],
130+
question: 'Which jobs for tomorrow still have nobody assigned?',
131+
answer: 'Harbor Café’s inspection is tomorrow. Nobody is assigned.',
132+
operation: 'Assign Maya. Ask her to confirm the visit time.',
133+
result: 'Maya now owns the job. The office can see the next step.',
134+
brief: {
135+
manage: 'Service requests and site visits for my field team',
136+
missed: 'Jobs arrive in messages and a spreadsheet. We lose track of who is going, what is unfinished and who should follow up.',
137+
team: 'I manage all work; the office assigns jobs and schedules visits; technicians see and update only their assigned jobs',
138+
source: 'spreadsheet',
139+
details: 'A job needs a customer, site, request, priority, owner, visit date, status, visit notes and next action. Start with new, assigned, scheduled, on site, follow-up needed and completed. Do not build automatic dispatch or route optimisation.',
140+
},
141+
prompts: ['Which jobs for tomorrow still have nobody assigned?', 'Show unfinished visits with no next action.', 'Assign JOB-3017 to Maya and add a visit confirmation as the next action.', 'What does Tom need to finish today?', 'Show who changed the status of JOB-3017.'],
142+
},
143+
];
144+
145+
export function businessVariant(campaign: string | null): BusinessVariant | undefined {
146+
return BUSINESS_VARIANTS.find((variant) => variant.campaign === campaign);
147+
}

0 commit comments

Comments
 (0)