@accounter/client 0.0.8-alpha-20251021150615-800574fc6d416cd319de216c97b431643d8958a2 → 0.0.8-alpha-20251021163440-2ab1a9ffaec95fd99fac5495c3a392b97429ce77
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.
- package/CHANGELOG.md +43 -1
- package/dist/assets/index-B2UYAO1O.css +1 -0
- package/dist/assets/index-BexxGuN6.js +1224 -0
- package/dist/assets/{index.es-DHwHzag1.js → index.es-CWwhWGxX.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +6 -5
- package/src/app.tsx +35 -25
- package/src/components/business/business-header.tsx +68 -0
- package/src/components/business/charges-section.tsx +82 -0
- package/src/components/business/charts-section.tsx +115 -0
- package/src/components/business/configurations-section.tsx +885 -0
- package/src/components/business/contact-info-section.tsx +536 -0
- package/src/components/business/contracts-section.tsx +196 -0
- package/src/components/business/documents-section.tsx +26 -0
- package/src/components/business/index.tsx +171 -0
- package/src/components/business/integrations-section.tsx +477 -0
- package/src/components/business/transactions-section.tsx +26 -0
- package/src/components/business-transactions/business-extended-info.tsx +11 -15
- package/src/components/business-transactions/business-transactions-single.tsx +1 -1
- package/src/components/business-transactions/index.tsx +1 -1
- package/src/components/charges/charge-extended-info-menu.tsx +27 -21
- package/src/components/charges/charges-row.tsx +12 -10
- package/src/components/charges/charges-table.tsx +15 -9
- package/src/components/clients/contracts/modify-contract-dialog.tsx +464 -0
- package/src/components/clients/modify-client-dialog.tsx +276 -0
- package/src/components/common/documents/issue-document/index.tsx +3 -3
- package/src/components/common/documents/issue-document/{recent-client-docs.tsx → recent-business-docs.tsx} +19 -13
- package/src/components/common/forms/business-card.tsx +1 -0
- package/src/components/common/forms/modify-business-fields.tsx +2 -19
- package/src/components/common/inputs/combo-box.tsx +1 -1
- package/src/components/layout/sidelinks.tsx +3 -3
- package/src/components/reports/trial-balance-report/trial-balance-report-group.tsx +4 -6
- package/src/components/reports/trial-balance-report/trial-balance-report-sort-code.tsx +8 -11
- package/src/components/screens/businesses/business.tsx +44 -0
- package/src/components/screens/documents/issue-documents/edit-issue-document-modal.tsx +4 -4
- package/src/components/ui/progress.tsx +25 -0
- package/src/components/ui/skeleton.tsx +12 -0
- package/src/gql/gql.ts +93 -9
- package/src/gql/graphql.ts +289 -9
- package/src/helpers/contracts.ts +22 -0
- package/src/helpers/currency.ts +5 -0
- package/src/helpers/index.ts +2 -0
- package/src/helpers/pcn874.ts +17 -0
- package/src/hooks/use-add-sort-code.ts +1 -1
- package/src/hooks/use-add-tag.ts +1 -1
- package/src/hooks/use-create-contract.ts +62 -0
- package/src/hooks/use-delete-contract.ts +64 -0
- package/src/hooks/use-delete-tag.ts +1 -1
- package/src/hooks/use-get-all-contracts.ts +0 -1
- package/src/hooks/use-insert-client.ts +80 -0
- package/src/hooks/use-merge-businesses.ts +1 -1
- package/src/hooks/use-merge-charges.ts +1 -1
- package/src/hooks/use-update-client.ts +75 -0
- package/src/hooks/use-update-contract.ts +69 -0
- package/dist/assets/index-0eCf1BcD.css +0 -1
- package/dist/assets/index-DHTbHvtz.js +0 -1188
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { Calendar, ExternalLink, LinkIcon, Pencil } from 'lucide-react';
|
|
3
|
+
import { useQuery } from 'urql';
|
|
4
|
+
import { Badge } from '@/components/ui/badge.js';
|
|
5
|
+
import { Button } from '@/components/ui/button.js';
|
|
6
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card.js';
|
|
7
|
+
import { ClientContractsSectionDocument, type ClientContractsSectionQuery } from '@/gql/graphql.js';
|
|
8
|
+
import {
|
|
9
|
+
formatAmountWithCurrency,
|
|
10
|
+
getDocumentNameFromType,
|
|
11
|
+
standardBillingCycle,
|
|
12
|
+
standardPlan,
|
|
13
|
+
} from '@/helpers/index.js';
|
|
14
|
+
import {
|
|
15
|
+
ModifyContractDialog,
|
|
16
|
+
type ContractFormValues,
|
|
17
|
+
} from '../clients/contracts/modify-contract-dialog.js';
|
|
18
|
+
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions -- used by codegen
|
|
20
|
+
/* GraphQL */ `
|
|
21
|
+
query ClientContractsSection($clientId: UUID!) {
|
|
22
|
+
contractsByClient(clientId: $clientId) {
|
|
23
|
+
id
|
|
24
|
+
purchaseOrder
|
|
25
|
+
startDate
|
|
26
|
+
endDate
|
|
27
|
+
amount {
|
|
28
|
+
raw
|
|
29
|
+
currency
|
|
30
|
+
}
|
|
31
|
+
billingCycle
|
|
32
|
+
isActive
|
|
33
|
+
product
|
|
34
|
+
documentType
|
|
35
|
+
remarks
|
|
36
|
+
plan
|
|
37
|
+
msCloud
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
`;
|
|
41
|
+
|
|
42
|
+
function convertContractDataToFormValues(
|
|
43
|
+
contract: ClientContractsSectionQuery['contractsByClient'][number],
|
|
44
|
+
): ContractFormValues {
|
|
45
|
+
return {
|
|
46
|
+
id: contract.id,
|
|
47
|
+
// TODO: activate this field later. requires additional backend support
|
|
48
|
+
// operationsLimit: 0, // Placeholder, as this field is not in the query
|
|
49
|
+
po: contract.purchaseOrder ?? undefined,
|
|
50
|
+
startDate: contract.startDate,
|
|
51
|
+
endDate: contract.endDate,
|
|
52
|
+
paymentAmount: contract.amount.raw,
|
|
53
|
+
paymentCurrency: contract.amount.currency,
|
|
54
|
+
productType: contract.product ?? undefined,
|
|
55
|
+
billingCycle: contract.billingCycle,
|
|
56
|
+
msCloudLink: contract.msCloud?.toString(),
|
|
57
|
+
isActive: contract.isActive,
|
|
58
|
+
subscriptionPlan: contract.plan ?? undefined,
|
|
59
|
+
defaultRemark: contract.remarks ?? undefined,
|
|
60
|
+
defaultDocumentType: contract.documentType ?? undefined,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
interface Props {
|
|
65
|
+
clientId: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function ContractsSection({ clientId }: Props) {
|
|
69
|
+
const [editingContract, setEditingContract] = useState<ContractFormValues | null>(null);
|
|
70
|
+
|
|
71
|
+
const [{ data, fetching }, refetch] = useQuery({
|
|
72
|
+
query: ClientContractsSectionDocument,
|
|
73
|
+
variables: {
|
|
74
|
+
clientId,
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const contracts = data?.contractsByClient ?? [];
|
|
79
|
+
|
|
80
|
+
if (fetching) {
|
|
81
|
+
return <div>Loading contracts...</div>;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<Card>
|
|
86
|
+
<CardHeader>
|
|
87
|
+
<div className="flex items-center justify-between">
|
|
88
|
+
<div>
|
|
89
|
+
<CardTitle>Contracts</CardTitle>
|
|
90
|
+
<CardDescription>Current and past contracts sorted by start date</CardDescription>
|
|
91
|
+
</div>
|
|
92
|
+
<ModifyContractDialog clientId={clientId} contract={editingContract} onDone={refetch} />
|
|
93
|
+
</div>
|
|
94
|
+
</CardHeader>
|
|
95
|
+
<CardContent className="space-y-4">
|
|
96
|
+
{contracts.map(contract => {
|
|
97
|
+
const isActive = !!contract.isActive;
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<div key={contract.id} className="rounded-lg border p-4 space-y-4">
|
|
101
|
+
<div className="flex items-start justify-between">
|
|
102
|
+
<div className="space-y-1">
|
|
103
|
+
<div className="flex items-center gap-2">
|
|
104
|
+
<h4 className="font-semibold">{contract.product}</h4>
|
|
105
|
+
<Badge variant={isActive ? 'default' : 'secondary'}>
|
|
106
|
+
{contract.isActive ? 'Active' : 'Inactive'}
|
|
107
|
+
</Badge>
|
|
108
|
+
</div>
|
|
109
|
+
<p className="text-sm text-muted-foreground font-mono">{contract.id}</p>
|
|
110
|
+
</div>
|
|
111
|
+
<Button
|
|
112
|
+
variant="ghost"
|
|
113
|
+
size="sm"
|
|
114
|
+
onClick={() => setEditingContract(convertContractDataToFormValues(contract))}
|
|
115
|
+
>
|
|
116
|
+
<Pencil className="h-4 w-4" />
|
|
117
|
+
</Button>
|
|
118
|
+
</div>
|
|
119
|
+
|
|
120
|
+
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
121
|
+
<div className="space-y-0">
|
|
122
|
+
<p className="text-sm text-muted-foreground flex items-center gap-1">
|
|
123
|
+
<Calendar className="h-3 w-3" />
|
|
124
|
+
Contract Period
|
|
125
|
+
</p>
|
|
126
|
+
<p className="text-sm font-medium">
|
|
127
|
+
{new Date(contract.startDate).toLocaleDateString()} →{' '}
|
|
128
|
+
{new Date(contract.endDate).toLocaleDateString()}
|
|
129
|
+
</p>
|
|
130
|
+
</div>
|
|
131
|
+
|
|
132
|
+
<div className="space-y-0">
|
|
133
|
+
<p className="text-sm text-muted-foreground">PO Number</p>
|
|
134
|
+
<p className="text-sm font-medium font-mono">{contract.purchaseOrder}</p>
|
|
135
|
+
</div>
|
|
136
|
+
|
|
137
|
+
{/* TODO: activate this field later. requires additional backend support */}
|
|
138
|
+
{/* <div className="space-y-0">
|
|
139
|
+
<p className="text-sm text-muted-foreground">Operations Limit</p>
|
|
140
|
+
<p className="text-sm font-medium">{contract.operationsLimit.toLocaleString()}</p>
|
|
141
|
+
</div> */}
|
|
142
|
+
|
|
143
|
+
<div className="space-y-0">
|
|
144
|
+
<p className="text-sm text-muted-foreground">Payment</p>
|
|
145
|
+
<p className="text-sm font-medium">
|
|
146
|
+
{formatAmountWithCurrency(contract.amount.raw, contract.amount.currency)}
|
|
147
|
+
</p>
|
|
148
|
+
</div>
|
|
149
|
+
|
|
150
|
+
<div className="space-y-0">
|
|
151
|
+
<p className="text-sm text-muted-foreground">Billing Cycle</p>
|
|
152
|
+
<p className="text-sm font-medium">
|
|
153
|
+
{standardBillingCycle(contract.billingCycle)}
|
|
154
|
+
</p>
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
<div className="space-y-0">
|
|
158
|
+
<p className="text-sm text-muted-foreground">Subscription Plan</p>
|
|
159
|
+
<p className="text-sm font-medium">
|
|
160
|
+
{contract.plan ? standardPlan(contract.plan) : 'N/A'}
|
|
161
|
+
</p>
|
|
162
|
+
</div>
|
|
163
|
+
|
|
164
|
+
<div className="space-y-0">
|
|
165
|
+
<p className="text-sm text-muted-foreground">Document Type</p>
|
|
166
|
+
<p className="text-sm font-medium">
|
|
167
|
+
{getDocumentNameFromType(contract.documentType)}
|
|
168
|
+
</p>
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
|
|
172
|
+
{contract.remarks && (
|
|
173
|
+
<div className="space-y-0">
|
|
174
|
+
<p className="text-sm text-muted-foreground">Remark</p>
|
|
175
|
+
<p className="text-sm">{contract.remarks}</p>
|
|
176
|
+
</div>
|
|
177
|
+
)}
|
|
178
|
+
|
|
179
|
+
<div className="flex flex-wrap gap-2">
|
|
180
|
+
{contract.msCloud && (
|
|
181
|
+
<Button variant="outline" size="sm" asChild>
|
|
182
|
+
<a href={contract.msCloud.toString()} target="_blank" rel="noopener noreferrer">
|
|
183
|
+
<LinkIcon className="h-4 w-4 mr-2" />
|
|
184
|
+
MS Cloud
|
|
185
|
+
<ExternalLink className="h-3 w-3 ml-1" />
|
|
186
|
+
</a>
|
|
187
|
+
</Button>
|
|
188
|
+
)}
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
);
|
|
192
|
+
})}
|
|
193
|
+
</CardContent>
|
|
194
|
+
</Card>
|
|
195
|
+
);
|
|
196
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card.js';
|
|
2
|
+
import { RecentBusinessDocs } from '../common/documents/issue-document/recent-business-docs.js';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
businessId: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function DocumentsSection({ businessId }: Props) {
|
|
9
|
+
return (
|
|
10
|
+
<Card>
|
|
11
|
+
<CardHeader>
|
|
12
|
+
<div className="flex items-center justify-between">
|
|
13
|
+
<div>
|
|
14
|
+
<CardTitle>Documents</CardTitle>
|
|
15
|
+
<CardDescription>
|
|
16
|
+
Invoices, proformas, receipts, and other accounting documents
|
|
17
|
+
</CardDescription>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
</CardHeader>
|
|
21
|
+
<CardContent>
|
|
22
|
+
<RecentBusinessDocs businessId={businessId} linkedDocumentIds={[]} limit={1000} />
|
|
23
|
+
</CardContent>
|
|
24
|
+
</Card>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
ArrowLeftRight,
|
|
4
|
+
Building2,
|
|
5
|
+
DollarSign,
|
|
6
|
+
FileCheck,
|
|
7
|
+
FileText,
|
|
8
|
+
Plug,
|
|
9
|
+
Settings,
|
|
10
|
+
// TrendingUp,
|
|
11
|
+
} from 'lucide-react';
|
|
12
|
+
import { useSearchParams } from 'react-router-dom';
|
|
13
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs.js';
|
|
14
|
+
import { getFragmentData, type FragmentType } from '@/gql/index.js';
|
|
15
|
+
import { BusinessPageFragmentDoc } from '../../gql/graphql.js';
|
|
16
|
+
import { BusinessHeader } from './business-header.js';
|
|
17
|
+
import { ChargesSection } from './charges-section.jsx';
|
|
18
|
+
import { ChartsSection } from './charts-section.jsx';
|
|
19
|
+
import { ConfigurationsSection } from './configurations-section.jsx';
|
|
20
|
+
import { ContactInfoSection } from './contact-info-section.jsx';
|
|
21
|
+
import { ContractsSection } from './contracts-section.jsx';
|
|
22
|
+
import { DocumentsSection } from './documents-section.jsx';
|
|
23
|
+
import { IntegrationsSection } from './integrations-section.jsx';
|
|
24
|
+
import { TransactionsSection } from './transactions-section.js';
|
|
25
|
+
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions -- used by codegen
|
|
27
|
+
/* GraphQL */ `
|
|
28
|
+
fragment BusinessPage on Business {
|
|
29
|
+
id
|
|
30
|
+
... on LtdFinancialEntity {
|
|
31
|
+
clientInfo {
|
|
32
|
+
id
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
...ClientIntegrationsSection
|
|
36
|
+
...BusinessHeader
|
|
37
|
+
...BusinessContactSection
|
|
38
|
+
...BusinessConfigurationSection
|
|
39
|
+
}
|
|
40
|
+
`;
|
|
41
|
+
|
|
42
|
+
interface Props {
|
|
43
|
+
data?: FragmentType<typeof BusinessPageFragmentDoc>;
|
|
44
|
+
refetchBusiness?: () => Promise<void>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export default function Business({ data, refetchBusiness }: Props): ReactElement {
|
|
48
|
+
const business = getFragmentData(BusinessPageFragmentDoc, data);
|
|
49
|
+
const [searchParams, setSearchParams] = useSearchParams();
|
|
50
|
+
|
|
51
|
+
const activeTab = searchParams.get('tab') || 'contact';
|
|
52
|
+
|
|
53
|
+
const handleTabChange = (value: string) => {
|
|
54
|
+
setSearchParams({ tab: value });
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
if (!business) {
|
|
58
|
+
return <div>Business not found</div>;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const isClient = 'clientInfo' in business && !!business.clientInfo;
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<div className="min-h-screen bg-background">
|
|
65
|
+
<BusinessHeader data={business} />
|
|
66
|
+
|
|
67
|
+
<main className="container mx-auto px-4 py-6 md:px-6 lg:px-8 max-w-7xl">
|
|
68
|
+
<Tabs value={activeTab} onValueChange={handleTabChange} className="w-full">
|
|
69
|
+
<TabsList className="grid w-full lg:grid-cols-8 sm:grid-cols-4 grid-cols-8 mb-6 h-auto gap-1 bg-muted/50 p-1">
|
|
70
|
+
<TabsTrigger
|
|
71
|
+
value="contact"
|
|
72
|
+
className="flex items-center gap-2 data-[state=active]:bg-background"
|
|
73
|
+
>
|
|
74
|
+
<Building2 className="h-4 w-4" />
|
|
75
|
+
<span className="hidden sm:inline">Contact</span>
|
|
76
|
+
</TabsTrigger>
|
|
77
|
+
<TabsTrigger
|
|
78
|
+
value="config"
|
|
79
|
+
className="flex items-center gap-2 data-[state=active]:bg-background"
|
|
80
|
+
>
|
|
81
|
+
<Settings className="h-4 w-4" />
|
|
82
|
+
<span className="hidden sm:inline">Config</span>
|
|
83
|
+
</TabsTrigger>
|
|
84
|
+
<TabsTrigger
|
|
85
|
+
value="charges"
|
|
86
|
+
className="flex items-center gap-2 data-[state=active]:bg-background"
|
|
87
|
+
>
|
|
88
|
+
<DollarSign className="h-4 w-4" />
|
|
89
|
+
<span className="hidden sm:inline">Charges</span>
|
|
90
|
+
</TabsTrigger>
|
|
91
|
+
<TabsTrigger
|
|
92
|
+
value="transactions"
|
|
93
|
+
className="flex items-center gap-2 data-[state=active]:bg-background"
|
|
94
|
+
>
|
|
95
|
+
<ArrowLeftRight className="h-4 w-4" />
|
|
96
|
+
<span className="hidden sm:inline">Transactions</span>
|
|
97
|
+
</TabsTrigger>
|
|
98
|
+
<TabsTrigger
|
|
99
|
+
value="documents"
|
|
100
|
+
className="flex items-center gap-2 data-[state=active]:bg-background"
|
|
101
|
+
>
|
|
102
|
+
<FileText className="h-4 w-4" />
|
|
103
|
+
<span className="hidden sm:inline">Documents</span>
|
|
104
|
+
</TabsTrigger>
|
|
105
|
+
{isClient && (
|
|
106
|
+
<>
|
|
107
|
+
<TabsTrigger
|
|
108
|
+
value="contracts"
|
|
109
|
+
className="flex items-center gap-2 data-[state=active]:bg-background"
|
|
110
|
+
>
|
|
111
|
+
<FileCheck className="h-4 w-4" />
|
|
112
|
+
<span className="hidden sm:inline">Contracts</span>
|
|
113
|
+
</TabsTrigger>
|
|
114
|
+
<TabsTrigger
|
|
115
|
+
value="integrations"
|
|
116
|
+
className="flex items-center gap-2 data-[state=active]:bg-background"
|
|
117
|
+
>
|
|
118
|
+
<Plug className="h-4 w-4" />
|
|
119
|
+
<span className="hidden sm:inline">Integrations</span>
|
|
120
|
+
</TabsTrigger>
|
|
121
|
+
{/* <TabsTrigger
|
|
122
|
+
value="charts"
|
|
123
|
+
className="flex items-center gap-2 data-[state=active]:bg-background"
|
|
124
|
+
>
|
|
125
|
+
<TrendingUp className="h-4 w-4" />
|
|
126
|
+
<span className="hidden sm:inline">Charts</span>
|
|
127
|
+
</TabsTrigger> */}
|
|
128
|
+
</>
|
|
129
|
+
)}
|
|
130
|
+
</TabsList>
|
|
131
|
+
|
|
132
|
+
<TabsContent value="contact" className="mt-0">
|
|
133
|
+
<ContactInfoSection data={business} refetchBusiness={refetchBusiness} />
|
|
134
|
+
</TabsContent>
|
|
135
|
+
|
|
136
|
+
<TabsContent value="config" className="mt-0">
|
|
137
|
+
<ConfigurationsSection data={business} refetchBusiness={refetchBusiness} />
|
|
138
|
+
</TabsContent>
|
|
139
|
+
|
|
140
|
+
<TabsContent value="charges" className="mt-0">
|
|
141
|
+
<ChargesSection businessId={business.id} />
|
|
142
|
+
</TabsContent>
|
|
143
|
+
|
|
144
|
+
<TabsContent value="transactions" className="mt-0">
|
|
145
|
+
<TransactionsSection businessId={business.id} />
|
|
146
|
+
</TabsContent>
|
|
147
|
+
|
|
148
|
+
<TabsContent value="documents" className="mt-0">
|
|
149
|
+
<DocumentsSection businessId={business.id} />
|
|
150
|
+
</TabsContent>
|
|
151
|
+
|
|
152
|
+
{isClient && (
|
|
153
|
+
<>
|
|
154
|
+
<TabsContent value="contracts" className="mt-0">
|
|
155
|
+
<ContractsSection clientId={business.id} />
|
|
156
|
+
</TabsContent>
|
|
157
|
+
|
|
158
|
+
<TabsContent value="integrations" className="mt-0">
|
|
159
|
+
<IntegrationsSection data={business} />
|
|
160
|
+
</TabsContent>
|
|
161
|
+
|
|
162
|
+
<TabsContent value="charts" className="mt-0">
|
|
163
|
+
<ChartsSection />
|
|
164
|
+
</TabsContent>
|
|
165
|
+
</>
|
|
166
|
+
)}
|
|
167
|
+
</Tabs>
|
|
168
|
+
</main>
|
|
169
|
+
</div>
|
|
170
|
+
);
|
|
171
|
+
}
|