db.ts and server.ts work confirmed by test.js. moving to cards.
This commit is contained in:
@@ -10,9 +10,11 @@
|
|||||||
"bun-plugin-tailwind": "^0.1.2",
|
"bun-plugin-tailwind": "^0.1.2",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
"kysely": "^0.28.8",
|
||||||
"lucide-react": "^0.545.0",
|
"lucide-react": "^0.545.0",
|
||||||
"react": "^19",
|
"react": "^19",
|
||||||
"react-dom": "^19",
|
"react-dom": "^19",
|
||||||
|
"sqlite": "^5.1.1",
|
||||||
"tailwind-merge": "^3.3.1",
|
"tailwind-merge": "^3.3.1",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -135,6 +137,8 @@
|
|||||||
|
|
||||||
"get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="],
|
"get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="],
|
||||||
|
|
||||||
|
"kysely": ["kysely@0.28.8", "", {}, "sha512-QUOgl5ZrS9IRuhq5FvOKFSsD/3+IA6MLE81/bOOTRA/YQpKDza2sFdN5g6JCB9BOpqMJDGefLCQ9F12hRS13TA=="],
|
||||||
|
|
||||||
"lucide-react": ["lucide-react@0.545.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-7r1/yUuflQDSt4f1bpn5ZAocyIxcTyVyBBChSVtBKn5M+392cPmI5YJMWOJKk/HUWGm5wg83chlAZtCcGbEZtw=="],
|
"lucide-react": ["lucide-react@0.545.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-7r1/yUuflQDSt4f1bpn5ZAocyIxcTyVyBBChSVtBKn5M+392cPmI5YJMWOJKk/HUWGm5wg83chlAZtCcGbEZtw=="],
|
||||||
|
|
||||||
"react": ["react@19.2.0", "", {}, "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ=="],
|
"react": ["react@19.2.0", "", {}, "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ=="],
|
||||||
@@ -149,6 +153,8 @@
|
|||||||
|
|
||||||
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
||||||
|
|
||||||
|
"sqlite": ["sqlite@5.1.1", "", {}, "sha512-oBkezXa2hnkfuJwUo44Hl9hS3er+YFtueifoajrgidvqsJRQFpc5fKoAkAor1O5ZnLoa28GBScfHXs8j0K358Q=="],
|
||||||
|
|
||||||
"tailwind-merge": ["tailwind-merge@3.4.0", "", {}, "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g=="],
|
"tailwind-merge": ["tailwind-merge@3.4.0", "", {}, "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g=="],
|
||||||
|
|
||||||
"tailwindcss": ["tailwindcss@4.1.17", "", {}, "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q=="],
|
"tailwindcss": ["tailwindcss@4.1.17", "", {}, "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q=="],
|
||||||
|
|||||||
@@ -15,9 +15,11 @@
|
|||||||
"bun-plugin-tailwind": "^0.1.2",
|
"bun-plugin-tailwind": "^0.1.2",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
"kysely": "^0.28.8",
|
||||||
"lucide-react": "^0.545.0",
|
"lucide-react": "^0.545.0",
|
||||||
"react": "^19",
|
"react": "^19",
|
||||||
"react-dom": "^19",
|
"react-dom": "^19",
|
||||||
|
"sqlite": "^5.1.1",
|
||||||
"tailwind-merge": "^3.3.1"
|
"tailwind-merge": "^3.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -0,0 +1,179 @@
|
|||||||
|
// db.ts
|
||||||
|
import { Database } from "bun:sqlite";
|
||||||
|
|
||||||
|
// --- Initialize Bun SQLite ---
|
||||||
|
const db = new Database("./flow.db");
|
||||||
|
|
||||||
|
// --- Helper functions to bypass TS binding issues ---
|
||||||
|
function getOne<T>(sql: string, ...params: any[]): T | null {
|
||||||
|
const row = db.query(sql).get(...params as any);
|
||||||
|
return row ? (row as T) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAll<T>(sql: string, ...params: any[]): T[] {
|
||||||
|
const rows = db.query(sql).all(...params as any);
|
||||||
|
return rows.map(r => r as T);
|
||||||
|
}
|
||||||
|
|
||||||
|
function run(sql: string, ...params: any[]) {
|
||||||
|
db.run(sql, ...params as any);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Create tables if they do not exist ---
|
||||||
|
run(`
|
||||||
|
CREATE TABLE IF NOT EXISTS flow_steps (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
stepNumber TEXT,
|
||||||
|
caseCode TEXT,
|
||||||
|
subStep TEXT,
|
||||||
|
stage TEXT,
|
||||||
|
role TEXT,
|
||||||
|
handoffTo TEXT,
|
||||||
|
handoffChannel TEXT,
|
||||||
|
description TEXT,
|
||||||
|
createdAt TEXT DEFAULT CURRENT_TIMESTAMP
|
||||||
|
)
|
||||||
|
`);
|
||||||
|
|
||||||
|
run(`
|
||||||
|
CREATE TABLE IF NOT EXISTS extended_descriptions (
|
||||||
|
stepId TEXT PRIMARY KEY REFERENCES flow_steps(id),
|
||||||
|
content TEXT
|
||||||
|
)
|
||||||
|
`);
|
||||||
|
|
||||||
|
run(`
|
||||||
|
CREATE TABLE IF NOT EXISTS questions (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
stepId TEXT REFERENCES flow_steps(id),
|
||||||
|
question TEXT,
|
||||||
|
followUp TEXT,
|
||||||
|
createdAt TEXT DEFAULT CURRENT_TIMESTAMP
|
||||||
|
)
|
||||||
|
`);
|
||||||
|
|
||||||
|
run(`
|
||||||
|
CREATE TABLE IF NOT EXISTS concerns (
|
||||||
|
stepId TEXT PRIMARY KEY REFERENCES flow_steps(id),
|
||||||
|
flagged INTEGER DEFAULT 0,
|
||||||
|
comment TEXT,
|
||||||
|
createdAt TEXT DEFAULT CURRENT_TIMESTAMP
|
||||||
|
)
|
||||||
|
`);
|
||||||
|
|
||||||
|
// --- Types ---
|
||||||
|
export type FlowStep = {
|
||||||
|
id: string;
|
||||||
|
stepNumber: string;
|
||||||
|
caseCode: string;
|
||||||
|
subStep: string;
|
||||||
|
stage: string;
|
||||||
|
role: string;
|
||||||
|
handoffTo: string;
|
||||||
|
handoffChannel: string;
|
||||||
|
description: string;
|
||||||
|
createdAt: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ExtendedDescription = {
|
||||||
|
stepId: string;
|
||||||
|
content: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Question = {
|
||||||
|
id: number;
|
||||||
|
stepId: string;
|
||||||
|
question: string;
|
||||||
|
followUp: string | null;
|
||||||
|
createdAt: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Concern = {
|
||||||
|
stepId: string;
|
||||||
|
flagged: boolean;
|
||||||
|
comment: string;
|
||||||
|
createdAt: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- Flow Steps ---
|
||||||
|
export function createStep(step: Omit<FlowStep, "createdAt">) {
|
||||||
|
run(
|
||||||
|
`INSERT OR IGNORE INTO flow_steps
|
||||||
|
(id, stepNumber, caseCode, subStep, stage, role, handoffTo, handoffChannel, description)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
|
step.id,
|
||||||
|
step.stepNumber,
|
||||||
|
step.caseCode,
|
||||||
|
step.subStep,
|
||||||
|
step.stage,
|
||||||
|
step.role,
|
||||||
|
step.handoffTo,
|
||||||
|
step.handoffChannel,
|
||||||
|
step.description
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getStep(id: string): FlowStep | null {
|
||||||
|
return getOne<FlowStep>("SELECT * FROM flow_steps WHERE id = ?", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAllSteps(): FlowStep[] {
|
||||||
|
return getAll<FlowStep>("SELECT * FROM flow_steps");
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Extended Descriptions ---
|
||||||
|
export function saveExtendedDescription(stepId: string, content: string) {
|
||||||
|
run(
|
||||||
|
`INSERT OR REPLACE INTO extended_descriptions (stepId, content) VALUES (?, ?)`,
|
||||||
|
stepId,
|
||||||
|
content
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getExtendedDescription(stepId: string): string | null {
|
||||||
|
const row = getOne<{ content: string }>(
|
||||||
|
"SELECT content FROM extended_descriptions WHERE stepId = ?",
|
||||||
|
stepId
|
||||||
|
);
|
||||||
|
return row ? row.content : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Questions ---
|
||||||
|
export function addQuestion(stepId: string, question: string, followUp?: string | null) {
|
||||||
|
run(
|
||||||
|
`INSERT INTO questions (stepId, question, followUp) VALUES (?, ?, ?)`,
|
||||||
|
stepId,
|
||||||
|
question,
|
||||||
|
followUp ?? null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getQuestions(stepId: string): Question[] {
|
||||||
|
return getAll<Question>("SELECT * FROM questions WHERE stepId = ?", stepId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Concerns ---
|
||||||
|
export function flagConcern(stepId: string, flagged: boolean, comment: string) {
|
||||||
|
run(
|
||||||
|
`INSERT OR REPLACE INTO concerns (stepId, flagged, comment) VALUES (?, ?, ?)`,
|
||||||
|
stepId,
|
||||||
|
flagged ? 1 : 0,
|
||||||
|
comment
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getConcern(stepId: string): Concern | null {
|
||||||
|
const row = getOne<{ stepId: string; flagged: number; comment: string; createdAt: string }>(
|
||||||
|
"SELECT * FROM concerns WHERE stepId = ?",
|
||||||
|
stepId
|
||||||
|
);
|
||||||
|
if (!row) return null;
|
||||||
|
return {
|
||||||
|
stepId: row.stepId,
|
||||||
|
flagged: row.flagged === 1,
|
||||||
|
comment: row.comment,
|
||||||
|
createdAt: row.createdAt
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//export default db;
|
||||||
+105
@@ -0,0 +1,105 @@
|
|||||||
|
// src/server.ts
|
||||||
|
import { serve } from "bun";
|
||||||
|
import {
|
||||||
|
createStep,
|
||||||
|
getStep,
|
||||||
|
getAllSteps,
|
||||||
|
saveExtendedDescription,
|
||||||
|
getExtendedDescription,
|
||||||
|
addQuestion,
|
||||||
|
getQuestions,
|
||||||
|
flagConcern,
|
||||||
|
getConcern
|
||||||
|
} from "./db.ts";
|
||||||
|
|
||||||
|
const corsHeaders = {
|
||||||
|
"Access-Control-Allow-Origin": "*",
|
||||||
|
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
|
||||||
|
"Access-Control-Allow-Headers": "Content-Type",
|
||||||
|
};
|
||||||
|
|
||||||
|
async function getJSON(req: Request) {
|
||||||
|
try {
|
||||||
|
return await req.json();
|
||||||
|
} catch {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
serve({
|
||||||
|
port: 3000,
|
||||||
|
fetch: async (req: Request) => {
|
||||||
|
const { method } = req;
|
||||||
|
const path = new URL(req.url).pathname;
|
||||||
|
|
||||||
|
if (method === "OPTIONS") {
|
||||||
|
return new Response(null, { headers: corsHeaders });
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Steps ---
|
||||||
|
if (path === "/steps" && method === "GET") {
|
||||||
|
return new Response(JSON.stringify(getAllSteps()), { headers: { ...corsHeaders, "Content-Type": "application/json" } });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.startsWith("/steps/") && method === "GET") {
|
||||||
|
const stepId = String(path.split("/")[2]);
|
||||||
|
return new Response(JSON.stringify(getStep(stepId)), { headers: { ...corsHeaders, "Content-Type": "application/json" } });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path === "/steps" && method === "POST") {
|
||||||
|
const data = await getJSON(req);
|
||||||
|
createStep({
|
||||||
|
id: String(data.id),
|
||||||
|
stepNumber: String(data.stepNumber),
|
||||||
|
caseCode: String(data.caseCode),
|
||||||
|
subStep: String(data.subStep),
|
||||||
|
stage: String(data.stage),
|
||||||
|
role: String(data.role),
|
||||||
|
handoffTo: String(data.handoffTo),
|
||||||
|
handoffChannel: String(data.handoffChannel),
|
||||||
|
description: String(data.description),
|
||||||
|
});
|
||||||
|
return new Response(JSON.stringify({ success: true }), { headers: { ...corsHeaders, "Content-Type": "application/json" } });
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Extended Descriptions ---
|
||||||
|
if (path === "/descriptions" && method === "POST") {
|
||||||
|
const { stepId, content } = await getJSON(req);
|
||||||
|
saveExtendedDescription(String(stepId), String(content));
|
||||||
|
return new Response(JSON.stringify({ success: true }), { headers: { ...corsHeaders, "Content-Type": "application/json" } });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.startsWith("/descriptions/") && method === "GET") {
|
||||||
|
const stepId = String(path.split("/")[2]);
|
||||||
|
return new Response(JSON.stringify({ stepId, content: getExtendedDescription(stepId) }), { headers: { ...corsHeaders, "Content-Type": "application/json" } });
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Questions ---
|
||||||
|
if (path === "/questions" && method === "POST") {
|
||||||
|
const { stepId, question, followUp } = await getJSON(req);
|
||||||
|
addQuestion(String(stepId), String(question), followUp ? String(followUp) : null);
|
||||||
|
return new Response(JSON.stringify({ success: true }), { headers: { ...corsHeaders, "Content-Type": "application/json" } });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.startsWith("/questions/") && method === "GET") {
|
||||||
|
const stepId = String(path.split("/")[2]);
|
||||||
|
return new Response(JSON.stringify(getQuestions(stepId)), { headers: { ...corsHeaders, "Content-Type": "application/json" } });
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Concerns ---
|
||||||
|
if (path === "/concerns" && method === "POST") {
|
||||||
|
const { stepId, flagged, comment } = await getJSON(req);
|
||||||
|
flagConcern(String(stepId), Boolean(flagged), String(comment));
|
||||||
|
return new Response(JSON.stringify({ success: true }), { headers: { ...corsHeaders, "Content-Type": "application/json" } });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.startsWith("/concerns/") && method === "GET") {
|
||||||
|
const stepId = String(path.split("/")[2]);
|
||||||
|
return new Response(JSON.stringify(getConcern(stepId)), { headers: { ...corsHeaders, "Content-Type": "application/json" } });
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response("Not Found", { status: 404 });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("Server running at http://localhost:3000");
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
// test-server.ts
|
||||||
|
async function test() {
|
||||||
|
const base = "http://localhost:3000";
|
||||||
|
|
||||||
|
// --- Create a step ---
|
||||||
|
const stepData = {
|
||||||
|
id: "step1",
|
||||||
|
stepNumber: "0.1",
|
||||||
|
caseCode: "caseA",
|
||||||
|
subStep: "a",
|
||||||
|
stage: "Pre-Intake",
|
||||||
|
role: "Estimator",
|
||||||
|
handoffTo: "Office Manager",
|
||||||
|
handoffChannel: "Phone",
|
||||||
|
description: "Initial ITB received",
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log("Creating step...");
|
||||||
|
let res = await fetch(`${base}/steps`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify(stepData),
|
||||||
|
});
|
||||||
|
console.log(await res.json());
|
||||||
|
|
||||||
|
// --- Get the step ---
|
||||||
|
console.log("Getting step...");
|
||||||
|
res = await fetch(`${base}/steps/${stepData.id}`);
|
||||||
|
console.log(await res.json());
|
||||||
|
|
||||||
|
// --- Add extended description ---
|
||||||
|
console.log("Adding extended description...");
|
||||||
|
res = await fetch(`${base}/descriptions`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ stepId: stepData.id, content: "Extended info about step1" }),
|
||||||
|
});
|
||||||
|
console.log(await res.json());
|
||||||
|
|
||||||
|
// --- Get extended description ---
|
||||||
|
console.log("Getting extended description...");
|
||||||
|
res = await fetch(`${base}/descriptions/${stepData.id}`);
|
||||||
|
console.log(await res.json());
|
||||||
|
|
||||||
|
// --- Add a question ---
|
||||||
|
console.log("Adding question...");
|
||||||
|
res = await fetch(`${base}/questions`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ stepId: stepData.id, question: "Follow up question?", followUp: "Yes, clarify details" }),
|
||||||
|
});
|
||||||
|
console.log(await res.json());
|
||||||
|
|
||||||
|
// --- Get questions ---
|
||||||
|
console.log("Getting questions...");
|
||||||
|
res = await fetch(`${base}/questions/${stepData.id}`);
|
||||||
|
console.log(await res.json());
|
||||||
|
|
||||||
|
// --- Add a concern ---
|
||||||
|
console.log("Adding concern...");
|
||||||
|
res = await fetch(`${base}/concerns`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ stepId: stepData.id, flagged: true, comment: "Check this step for errors" }),
|
||||||
|
});
|
||||||
|
console.log(await res.json());
|
||||||
|
|
||||||
|
// --- Get concern ---
|
||||||
|
console.log("Getting concern...");
|
||||||
|
res = await fetch(`${base}/concerns/${stepData.id}`);
|
||||||
|
console.log(await res.json());
|
||||||
|
|
||||||
|
// --- Get all steps ---
|
||||||
|
console.log("Getting all steps...");
|
||||||
|
res = await fetch(`${base}/steps`);
|
||||||
|
console.log(await res.json());
|
||||||
|
|
||||||
|
console.log("Test complete.");
|
||||||
|
}
|
||||||
|
|
||||||
|
test();
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import workflow from "./workflow.json";
|
||||||
|
|
||||||
|
export function useWorkflow() {
|
||||||
|
return workflow;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user