@accounter/client 0.0.7-alpha-20250821154938-954b3cb72dbd3ab35c2f363f90aa079483f72196 → 0.0.7-alpha-20250821161622-fcb54c5f6524707e4544fc94066cd6ec87cbba51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,121 +0,0 @@
1
- import { useEffect, useMemo, useState } from 'react';
2
- import { AlertTriangle, Settings } from 'lucide-react';
3
- import { useQuery } from 'urql';
4
- import { ChargeSortByField, LedgerValidationStatusDocument } from '../../../../../gql/graphql.js';
5
- import type { TimelessDateString } from '../../../../../helpers/index.js';
6
- import { Badge } from '../../../../ui/badge.jsx';
7
- import { CardContent } from '../../../../ui/card.jsx';
8
- import { getAllChargesHref } from '../../../charges/all-charges.jsx';
9
- import {
10
- BaseStepCard,
11
- type BaseStepProps,
12
- type StepAction,
13
- type StepStatus,
14
- } from '../step-base.jsx';
15
-
16
- // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- used by codegen
17
- /* GraphQL */ `
18
- query LedgerValidationStatus($limit: Int, $filters: ChargeFilter) {
19
- chargesWithLedgerChanges(limit: $limit, filters: $filters) {
20
- charge {
21
- id
22
- }
23
- }
24
- }
25
- `;
26
-
27
- interface Step02Props extends BaseStepProps {
28
- year: number;
29
- adminBusinessId?: string;
30
- }
31
-
32
- export function Step02LedgerChanges(props: Step02Props) {
33
- const [status, setStatus] = useState<StepStatus>('blocked');
34
- const [pendingChanges, setPendingChanges] = useState<number>(Infinity);
35
-
36
- const [{ data, fetching }, fetchStatus] = useQuery({
37
- query: LedgerValidationStatusDocument,
38
- variables: {
39
- filters: {
40
- byOwners: props.adminBusinessId ? [props.adminBusinessId] : [],
41
- fromAnyDate: `${props.year}-01-01` as TimelessDateString,
42
- toAnyDate: `${props.year}-12-31` as TimelessDateString,
43
- },
44
- },
45
- pause: true,
46
- });
47
-
48
- useEffect(() => {
49
- if (!data && !fetching && props.adminBusinessId) {
50
- fetchStatus();
51
- }
52
- });
53
-
54
- useEffect(() => {
55
- if (fetching) setStatus('loading');
56
- }, [fetching]);
57
-
58
- // Report status changes to parent
59
- useEffect(() => {
60
- if (props.onStatusChange) {
61
- props.onStatusChange(props.id, status);
62
- }
63
- }, [status, props.onStatusChange, props.id]);
64
-
65
- useEffect(() => {
66
- if (data?.chargesWithLedgerChanges) {
67
- const pendingChanges = data.chargesWithLedgerChanges.filter(
68
- charge => !!charge.charge?.id,
69
- ).length;
70
- setPendingChanges(pendingChanges);
71
-
72
- if (pendingChanges === 0) {
73
- setStatus('completed');
74
- } else {
75
- setStatus('in-progress');
76
- }
77
- }
78
- }, [data]);
79
-
80
- const href = useMemo(() => {
81
- return getAllChargesHref({
82
- byOwners: props.adminBusinessId ? [props.adminBusinessId] : undefined,
83
- fromAnyDate: `${props.year}-01-01` as TimelessDateString,
84
- toAnyDate: `${props.year}-12-31` as TimelessDateString,
85
- sortBy: {
86
- field: ChargeSortByField.Date,
87
- asc: false,
88
- },
89
- });
90
- }, [props.adminBusinessId, props.year]);
91
-
92
- const actions: StepAction[] = [{ label: 'View Ledger Status', href }];
93
-
94
- return (
95
- <BaseStepCard
96
- {...props}
97
- status={status}
98
- icon={<Settings className="h-4 w-4" />}
99
- actions={actions}
100
- >
101
- {pendingChanges > 0 && (
102
- <CardContent className="pt-0 border-t">
103
- <div className="flex items-center gap-2 p-3 bg-red-50 rounded-lg border border-red-200">
104
- <AlertTriangle className="h-4 w-4 text-red-600" />
105
- <div className="flex-1">
106
- <span className="text-sm text-red-800 font-medium">
107
- {pendingChanges} pending ledger changes detected
108
- </span>
109
- {/* <div className="text-xs text-red-600 mt-1">
110
- Last updated: {new Date(ledgerStatus.lastUpdate).toLocaleString()}
111
- </div> */}
112
- </div>
113
- <Badge variant="destructive" className="text-xs">
114
- Action Required
115
- </Badge>
116
- </div>
117
- </CardContent>
118
- )}
119
- </BaseStepCard>
120
- );
121
- }
@@ -1,171 +0,0 @@
1
- 'use client';
2
-
3
- import { useEffect, useState } from 'react';
4
- import { Calculator } from 'lucide-react';
5
- import type { TimelessDateString } from '../../../../../helpers/dates.js';
6
- import { BalanceChargeModal } from '../../../../common/modals/balance-charge-modal.jsx';
7
- import { getContoReportHref } from '../../../../reports/conto/index.jsx';
8
- import { getTrialBalanceReportHref } from '../../../../reports/trial-balance-report/index.jsx';
9
- import { Collapsible, CollapsibleContent } from '../../../../ui/collapsible.js';
10
- import { BaseStepCard, type BaseStepProps, type StepStatus } from '../step-base.jsx';
11
-
12
- interface UserType {
13
- type: 'new' | 'migrating' | 'continuing';
14
- balanceStatus?: 'verified' | 'pending' | 'missing';
15
- }
16
-
17
- interface Step03Props extends BaseStepProps {
18
- year: number;
19
- adminBusinessId?: string;
20
- }
21
-
22
- // Sub-step components
23
- function SubStep3A({
24
- level,
25
- year,
26
- adminBusinessId,
27
- disabled,
28
- }: {
29
- level: number;
30
- year: number;
31
- adminBusinessId: string;
32
- disabled: boolean;
33
- }) {
34
- const contoHref = getContoReportHref({
35
- fromDate: `${year - 1}-01-01` as TimelessDateString,
36
- toDate: `${year - 1}-12-31` as TimelessDateString,
37
- ownerIds: [adminBusinessId],
38
- });
39
- const [balanceChargeModalOpen, setBalanceChargeModalOpen] = useState(false);
40
-
41
- return (
42
- <>
43
- <BaseStepCard
44
- id="3a"
45
- title="Migrating Users"
46
- description="Create balance charge and dynamic report"
47
- status="pending"
48
- level={level}
49
- actions={[
50
- { label: 'Create Balance Charge', onClick: () => setBalanceChargeModalOpen(true) },
51
- { label: 'Generate Dynamic Report', href: contoHref },
52
- // { label: 'Upload Conto 331 Report', href: '/upload/conto331' },
53
- ]}
54
- disabled={disabled}
55
- />
56
- <BalanceChargeModal
57
- open={balanceChargeModalOpen}
58
- onOpenChange={setBalanceChargeModalOpen}
59
- onClose={() => setBalanceChargeModalOpen(false)}
60
- />
61
- </>
62
- );
63
- }
64
-
65
- function SubStep3B({
66
- level,
67
- year,
68
- adminBusinessId,
69
- disabled,
70
- }: {
71
- level: number;
72
- year: number;
73
- adminBusinessId: string;
74
- disabled: boolean;
75
- }) {
76
- const href = getTrialBalanceReportHref({
77
- toDate: `${year - 1}-12-31` as TimelessDateString,
78
- ownerIds: [adminBusinessId],
79
- });
80
- return (
81
- <BaseStepCard
82
- id="3b"
83
- title="Continuing Users"
84
- description="Compare with previous year final trial balance"
85
- status="pending"
86
- level={level}
87
- actions={[{ label: 'View Previous Year Ending Balance', href }]}
88
- disabled={disabled}
89
- />
90
- );
91
- }
92
-
93
- export default function Step03OpeningBalance(props: Step03Props) {
94
- const [status, setStatus] = useState<StepStatus>('loading');
95
- const [userType, setUserType] = useState<UserType | null>(null);
96
- const [isExpanded, setIsExpanded] = useState(false);
97
-
98
- useEffect(() => {
99
- if (!props.adminBusinessId) {
100
- setStatus('blocked');
101
- }
102
- }, [props.adminBusinessId]);
103
-
104
- // Report status changes to parent
105
- useEffect(() => {
106
- if (props.onStatusChange) {
107
- props.onStatusChange(props.id, status);
108
- }
109
- }, [status, props.onStatusChange, props.id]);
110
-
111
- useEffect(() => {
112
- const fetchUserType = async () => {
113
- try {
114
- // Simulate API call to determine user type
115
- await new Promise(resolve => setTimeout(resolve, 600));
116
-
117
- const data: UserType = {
118
- type: 'continuing',
119
- balanceStatus: 'pending',
120
- };
121
-
122
- setUserType(data);
123
-
124
- if (data.type === 'new') {
125
- setStatus('completed');
126
- } else if (data.balanceStatus === 'verified') {
127
- setStatus('completed');
128
- } else {
129
- setStatus('pending');
130
- }
131
- } catch (error) {
132
- console.error('Error fetching user type:', error);
133
- setStatus('blocked');
134
- }
135
- };
136
-
137
- fetchUserType();
138
- }, []);
139
-
140
- return (
141
- <>
142
- <BaseStepCard
143
- {...props}
144
- status={status}
145
- icon={<Calculator className="h-4 w-4" />}
146
- hasSubsteps
147
- isExpanded={isExpanded}
148
- onToggleExpanded={() => setIsExpanded(!isExpanded)}
149
- />
150
-
151
- {props.adminBusinessId && (
152
- <Collapsible open={isExpanded}>
153
- <CollapsibleContent className="space-y-2">
154
- <SubStep3A
155
- level={1}
156
- year={props.year}
157
- adminBusinessId={props.adminBusinessId}
158
- disabled={userType?.type !== 'migrating'}
159
- />
160
- <SubStep3B
161
- level={1}
162
- year={props.year}
163
- adminBusinessId={props.adminBusinessId}
164
- disabled={userType?.type !== 'continuing'}
165
- />
166
- </CollapsibleContent>
167
- </Collapsible>
168
- )}
169
- </>
170
- );
171
- }
@@ -1,149 +0,0 @@
1
- 'use client';
2
-
3
- import type { ReactNode } from 'react';
4
- import { AlertCircle, CheckCircle2, ChevronDown, ChevronRight, Clock, Loader2 } from 'lucide-react';
5
- import { Badge } from '../../../ui/badge.jsx';
6
- import { Button } from '../../../ui/button.jsx';
7
- import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../../../ui/card.jsx';
8
-
9
- export type StepStatus = 'completed' | 'in-progress' | 'pending' | 'blocked' | 'loading';
10
-
11
- export interface BaseStepProps {
12
- id: string;
13
- title: string;
14
- description?: string;
15
- icon?: ReactNode;
16
- level?: number;
17
- onStatusChange?: (stepId: string, status: StepStatus) => void;
18
- }
19
-
20
- export interface StepAction {
21
- label: string;
22
- href?: string;
23
- onClick?: () => void;
24
- }
25
-
26
- export const getStatusIcon = (status: StepStatus) => {
27
- switch (status) {
28
- case 'completed':
29
- return <CheckCircle2 className="h-5 w-5 text-green-600" />;
30
- case 'in-progress':
31
- return <Clock className="h-5 w-5 text-blue-600" />;
32
- case 'pending':
33
- return <Clock className="h-5 w-5 text-gray-400" />;
34
- case 'blocked':
35
- return <AlertCircle className="h-5 w-5 text-red-600" />;
36
- case 'loading':
37
- return <Loader2 className="h-5 w-5 text-gray-400 animate-spin" />;
38
- }
39
- };
40
-
41
- export const getStatusBadge = (status: StepStatus) => {
42
- const variants = {
43
- completed: 'default',
44
- 'in-progress': 'secondary',
45
- pending: 'outline',
46
- blocked: 'destructive',
47
- loading: 'outline',
48
- } as const;
49
-
50
- const labels = {
51
- completed: 'Completed',
52
- 'in-progress': 'In Progress',
53
- pending: 'Pending',
54
- blocked: 'Blocked',
55
- loading: 'Loading...',
56
- };
57
-
58
- return <Badge variant={variants[status]}>{labels[status]}</Badge>;
59
- };
60
-
61
- interface BaseStepCardProps extends BaseStepProps {
62
- status: StepStatus;
63
- actions?: StepAction[];
64
- children?: ReactNode;
65
- hasSubsteps?: boolean;
66
- isExpanded?: boolean;
67
- onToggleExpanded?: () => void;
68
- disabled?: boolean;
69
- }
70
-
71
- export function BaseStepCard({
72
- id,
73
- title,
74
- description,
75
- icon,
76
- status,
77
- actions,
78
- children,
79
- hasSubsteps = false,
80
- isExpanded = false,
81
- onToggleExpanded,
82
- level = 0,
83
- disabled = false,
84
- }: BaseStepCardProps) {
85
- return (
86
- <div className={`${level > 0 ? 'ml-6 border-l-2 border-gray-200 pl-4' : ''} relative`}>
87
- <Card className="mb-4">
88
- <CardHeader className="pb-3">
89
- <div className="flex items-center justify-between">
90
- <div className="flex items-center gap-3">
91
- {hasSubsteps ? (
92
- <Button
93
- disabled={disabled}
94
- variant="ghost"
95
- size="sm"
96
- onClick={onToggleExpanded}
97
- className="p-0 h-auto"
98
- >
99
- {isExpanded ? (
100
- <ChevronDown className="h-4 w-4" />
101
- ) : (
102
- <ChevronRight className="h-4 w-4" />
103
- )}
104
- </Button>
105
- ) : (
106
- <div className="w-4" />
107
- )}
108
- {getStatusIcon(status)}
109
- {icon}
110
- <div>
111
- <CardTitle className="text-lg">
112
- {id}. {title}
113
- </CardTitle>
114
- {description && <CardDescription className="mt-1">{description}</CardDescription>}
115
- </div>
116
- </div>
117
- {getStatusBadge(status)}
118
- </div>
119
- </CardHeader>
120
- {actions && actions.length > 0 && (
121
- <CardContent className="pt-0">
122
- <div className="flex flex-wrap gap-2">
123
- {actions.map((action, index) => (
124
- <Button
125
- key={index}
126
- variant="outline"
127
- size="sm"
128
- disabled={disabled}
129
- onClick={action.onClick}
130
- asChild={!!action.href && !disabled}
131
- >
132
- {action.href ? <a href={action.href}>{action.label}</a> : action.label}
133
- </Button>
134
- ))}
135
- </div>
136
- </CardContent>
137
- )}
138
- {children}
139
- </Card>
140
-
141
- {/* disabled overlay */}
142
- {disabled && (
143
- <Card
144
- className={`absolute inset-0 bg-gray-500 opacity-50 pointer-events-auto z-10 ${level > 0 ? 'ml-4' : ''}`}
145
- />
146
- )}
147
- </div>
148
- );
149
- }
@@ -1,43 +0,0 @@
1
- import { useEffect, useState } from 'react';
2
- import {
3
- BaseStepCard,
4
- type BaseStepProps,
5
- type StepAction,
6
- type StepStatus,
7
- } from './step-base.jsx';
8
-
9
- interface SimpleStepProps extends BaseStepProps {
10
- defaultStatus?: StepStatus;
11
- actions?: StepAction[];
12
- fetchStatus?: () => Promise<StepStatus>;
13
- onStatusChange?: (id: string, status: StepStatus) => void;
14
- }
15
-
16
- export default function SimpleStep({
17
- defaultStatus = 'pending',
18
- actions = [],
19
- fetchStatus,
20
- onStatusChange,
21
- ...props
22
- }: SimpleStepProps) {
23
- const [status, setStatus] = useState<StepStatus>(fetchStatus ? 'loading' : defaultStatus);
24
-
25
- // Report status changes to parent
26
- useEffect(() => {
27
- if (onStatusChange) {
28
- onStatusChange(props.id, status);
29
- }
30
- }, [status, onStatusChange, props.id]);
31
-
32
- useEffect(() => {
33
- if (fetchStatus) {
34
- fetchStatus()
35
- .then(setStatus)
36
- .catch(() => setStatus('blocked'));
37
- }
38
- }, [fetchStatus]);
39
-
40
- return (
41
- <BaseStepCard {...props} status={status} actions={actions} onStatusChange={onStatusChange} />
42
- );
43
- }
@@ -1,28 +0,0 @@
1
- import React from 'react';
2
- import { cn } from '@/lib/utils.js';
3
- import * as ProgressPrimitive from '@radix-ui/react-progress';
4
-
5
- function Progress({
6
- className,
7
- value,
8
- ...props
9
- }: React.ComponentProps<typeof ProgressPrimitive.Root>) {
10
- return (
11
- <ProgressPrimitive.Root
12
- data-slot="progress"
13
- className={cn(
14
- 'bg-gray-900/20 relative h-2 w-full overflow-hidden rounded-full dark:bg-gray-50/20',
15
- className,
16
- )}
17
- {...props}
18
- >
19
- <ProgressPrimitive.Indicator
20
- data-slot="progress-indicator"
21
- className="bg-gray-900 h-full w-full flex-1 transition-all dark:bg-gray-50"
22
- style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
23
- />
24
- </ProgressPrimitive.Root>
25
- );
26
- }
27
-
28
- export { Progress };
@@ -1,57 +0,0 @@
1
- import { useMemo } from 'react';
2
- import { toast } from 'sonner';
3
- import { useQuery } from 'urql';
4
- import { AdminBusinessesDocument, type AdminBusinessesQuery } from '../gql/graphql.js';
5
-
6
- // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- used by codegen
7
- /* GraphQL */ `
8
- query AdminBusinesses {
9
- allAdminBusinesses {
10
- id
11
- name
12
- governmentId
13
- }
14
- }
15
- `;
16
-
17
- export type AdminBusinesses = Array<
18
- NonNullable<AdminBusinessesQuery['allAdminBusinesses']>[number]
19
- >;
20
-
21
- type UseGetAdminBusinesses = {
22
- fetching: boolean;
23
- refresh: () => void;
24
- adminBusinesses: AdminBusinesses;
25
- selectableAdminBusinesses: Array<{ value: string; label: string }>;
26
- };
27
-
28
- export const useGetAdminBusinesses = (): UseGetAdminBusinesses => {
29
- const [{ data, fetching, error }, fetch] = useQuery({
30
- query: AdminBusinessesDocument,
31
- });
32
-
33
- if (error) {
34
- console.error(`Error fetching admin businesses: ${error}`);
35
- toast.error('Error', {
36
- description: 'Unable to fetch admin businesses',
37
- });
38
- }
39
-
40
- const adminBusinesses = useMemo(() => {
41
- return data?.allAdminBusinesses?.slice().sort((a, b) => (a.name > b.name ? 1 : -1)) ?? [];
42
- }, [data]);
43
-
44
- const selectableAdminBusinesses = useMemo(() => {
45
- return adminBusinesses.map(entity => ({
46
- value: entity.id,
47
- label: entity.name,
48
- }));
49
- }, [adminBusinesses]);
50
-
51
- return {
52
- fetching,
53
- refresh: () => fetch(),
54
- adminBusinesses,
55
- selectableAdminBusinesses,
56
- };
57
- };