Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
],
"scripts": {
"dev": "next dev",
"prebuild": "npm run build --workspace=@crewcircle/knowledge",
"build": "next build",
"start": "next start",
"lint": "eslint"
Expand Down
12 changes: 6 additions & 6 deletions packages/knowledge/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,32 @@
"version": "0.1.0",
"description": "CrewCircle Knowledge Layer - Internal Org Memory & External App Context Layer",
"main": "dist/index.js",
"module": "dist/index.mjs",
"module": "dist/esm/index.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"import": "./dist/esm/index.js",
"require": "./dist/index.js"
},
"./core": {
"types": "./dist/core/index.d.ts",
"import": "./dist/core/index.mjs",
"import": "./dist/esm/core/index.js",
"require": "./dist/core/index.js"
},
"./providers": {
"types": "./dist/providers/index.d.ts",
"import": "./dist/providers/index.mjs",
"import": "./dist/esm/providers/index.js",
"require": "./dist/providers/index.js"
},
"./internal": {
"types": "./dist/internal/index.d.ts",
"import": "./dist/internal/index.mjs",
"import": "./dist/esm/internal/index.js",
"require": "./dist/internal/index.js"
},
"./external": {
"types": "./dist/external/index.d.ts",
"import": "./dist/external/index.mjs",
"import": "./dist/esm/external/index.js",
"require": "./dist/external/index.js"
},
"./ai-sdk": {
Expand Down
1 change: 0 additions & 1 deletion packages/knowledge/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
Expand Down
1 change: 0 additions & 1 deletion packages/knowledge/vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,4 @@ export default defineConfig({
'@crewcircle/knowledge': path.resolve(__dirname, './src/index.ts'),
},
},
tsconfig: './tsconfig.json',
});
14 changes: 4 additions & 10 deletions src/app/admin/costs/fixed-costs-table.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
"use client";

import { DataTable } from "@crewcircle/admin-ui";

interface FixedCostItem {
name: string;
category: string;
provider: string;
amount_cents: number;
frequency: string;
}
import type { DataTableColumn } from "@crewcircle/admin-ui";
import type { FixedCostItem } from "@/lib/admin/costs";

export function FixedCostsTable({ items }: { items: FixedCostItem[] }) {
const columns = [
const columns: DataTableColumn<FixedCostItem>[] = [
{ key: "name" as const, header: "Name", sortable: true },
{ key: "category" as const, header: "Category", sortable: true },
{ key: "provider" as const, header: "Provider", sortable: true },
Expand All @@ -30,7 +24,7 @@ export function FixedCostsTable({ items }: { items: FixedCostItem[] }) {
];

return (
<DataTable
<DataTable<FixedCostItem>
columns={columns}
rows={items}
emptyMessage="No fixed costs recorded. Add one via the API."
Expand Down
5 changes: 3 additions & 2 deletions src/app/admin/knowledge/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getOrgMemory, InternalMemory } from "@crewcircle/knowledge";
import { InternalMemory } from "@crewcircle/knowledge";
import { Search, FileText, Database, Zap, Loader2, TrendingUp } from "lucide-react";

export const dynamic = "force-dynamic";
Expand All @@ -14,7 +14,8 @@ interface KnowledgeStats {

async function getKnowledgeStats(): Promise<KnowledgeStats> {
try {
const memory = await InternalMemory.create({ provider: "mock" });
const memory = new InternalMemory({ provider: "mock" });
await memory.initialize();

const [standards, decisions, projects, retros, techs, adrs] = await Promise.all([
memory.searchStandards("").then(r => r.chunks.length),
Expand Down
6 changes: 4 additions & 2 deletions src/app/admin/projects-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import { useRouter } from "next/navigation";
import { DataTable, StatusBadge } from "@crewcircle/admin-ui";
import type { DataTableColumn } from "@crewcircle/admin-ui";

interface ProjectRow {
[key: string]: unknown;
id: string;
name: string;
status: string;
Expand All @@ -15,7 +17,7 @@ interface ProjectRow {
export function ProjectsTable({ projects }: { projects: ProjectRow[] }) {
const router = useRouter();

const columns = [
const columns: DataTableColumn<ProjectRow>[] = [
{ key: "name", header: "Project", sortable: true },
{ key: "id", header: "ID", sortable: true },
{
Expand Down Expand Up @@ -54,7 +56,7 @@ export function ProjectsTable({ projects }: { projects: ProjectRow[] }) {
];

return (
<DataTable
<DataTable<ProjectRow>
columns={columns}
rows={projects}
emptyMessage="No projects registered yet. Provision one to get started."
Expand Down
4 changes: 2 additions & 2 deletions src/app/admin/projects/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { notFound } from "next/navigation";
import { StatusBadge } from "@crewcircle/admin-ui";
import { readRegistry } from "@/lib/admin/registry";
import { getRepo } from "@/lib/admin/github";
import { ExternalLink, Github, BarChart3 } from "lucide-react";
import { ExternalLink, GitFork, BarChart3 } from "lucide-react";
import { DestroyProjectButton } from "./destroy-button";

export const dynamic = "force-dynamic";
Expand Down Expand Up @@ -57,7 +57,7 @@ export default async function ProjectDetailPage({
rel="noopener noreferrer"
className="inline-flex items-center gap-1.5 rounded-md border border-gray-300 bg-white px-3 py-1.5 text-sm font-medium text-gray-700 transition-colors hover:bg-gray-50"
>
<Github className="h-4 w-4" />
<GitFork className="h-4 w-4" />
GitHub
</a>
)}
Expand Down
4 changes: 3 additions & 1 deletion src/app/api/admin/costs/fixed/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ async function checkAuth(): Promise<NextResponse | null> {
await requireAdmin();
return null;
} catch (e) {
if (e instanceof Response) return e;
if (e instanceof Response) {
return new NextResponse(e.body, { status: e.status, headers: e.headers });
}
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/app/api/admin/costs/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ export async function GET(request: NextRequest): Promise<NextResponse> {
try {
await requireAdmin();
} catch (e) {
if (e instanceof Response) return e;
if (e instanceof Response) {
return new NextResponse(e.body, { status: e.status, headers: e.headers });
}
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}

Expand Down
4 changes: 3 additions & 1 deletion src/app/api/admin/observability/llm/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ export async function GET(request: NextRequest): Promise<NextResponse> {
try {
await requireAdmin();
} catch (e) {
if (e instanceof Response) return e;
if (e instanceof Response) {
return new NextResponse(e.body, { status: e.status, headers: e.headers });
}
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}

Expand Down
4 changes: 3 additions & 1 deletion src/app/api/admin/observability/sentry/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ export async function GET(): Promise<NextResponse> {
try {
await requireAdmin();
} catch (e) {
if (e instanceof Response) return e;
if (e instanceof Response) {
return new NextResponse(e.body, { status: e.status, headers: e.headers });
}
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}

Expand Down
4 changes: 3 additions & 1 deletion src/app/api/admin/observability/uptime/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ export async function GET(): Promise<NextResponse> {
try {
await requireAdmin();
} catch (e) {
if (e instanceof Response) return e;
if (e instanceof Response) {
return new NextResponse(e.body, { status: e.status, headers: e.headers });
}
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}

Expand Down
4 changes: 3 additions & 1 deletion src/app/api/admin/projects/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ export async function GET(): Promise<NextResponse> {
try {
await requireAdmin();
} catch (e) {
if (e instanceof Response) return e;
if (e instanceof Response) {
return new NextResponse(e.body, { status: e.status, headers: e.headers });
}
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}

Expand Down
1 change: 1 addition & 0 deletions src/lib/admin/costs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export interface CostDashboardData {
}

export interface FixedCostItem {
[key: string]: unknown;
id: string;
name: string;
category: string;
Expand Down
105 changes: 70 additions & 35 deletions src/lib/config/apps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,69 +5,104 @@ export interface AppDef {
features: string[];
icon: string;
theme: 'orange' | 'blue' | 'green' | 'purple';
ctaType: 'demo' | 'visit' | 'download';
ctaType: 'demo' | 'visit' | 'download' | 'coming-soon';
links: {
web?: string;
appStore?: string;
playStore?: string;
chromeStore?: string;
};
description: string;
featured?: boolean;
beta?: boolean;
}

export const APPS: AppDef[] = [
{
name: 'Crew Roster',
slug: 'crew-roster',
oneLiner: 'Digital timesheet for your crew, sorted.',
features: ['Roster scheduling', 'GPS clock-in/out', 'Award compliance'],
icon: 'Timer',
name: 'TaxFlow',
slug: 'taxflowai',
oneLiner: 'ATO tax research, done.',
features: ['Cited ATO answers', 'ATO letter response drafting', 'Firm knowledge base', 'Document generation'],
icon: 'Calculator',
theme: 'orange',
ctaType: 'visit',
links: {
web: 'https://roster.crewcircle.com',
web: 'https://taxflow.crewcircle.com.au',
},
description: 'Replace paper timesheets with digital clock-ins. GPS-tracked shifts, award compliance rules baked in, and automated payroll export. Your crew signs in from the job site, you get the hours sorted automatically at the end of the week.',
description:
'AI research assistant for Australian tax professionals. Ask ATO questions in plain English, get cited answers from public rulings and legislation. Upload an ATO letter for a drafted response, generate firm-branded documents, build a private knowledge base.',
featured: true,
},
{
name: 'Smart GL',
slug: 'smart-gl',
oneLiner: 'AI does the books for your crew.',
features: ['AI categorisation', 'GST/BAS reports', 'Ledger tracking'],
icon: 'BookOpen',
name: 'LocalMate',
slug: 'localmate',
oneLiner: 'Local business automation for Aussie SMBs.',
features: ['AI review replies', 'Local SEO tracking', 'Competitor monitoring', 'Rebooking follow-ups'],
icon: 'Users',
theme: 'blue',
ctaType: 'visit',
links: {
web: '#', // placeholder — no production URL yet
web: 'https://localmate.crewcircle.com.au',
},
description: 'AI-powered bookkeeping that categorises wages, supplier bills, and expenses automatically. Generates GST/BAS reports ready for your accountant. No cloud dependence, your data stays private.',
description:
'One dashboard runs five AI jobs: Review Guard drafts replies to Google and Yelp reviews; Rank Report tracks local SEO weekly; Competitor Watch monitors nearby rivals; Rebook follows up lapsed customers by SMS and email; Menu Sync pushes updates from Google Sheets to Google Business Profile and Square.',
featured: true,
},
{
name: 'Card Snap',
slug: 'card-snap',
oneLiner: 'Snap cards for your crew.',
features: ['ML Kit OCR', 'Contact save', 'Card history'],
icon: 'Camera',
theme: 'green',
ctaType: 'download',
name: 'CrewRoster',
slug: 'crewroster',
oneLiner: 'Rostering and timesheets for your crew.',
features: ['Shift scheduling', 'GPS clock-in/out', 'Payroll export', 'Award compliance'],
icon: 'Timer',
theme: 'orange',
ctaType: 'visit',
links: {
web: '/cardsnap',
appStore: '#ios',
playStore: '#android',
web: 'https://roster.crewcircle.com.au',
},
description: 'Snap a photo of any business card and watch the details land straight in your contacts. Uses on-device ML Kit OCR so nothing leaves your phone. Export to CSV, search history, never lose a contact again.',
description:
'Digital rostering and timesheets for shift-based crews. Schedule shifts, record GPS clock-in and clock-out, export hours for payroll. Built for Aussie cafes, shops, and tradies. Free for up to 5 employees; paid plans scale per employee.',
featured: false,
},
{
name: 'XeroAssist',
slug: 'xero-assist',
oneLiner: 'Xero spanner for your crew.',
features: ['ABN validation', 'BAS tracker', 'ATO rates'],
name: 'SmartGL',
slug: 'smartgl',
oneLiner: 'AI bookkeeping with Australian GST.',
features: ['Bank feed sync', 'AI categorisation', 'Double-entry ledger', 'GST/BAS ready'],
icon: 'BookOpen',
theme: 'blue',
ctaType: 'coming-soon',
links: {},
description:
'Connects to Australian bank feeds, auto-categorises transactions, maintains a double-entry ledger with GST/BAS reporting.',
featured: false,
beta: true,
},
{
name: 'CardSnap',
slug: 'cardsnap',
oneLiner: 'Snap cards, save contacts. Mobile app.',
features: ['On-device OCR', 'Contact save', 'CSV export', 'Search history'],
icon: 'Camera',
theme: 'green',
ctaType: 'coming-soon',
links: {},
description:
'Free business-card scanner with on-device OCR. Snap a card, contacts land in your address book. Export CSV, search history.',
featured: false,
beta: true,
},
{
name: 'AuRate',
slug: 'aurate',
oneLiner: 'ATO admin in one click. Chrome extension.',
features: ['ABN lookup', 'ATO rates', 'BAS reminders', 'Chrome extension'],
icon: 'Wrench',
theme: 'purple',
ctaType: 'download',
links: {
chromeStore: '#chrome',
},
description: 'Chrome extension that gives Xero superpowers. Validate ABNs instantly, track BAS deadlines, pull ATO rates without leaving your browser. Built for bookkeepers who live in Xero every day.',
ctaType: 'coming-soon',
links: {},
description:
'Free Chrome sidekick for sole traders. Validate ABN, check ATO rates, get BAS reminders without leaving your browser.',
featured: false,
beta: true,
},
];
13 changes: 6 additions & 7 deletions src/lib/config/content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,13 @@ export const FOOTER_LINKS = {
{ label: 'Support', href: '/#services' },
],
apps: [
{ label: 'Crew Roster', href: 'https://roster.crewcircle.com' },
{ label: 'Smart GL', href: '#' }, // placeholder — no production URL yet
{ label: 'Card Snap', href: '/cardsnap' },
{ label: 'XeroAssist', href: 'https://xero-assist.crewcircle.com' },
{ label: 'TaxFlowAI', href: 'https://taxflow.crewcircle.com.au' },
{ label: 'LocalMate', href: 'https://localmate.crewcircle.com.au' },
{ label: 'CrewRoster', href: 'https://roster.crewcircle.com.au' },
],
legal: [
{ label: 'Privacy Policy', href: '#' }, // placeholder — no legal pages yet
{ label: 'Terms of Service', href: '#' }, // placeholder — no legal pages yet
{ label: 'Documentation', href: '#' }, // placeholder — no legal pages yet
{ label: 'Privacy Policy', href: '/privacy' },
{ label: 'Terms of Service', href: '/terms' },
{ label: 'Documentation', href: '/documentation' },
],
};
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,5 @@
".next/dev/types/**/*.ts",
"**/*.mts"
],
"exclude": ["node_modules", "backstage-portal", "crewcircle-website"]
"exclude": ["node_modules", "backstage-portal", "crewcircle-website", "packages"]
}
Loading