diff --git a/.claude/commands/pr-review.md b/.claude/commands/pr-review.md new file mode 100644 index 0000000..a950dd2 --- /dev/null +++ b/.claude/commands/pr-review.md @@ -0,0 +1,110 @@ +Review a pull request by gathering all PR metadata, diff, and review comments, then provide a comprehensive review summary. + +## Instructions + +### 1. Identify the PR + +The user may provide a PR URL, number, or nothing: + +- **URL provided** (e.g., `https://github.com/RoboFinSystems/robosystems/pull/577`): Extract the repo and PR number +- **Number provided** (e.g., `577`): Use the current repository +- **Nothing provided**: Detect from the current branch using `gh pr view --json number,url` — if no open PR exists for the current branch, ask the user which PR to review + +### 2. Gather PR Data + +Run these `gh` commands to collect all context: + +```bash +# PR metadata — use only valid fields +gh pr view --json title,body,author,state,labels,reviews,reviewRequests,statusCheckRollup,mergeStateStatus,headRefName,baseRefName,additions,deletions,changedFiles,createdAt,updatedAt + +# PR diff (the actual code changes) +gh pr diff + +# Inline review comments (gh api needs owner/repo — use gh repo view to get it) +gh api repos/$(gh repo view --json nameWithOwner -q .nameWithOwner)/pulls//comments --paginate + +# Top-level PR conversation comments +gh api repos/$(gh repo view --json nameWithOwner -q .nameWithOwner)/issues//comments --paginate +``` + +**Important `gh pr view --json` field reference** (common mistakes to avoid): + +- Use `reviews` not `reviewers` (reviewers is not a valid field) +- Use `reviewRequests` for pending review requests +- Use `headRefOid` for the HEAD commit SHA + +### 3. Categorize Review Feedback + +Organize all comments and checks into categories: + +- **Human Reviews**: Comments from human reviewers (approve, request changes, general feedback) +- **AI Reviews**: Comments from Claude, Copilot, or other AI review bots +- **Code Quality**: Comments from linters, formatters, type checkers (e.g., CodeRabbit, SonarCloud, Codacy) +- **Security**: Findings from security scanners (e.g., Snyk, Dependabot, CodeQL, GitGuardian) +- **CI/CD**: Build status, test results, deployment checks + +### 4. Review the Diff + +With the full PR diff in hand, perform your own review focusing on: + +- **Correctness**: Does the code do what the PR description says? +- **Patterns**: Does it follow existing codebase patterns (check CLAUDE.md)? +- **Security**: Any OWASP top 10 concerns? +- **Multi-tenancy**: Are graph operations scoped to `graph_id`? +- **Error handling**: Appropriate for the context? +- **Tests**: Are changes covered by tests? +- **Missing changes**: Any files that should have been updated but weren't? + +### 5. Output Format + +Provide a structured review: + +``` +## PR Summary +**Title**: ... +**Author**: ... | **Branch**: ... → ... +**Status**: ... | **Changes**: +X / -Y across Z files + + + +## Existing Review Feedback + +### Human Reviews + + +### AI Reviews + + +### Code Quality + + +### Security + + +### CI/CD Status + + +## My Review + +### Issues (should fix before merge) + + +### Suggestions (non-blocking improvements) + + +### Questions + + +## Verdict + +``` + +### Notes + +- If the PR diff is very large (>2000 lines), focus on the most important files and note which files were skimmed +- For security findings, always err on the side of flagging — false positives are better than missed vulnerabilities +- Cross-reference the PR description with the actual diff to catch scope creep or missing implementation +- If the PR references an issue, check that the issue requirements are met + +$ARGUMENTS diff --git a/package-lock.json b/package-lock.json index 3ddf207..7c30780 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "Apache-2.0", "dependencies": { "@aws-sdk/client-sns": "^3.1021.0", - "@robosystems/client": "0.2.42", + "@robosystems/client": "0.2.44", "flowbite": "^3.1", "flowbite-react": "^0.12.5", "intuit-oauth": "^4.1.0", @@ -3219,9 +3219,9 @@ } }, "node_modules/@robosystems/client": { - "version": "0.2.42", - "resolved": "https://registry.npmjs.org/@robosystems/client/-/client-0.2.42.tgz", - "integrity": "sha512-2gUBXvX9/Pp67BzSdkYLu6YmxWLSAOAISYakaCmyiqNX/Xrp6EuwbuthMaIA+FBugD9A/j+LH/CFK8i/AXOUGA==", + "version": "0.2.44", + "resolved": "https://registry.npmjs.org/@robosystems/client/-/client-0.2.44.tgz", + "integrity": "sha512-b9gYvM7Y2lsxguHp5e7lPZqVF4VN1J6u4bpgBAJhwzYPBLxtFGyiX4HGJSkGO5/I9SMWdBKM3JV2eFAfB0ijRA==", "license": "MIT", "bin": { "create-feature": "bin/create-feature.sh" diff --git a/package.json b/package.json index 02926d3..5740f4b 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ }, "dependencies": { "@aws-sdk/client-sns": "^3.1021.0", - "@robosystems/client": "0.2.42", + "@robosystems/client": "0.2.44", "flowbite": "^3.1", "flowbite-react": "^0.12.5", "intuit-oauth": "^4.1.0", diff --git a/src/app/(app)/ledger/schedules/content.tsx b/src/app/(app)/ledger/schedules/content.tsx new file mode 100644 index 0000000..812a146 --- /dev/null +++ b/src/app/(app)/ledger/schedules/content.tsx @@ -0,0 +1,544 @@ +'use client' + +import { PageHeader } from '@/components/PageHeader' +import { + customTheme, + extensions, + GraphFilters, + PageLayout, + useGraphContext, +} from '@/lib/core' +import type { + PeriodCloseStatus, + Schedule, + ScheduleFact, +} from '@robosystems/client/extensions' +import { + Badge, + Button, + Card, + Modal, + ModalBody, + ModalHeader, + Spinner, + Table, + TableBody, + TableCell, + TableHead, + TableHeadCell, + TableRow, +} from 'flowbite-react' +import type { FC } from 'react' +import { useCallback, useEffect, useMemo, useState } from 'react' +import { + HiCalendar, + HiCheck, + HiChevronRight, + HiClock, + HiExclamationCircle, +} from 'react-icons/hi' +import { TbFileInvoice } from 'react-icons/tb' + +const STATUS_COLORS: Record = { + posted: 'success', + draft: 'warning', + pending: 'gray', + reversed: 'failure', +} + +const formatCurrencyDollars = (amount: number): string => { + return new Intl.NumberFormat('en-US', { + style: 'currency', + currency: 'USD', + }).format(amount) +} + +const formatDate = (dateString: string): string => { + const date = new Date(dateString + 'T00:00:00') + return date.toLocaleDateString('en-US', { + year: 'numeric', + month: 'short', + day: 'numeric', + }) +} + +const formatMonth = (dateString: string): string => { + const date = new Date(dateString + 'T00:00:00') + return date.toLocaleDateString('en-US', { + year: 'numeric', + month: 'short', + }) +} + +// ── Period Close Panel ────────────────────────────────────────────────── + +interface PeriodClosePanelProps { + graphId: string + onEntryCreated: () => void +} + +const PeriodClosePanel: FC = ({ + graphId, + onEntryCreated, +}) => { + const [closeStatus, setCloseStatus] = useState(null) + const [isLoading, setIsLoading] = useState(false) + const [error, setError] = useState(null) + const [creatingEntry, setCreatingEntry] = useState(null) + + // Default to current month + const now = new Date() + const defaultStart = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-01` + const lastDay = new Date(now.getFullYear(), now.getMonth() + 1, 0).getDate() + const defaultEnd = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${lastDay}` + + const [periodStart, setPeriodStart] = useState(defaultStart) + const [periodEnd, setPeriodEnd] = useState(defaultEnd) + + const loadCloseStatus = useCallback(async () => { + try { + setIsLoading(true) + setError(null) + const status = await extensions.ledger.getPeriodCloseStatus( + graphId, + periodStart, + periodEnd + ) + setCloseStatus(status) + } catch (err) { + console.error('Error loading close status:', err) + setError('Failed to load period close status.') + } finally { + setIsLoading(false) + } + }, [graphId, periodStart, periodEnd]) + + useEffect(() => { + if (graphId) { + loadCloseStatus() + } + }, [graphId, loadCloseStatus]) + + const handleCreateEntry = async (structureId: string) => { + try { + setCreatingEntry(structureId) + await extensions.ledger.createClosingEntry( + graphId, + structureId, + periodEnd, + periodStart, + periodEnd + ) + onEntryCreated() + await loadCloseStatus() + } catch (err) { + console.error('Error creating closing entry:', err) + setError('Failed to create closing entry.') + } finally { + setCreatingEntry(null) + } + } + + if (isLoading) { + return ( +
+ +
+ ) + } + + if (error) { + return ( +
+ + {error} +
+ ) + } + + if (!closeStatus || closeStatus.schedules.length === 0) { + return ( +
+ No schedules found for this period. +
+ ) + } + + return ( +
+
+
+ + setPeriodStart(e.target.value)} + className="rounded-lg border border-gray-300 bg-gray-50 px-3 py-1.5 text-sm dark:border-gray-600 dark:bg-gray-700 dark:text-white" + /> + to + setPeriodEnd(e.target.value)} + className="rounded-lg border border-gray-300 bg-gray-50 px-3 py-1.5 text-sm dark:border-gray-600 dark:bg-gray-700 dark:text-white" + /> +
+ + {closeStatus.periodStatus} + +
+ + + + Schedule + Amount + Status + + + + {closeStatus.schedules.map((item) => ( + + + {item.structureName} + + {formatCurrencyDollars(item.amount)} + + + {item.status} + + + + {item.status === 'pending' && ( + + )} + {item.status === 'draft' && ( + + + Draft created + + )} + {item.status === 'posted' && ( + + + Posted + + )} + + + ))} + +
+ +
+ Draft: {closeStatus.totalDraft} + Posted: {closeStatus.totalPosted} +
+
+ ) +} + +// ── Schedule Facts Modal ──────────────────────────────────────────────── + +interface FactsModalProps { + graphId: string + schedule: Schedule | null + onClose: () => void +} + +const FactsModal: FC = ({ graphId, schedule, onClose }) => { + const [facts, setFacts] = useState([]) + const [isLoading, setIsLoading] = useState(false) + const [error, setError] = useState(null) + + useEffect(() => { + if (!schedule) return + + const loadFacts = async () => { + try { + setIsLoading(true) + setError(null) + const result = await extensions.ledger.getScheduleFacts( + graphId, + schedule.structureId + ) + setFacts(result) + } catch (err) { + console.error('Error loading facts:', err) + setError('Failed to load schedule facts.') + } finally { + setIsLoading(false) + } + } + + loadFacts() + }, [graphId, schedule]) + + // Group facts by period + const groupedFacts = useMemo(() => { + const groups: Record = {} + for (const fact of facts) { + const key = `${fact.periodStart}_${fact.periodEnd}` + if (!groups[key]) groups[key] = [] + groups[key].push(fact) + } + return Object.entries(groups).sort(([a], [b]) => a.localeCompare(b)) + }, [facts]) + + return ( + + {schedule?.name} - Schedule Facts + + {isLoading ? ( +
+ +
+ ) : error ? ( +
+ + {error} +
+ ) : facts.length === 0 ? ( +

No facts found.

+ ) : ( +
+ + + Period + Element + Amount + + + {groupedFacts.map(([_key, periodFacts]) => + periodFacts.map((fact, idx) => ( + + {idx === 0 && ( + + {formatMonth(fact.periodStart)} + + )} + {fact.elementName} + {formatCurrencyDollars(fact.value)} + + )) + )} + +
+
+ )} +
+
+ ) +} + +// ── Main Content ──────────────────────────────────────────────────────── + +const SchedulesContent: FC = function () { + const { state: graphState } = useGraphContext() + const [schedules, setSchedules] = useState([]) + const [isLoading, setIsLoading] = useState(true) + const [error, setError] = useState(null) + const [selectedSchedule, setSelectedSchedule] = useState( + null + ) + const [showClosePanel, setShowClosePanel] = useState(false) + + const currentGraph = useMemo(() => { + const roboledgerGraphs = graphState.graphs.filter(GraphFilters.roboledger) + return ( + roboledgerGraphs.find((g) => g.graphId === graphState.currentGraphId) ?? + roboledgerGraphs[0] + ) + }, [graphState.graphs, graphState.currentGraphId]) + + const loadSchedules = useCallback(async () => { + if (!currentGraph) { + setSchedules([]) + setIsLoading(false) + return + } + + try { + setIsLoading(true) + setError(null) + const result = await extensions.ledger.listSchedules(currentGraph.graphId) + setSchedules(result) + } catch (err) { + console.error('Error loading schedules:', err) + setError('Failed to load schedules.') + } finally { + setIsLoading(false) + } + }, [currentGraph]) + + useEffect(() => { + loadSchedules() + }, [loadSchedules]) + + return ( + + setShowClosePanel(!showClosePanel)} + > + + {showClosePanel ? 'Hide' : 'Period Close'} + + } + /> + + {error && ( + +
+ + {error} +
+
+ )} + + {showClosePanel && currentGraph && ( + +

+ Period Close Status +

+ +
+ )} + + +
+ {isLoading ? ( +
+ +
+ ) : schedules.length === 0 ? ( +
+ +

+ No Schedules Found +

+

+ Schedules are created via the AI assistant or API. Ask your + assistant to set up a depreciation or amortization schedule. +

+
+ ) : ( + + + Schedule Name + Periods + Entries Created + Progress + + + + {schedules.map((schedule) => { + const progress = + schedule.totalPeriods > 0 + ? Math.round( + (schedule.periodsWithEntries / + schedule.totalPeriods) * + 100 + ) + : 0 + + return ( + + +
+ {schedule.name} + {schedule.scheduleMetadata?.method && ( + + {String( + schedule.scheduleMetadata.method + ).replaceAll('_', ' ')} + + )} +
+
+ {schedule.totalPeriods} months + + {schedule.periodsWithEntries} / {schedule.totalPeriods} + + +
+
+
+
+ + {progress}% + +
+ + + + + + ) + })} + +
+ )} +
+
+ + {currentGraph && ( + setSelectedSchedule(null)} + /> + )} +
+ ) +} + +export default SchedulesContent diff --git a/src/app/(app)/ledger/schedules/page.tsx b/src/app/(app)/ledger/schedules/page.tsx new file mode 100644 index 0000000..2a26ac2 --- /dev/null +++ b/src/app/(app)/ledger/schedules/page.tsx @@ -0,0 +1,15 @@ +'use client' + +import { useUser } from '@/lib/core' +import { Spinner } from '@/lib/core/ui-components' +import SchedulesContent from './content' + +export default function SchedulesPage() { + const { user, isLoading } = useUser() + + if (isLoading || !user) { + return + } + + return +} diff --git a/src/app/(app)/reports/[id]/content.tsx b/src/app/(app)/reports/[id]/content.tsx index 260d080..a9daba4 100644 --- a/src/app/(app)/reports/[id]/content.tsx +++ b/src/app/(app)/reports/[id]/content.tsx @@ -68,7 +68,7 @@ const StatementTable: FC<{ data: StatementData entityName?: string | null }> = ({ data, entityName }) => { - const hasComparative = data.rows.some((r) => r.priorValue !== null) + const periods = data.periods return (
{entityName && ( @@ -81,21 +81,19 @@ const StatementTable: FC<{ {data.structureName} - - {formatDate(data.periodStart)} — {formatDate(data.periodEnd)} - - {hasComparative && ( - - {formatDate(data.comparativePeriodStart)} —{' '} - {formatDate(data.comparativePeriodEnd)} + {periods.map((period, i) => ( + + {period.label || + `${formatDate(period.start)} — ${formatDate(period.end)}`} - )} + ))} {data.rows.map((row: StatementRow, idx: number) => { const indent = row.depth * 24 const isBold = row.isSubtotal - const isZero = row.currentValue === 0 + const primaryValue = row.values[0] ?? 0 + const isZero = primaryValue === 0 return ( {row.elementName} - - {formatCurrency(row.currentValue)} - - {hasComparative && ( + {row.values.map((value, i) => ( - {row.priorValue !== null - ? formatCurrency(row.priorValue) - : '—'} + {value !== null ? formatCurrency(value) : '—'} - )} + ))} ) })} @@ -306,7 +294,7 @@ const ReportViewerContent: FC = function () { diff --git a/src/app/(app)/reports/new/content.tsx b/src/app/(app)/reports/new/content.tsx index ac07864..dc62f4d 100644 --- a/src/app/(app)/reports/new/content.tsx +++ b/src/app/(app)/reports/new/content.tsx @@ -11,6 +11,7 @@ import { import type { MappingCoverage, MappingInfo, + PeriodSpecInput, } from '@robosystems/client/extensions' import { Alert, @@ -34,6 +35,215 @@ import { } from 'react-icons/hi' import { TbReportAnalytics } from 'react-icons/tb' +// ── Period Presets ──────────────────────────────────────────────────────── + +type PresetKey = + | 'this_month' + | 'last_month' + | 'this_quarter' + | 'last_quarter' + | 'monthly_ytd' + | 'monthly_full_year' + | 'annual_comparison' + | 'custom' + +interface PresetOption { + key: PresetKey + label: string + description: string +} + +const PRESETS: PresetOption[] = [ + { + key: 'this_month', + label: 'This Month', + description: 'Current month', + }, + { + key: 'last_month', + label: 'Last Month', + description: 'Prior month with comparison', + }, + { + key: 'this_quarter', + label: 'This Quarter', + description: 'Current quarter', + }, + { + key: 'last_quarter', + label: 'Last Quarter', + description: 'Prior quarter with comparison', + }, + { + key: 'monthly_ytd', + label: 'Monthly YTD', + description: 'Each month this year', + }, + { + key: 'monthly_full_year', + label: 'Monthly (Full Year)', + description: 'Trailing 12 months', + }, + { + key: 'annual_comparison', + label: 'Year over Year', + description: 'This year vs prior year', + }, + { + key: 'custom', + label: 'Custom', + description: 'Set dates manually', + }, +] + +const formatMonthLabel = (year: number, month: number): string => { + const date = new Date(year, month, 1) + return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }) +} + +const lastDayOfMonth = (year: number, month: number): number => + new Date(year, month + 1, 0).getDate() + +const pad = (n: number): string => String(n).padStart(2, '0') + +const getQuarter = (month: number): number => Math.floor(month / 3) + +function buildPresetPeriods( + preset: PresetKey, + now: Date +): { + periodStart: string + periodEnd: string + comparative: boolean + periods: PeriodSpecInput[] | undefined +} { + const year = now.getFullYear() + const month = now.getMonth() // 0-indexed + + switch (preset) { + case 'this_month': { + const start = `${year}-${pad(month + 1)}-01` + const end = `${year}-${pad(month + 1)}-${lastDayOfMonth(year, month)}` + return { + periodStart: start, + periodEnd: end, + comparative: false, + periods: undefined, + } + } + + case 'last_month': { + const prevMonth = month === 0 ? 11 : month - 1 + const prevYear = month === 0 ? year - 1 : year + const start = `${prevYear}-${pad(prevMonth + 1)}-01` + const end = `${prevYear}-${pad(prevMonth + 1)}-${lastDayOfMonth(prevYear, prevMonth)}` + return { + periodStart: start, + periodEnd: end, + comparative: true, + periods: undefined, + } + } + + case 'this_quarter': { + const q = getQuarter(month) + const qStart = q * 3 + const start = `${year}-${pad(qStart + 1)}-01` + const end = `${year}-${pad(qStart + 3)}-${lastDayOfMonth(year, qStart + 2)}` + return { + periodStart: start, + periodEnd: end, + comparative: false, + periods: undefined, + } + } + + case 'last_quarter': { + const q = getQuarter(month) + const prevQ = q === 0 ? 3 : q - 1 + const prevYear = q === 0 ? year - 1 : year + const qStart = prevQ * 3 + const start = `${prevYear}-${pad(qStart + 1)}-01` + const end = `${prevYear}-${pad(qStart + 3)}-${lastDayOfMonth(prevYear, qStart + 2)}` + return { + periodStart: start, + periodEnd: end, + comparative: true, + periods: undefined, + } + } + + case 'monthly_ytd': { + const periods: PeriodSpecInput[] = [] + for (let m = 0; m <= month; m++) { + periods.push({ + start: `${year}-${pad(m + 1)}-01`, + end: `${year}-${pad(m + 1)}-${lastDayOfMonth(year, m)}`, + label: formatMonthLabel(year, m), + }) + } + return { + periodStart: periods[0].start, + periodEnd: periods[periods.length - 1].end, + comparative: false, + periods, + } + } + + case 'monthly_full_year': { + const periods: PeriodSpecInput[] = [] + for (let i = 11; i >= 0; i--) { + const d = new Date(year, month - i, 1) + const y = d.getFullYear() + const m = d.getMonth() + periods.push({ + start: `${y}-${pad(m + 1)}-01`, + end: `${y}-${pad(m + 1)}-${lastDayOfMonth(y, m)}`, + label: formatMonthLabel(y, m), + }) + } + return { + periodStart: periods[0].start, + periodEnd: periods[periods.length - 1].end, + comparative: false, + periods, + } + } + + case 'annual_comparison': { + const periods: PeriodSpecInput[] = [ + { + start: `${year}-01-01`, + end: `${year}-12-31`, + label: `FY ${year}`, + }, + { + start: `${year - 1}-01-01`, + end: `${year - 1}-12-31`, + label: `FY ${year - 1}`, + }, + ] + return { + periodStart: periods[0].start, + periodEnd: periods[0].end, + comparative: false, + periods, + } + } + + case 'custom': + default: + return { + periodStart: '', + periodEnd: '', + comparative: true, + periods: undefined, + } + } +} + +// ── Component ──────────────────────────────────────────────────────────── + const ReportBuilderContent: FC = function () { const router = useRouter() const { state: graphState } = useGraphContext() @@ -43,6 +253,11 @@ const ReportBuilderContent: FC = function () { const [periodStart, setPeriodStart] = useState('') const [periodEnd, setPeriodEnd] = useState('') const [comparative, setComparative] = useState(true) + const [selectedPreset, setSelectedPreset] = + useState('last_quarter') + const [periods, setPeriods] = useState( + undefined + ) // Data state const [mappings, setMappings] = useState([]) @@ -65,6 +280,16 @@ const ReportBuilderContent: FC = function () { ) }, [graphState.graphs, graphState.currentGraphId]) + // Apply preset on mount and when preset changes + useEffect(() => { + if (selectedPreset === 'custom') return + const result = buildPresetPeriods(selectedPreset, new Date()) + setPeriodStart(result.periodStart) + setPeriodEnd(result.periodEnd) + setComparative(result.comparative) + setPeriods(result.periods) + }, [selectedPreset]) + // Load mappings useEffect(() => { const loadMappings = async () => { @@ -160,6 +385,7 @@ const ReportBuilderContent: FC = function () { periodStart, periodEnd, comparative, + periods, }) router.push(`/reports/${report.id}?graph=${currentGraph.graphId}`) @@ -175,12 +401,15 @@ const ReportBuilderContent: FC = function () { periodEnd, reportName, comparative, + periods, router, ]) const isValid = selectedMappingId && periodStart && periodEnd && periodEnd >= periodStart + const isMultiPeriod = periods && periods.length > 0 + return (

- Set the report name, period, and options. The system generates Income - Statement, Balance Sheet, and Cash Flow from the same data. + Set the report name and period. Choose a preset or set custom dates.

-
-
+
+
+ {/* Period Presets */}
- - setPeriodStart(e.target.value)} - /> -
- -
- - setPeriodEnd(e.target.value)} - /> + +
+ {PRESETS.map((preset) => ( + + ))} +
-
- -
+ {/* Period Summary / Custom Dates */} + {selectedPreset === 'custom' ? ( +
+
+ + { + setPeriodStart(e.target.value) + setPeriods(undefined) + }} + /> +
+
+ + { + setPeriodEnd(e.target.value) + setPeriods(undefined) + }} + /> +
+
+ +
+
+ ) : periodStart && periodEnd ? ( +
+ {isMultiPeriod ? ( +
+
+ {periods.length} period{periods.length !== 1 ? 's' : ''} +
+
+ {periods.map((p, i) => ( + + {p.label} + + ))} +
+
+ ) : ( +
+ Period:{' '} + {periodStart} to {periodEnd} + {comparative && ( + + + prior period comparison + + )} +
+ )} +
+ ) : null}
@@ -366,7 +651,7 @@ const ReportBuilderContent: FC = function () { 3. Generate

- Creates facts for all mapped elements and renders all financial + Creates facts for all mapped elements and renders financial statements.

diff --git a/src/app/(app)/sidebar-config.tsx b/src/app/(app)/sidebar-config.tsx index 2c3f146..41e6190 100644 --- a/src/app/(app)/sidebar-config.tsx +++ b/src/app/(app)/sidebar-config.tsx @@ -43,6 +43,7 @@ export const getNavigationItems = ( { href: '/ledger/chart-of-accounts', label: 'Chart of Accounts' }, { href: '/ledger/transactions', label: 'Transactions' }, { href: '/ledger/trial-balance', label: 'Trial Balance' }, + { href: '/ledger/schedules', label: 'Schedules' }, ], }, { diff --git a/src/lib/core/index.ts b/src/lib/core/index.ts index 27a09bc..153a97b 100644 --- a/src/lib/core/index.ts +++ b/src/lib/core/index.ts @@ -203,8 +203,6 @@ export { EventType, executeQuery, extensions, - FileClient, - MaterializationClient, monitorOperation, OperationClient, QueryClient, @@ -212,17 +210,11 @@ export { RoboSystemsExtensions, SSEClient, streamQuery, - TableClient, useMultipleOperations, useOperation, useQuery, useSDKClients, useStreamingQuery, - useTableUpload, - type FileUploadOptions, - type FileUploadResult, - type MaterializationOptions, - type MaterializationResult, type OperationMonitorOptions, type OperationProgress, type OperationResult, @@ -233,7 +225,6 @@ export { type RoboSystemsExtensionConfig, type SSEConfig, type SSEEvent, - type TableInfo, } from '@robosystems/client/extensions' // Export task monitoring utilities