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
57 changes: 3 additions & 54 deletions src/app/(home)/HomeClient.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,10 @@
'use client'
import { useApp } from '@/app/context/AppContext'
import { Main as DashboardMain } from '@/components/dashboard/Main'
import { SilentError } from '@/components/template/SilentError'
import { useAppBridge, useQuickbooks } from '@/hook/useQuickbooks'
import { Button, Spinner } from 'copilot-design-system'
import { useAppBridge } from '@/hook/useQuickbooks'

export default function HomeClient() {
const {
token,
tokenPayload,
reconnect,
portalConnectionStatus,
isEnabled,
syncFlag,
} = useApp()

const { loading, handleConnect, isReconnecting } = useQuickbooks(
token,
tokenPayload,
reconnect,
)
const { token, portalConnectionStatus, isEnabled, syncFlag } = useApp()

// bridge related logics like disconnect app and download sync log csv
useAppBridge({
Expand All @@ -29,45 +14,9 @@ export default function HomeClient() {
connectionStatus: portalConnectionStatus || false,
})

if (portalConnectionStatus === null) {
return (
<SilentError
message="Something went wrong while connecting to QuickBooks"
resetFn={handleConnect}
/>
)
}

return (
<div className="home-client-wrapper w-full h-full">
{portalConnectionStatus ? (
<>
{isReconnecting && (
<div>
<span className="me-2">Reconnecting to QuickBooks</span>{' '}
<Spinner size={5} />
</div>
)}
<DashboardMain />
</>
) : (
<div className="flex items-center justify-center h-full text-xl">
{loading ? (
<div className="flex items-center">
<span className="me-2">
Connecting to QuickBooks. Please wait
</span>{' '}
<Spinner size={5} />
</div>
) : (
<Button
label="Connect to QuickBooks"
onClick={() => handleConnect()}
disabled={loading}
/>
)}
</div>
)}
<DashboardMain />
</div>
)
}
8 changes: 8 additions & 0 deletions src/app/api/quickbooks/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
QBPortalConnectionCreateSchemaType,
QBPortalConnectionUpdateSchemaType,
} from '@/db/schema/qbPortalConnections'
import { QBSetting } from '@/db/schema/qbSettings'
import {
getPortalConnection,
getPortalSettings,
Expand Down Expand Up @@ -111,6 +112,13 @@ export class AuthService extends BaseService {
portalId,
syncFlag,
})
} else {
await settingsService.updateQBSettings(
{
syncFlag,
},
eq(QBSetting.portalId, portalId),
)
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/app/api/quickbooks/invoice/invoice.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,12 @@ export class InvoiceService extends BaseService {
// check if the flag is on for create new item
const settingService = new SettingService(this.user)
const setting = await settingService.getOneByPortalId([
'createInvoiceItemFlag',
'createNewProductFlag',
])

if (!setting?.createInvoiceItemFlag) {
if (!setting?.createNewProductFlag) {
console.info(
'WebhookService#getInvoiceItemRef | Create new invoice item flag is false',
'WebhookService#getInvoiceItemRef | Create new product flag is false',
)
return { ref: oneOffItem }
}
Expand Down
10 changes: 5 additions & 5 deletions src/app/api/quickbooks/setting/setting.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@ export async function getSettings(req: NextRequest) {
const settingService = new SettingService(user)
const type = req.nextUrl.searchParams.get('type')
const parsedType = z.nativeEnum(SettingType).safeParse(type)
const returnigFields: (keyof typeof QBSetting)[] = []
const returningFields: (keyof typeof QBSetting)[] = []

if (parsedType.success) {
// return attributes as per the type. If type not provided, return all attributes
returnigFields.push('id', 'initialSettingMap')
returningFields.push('id', 'initialSettingMap')
if (parsedType.data === SettingType.INVOICE)
returnigFields.push('absorbedFeeFlag', 'useCompanyNameFlag')
returningFields.push('absorbedFeeFlag', 'useCompanyNameFlag')
if (parsedType.data === SettingType.PRODUCT)
returnigFields.push('createNewProductFlag', 'createInvoiceItemFlag')
returningFields.push('createNewProductFlag')
}
const setting = await settingService.getOneByPortalId(returnigFields)
const setting = await settingService.getOneByPortalId(returningFields)
return NextResponse.json({ setting })
}

Expand Down
55 changes: 42 additions & 13 deletions src/components/dashboard/Main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
Spinner,
} from 'copilot-design-system'
import LastSyncAt from '@/components/dashboard/LastSyncAt'
import { SilentError } from '@/components/template/SilentError'

type CalloutType = {
title: string
Expand Down Expand Up @@ -43,6 +44,13 @@ const DashboardCallout = (lastSyncTime: string | null) => ({
actionIcon: 'Repeat' as IconType,
buttonVariant: 'secondary' as const,
},
[CalloutVariant.INFO]: {
title: 'Authorize your account',
description: 'Log into QuickBooks with an admin account to get started.',
actionLabel: 'Connect to QuickBooks',
actionIcon: 'Check' as IconType,
buttonVariant: 'primary' as const,
},
})

export const Main = () => {
Expand All @@ -53,8 +61,21 @@ export const Main = () => {
isReconnecting,
lastSyncTimestamp,
itemMapped,
portalConnectionStatus,
syncFlag,
handleConnect,
isConnecting,
} = useDashboardMain()

if (portalConnectionStatus === null) {
return (
<SilentError
message="Something went wrong while connecting to QuickBooks"
resetFn={handleConnect}
/>
)
}

const dashboardCallout: CalloutType =
DashboardCallout(lastSyncTimestamp)[status]

Expand All @@ -70,31 +91,39 @@ export const Main = () => {
variant={status}
{...(dashboardCallout.actionLabel && {
actionProps: {
label: isReconnecting
? 'Reauthorizing...'
: dashboardCallout.actionLabel,
label:
isReconnecting || isConnecting
? 'Connecting...'
: dashboardCallout.actionLabel,
onClick: buttonAction,
disabled:
isReconnecting ||
isConnecting ||
(status === CalloutVariant.WARNING && !itemMapped),
prefixIcon: dashboardCallout.actionIcon,
...(dashboardCallout.buttonVariant && {
variant: dashboardCallout.buttonVariant,
}),
className: !portalConnectionStatus ? 'lg:!px-8' : '',
},
})}
/>
<div className="mt-6 mb-2">
<Heading
size="xl"
tag="h2"
className="pb-4 border-b-1 border-b-card-divider !leading-7" // forcing styles with "!"
>
Settings
</Heading>
<Divider />
<div className={!portalConnectionStatus ? 'opacity-25 relative' : ''}>
{!portalConnectionStatus && (
<div className="absolute top-0 left-0 w-full h-full z-10"></div>
)}
<div className="mt-6 mb-2">
<Heading
size="xl"
tag="h2"
className="pb-4 border-b-1 border-b-card-divider !leading-7" // forcing styles with "!"
>
Settings
</Heading>
<Divider />
</div>
<SettingAccordion syncFlag={syncFlag} />
</div>
<SettingAccordion />
</>
)}
</>
Expand Down
9 changes: 7 additions & 2 deletions src/components/dashboard/settings/SettingAccordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import {
} from '@/hook/useSettings'
import { Button } from 'copilot-design-system'

export default function SettingAccordion() {
export default function SettingAccordion({
syncFlag,
}: {
syncFlag: boolean | null
}) {
const {
openDropdowns,
setOpenDropdowns,
Expand Down Expand Up @@ -89,6 +93,7 @@ export default function SettingAccordion() {
className={`absolute top-[14px] right-0 z-10 flex items-center justify-end`}
>
{index === 0 &&
syncFlag &&
(showProductConfirm || setting.settingShowConfirm) && (
<>
{!initialSettingMapFlag && (
Expand All @@ -107,7 +112,7 @@ export default function SettingAccordion() {
/>
</>
)}
{index === 1 && showInvoiceButton && (
{index === 1 && syncFlag && showInvoiceButton && (
<>
{!initialSettingMapFlag && (
<Button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,8 @@ export default function ProductMapping({
<div className="mt-2 mb-6">
<div className="mb-5">
<Checkbox
label="Add newly created products to Quickbooks"
description="Automatically create matching items in QuickBooks with product name,
description, and price when new products are created in Copilot."
label="Sync Copilot products to QuickBooks"
description="Automatically create and update QuickBooks items when products are created or used in Copilot."
checked={setting.settingState.createNewProductFlag}
onChange={() =>
setting.changeSettings(
Expand All @@ -70,20 +69,6 @@ export default function ProductMapping({
}
/>
</div>
<div className="mb-5">
<Checkbox
label="Create an item in QuickBooks if Copilot product is not found"
description="Create missing QuickBooks items when syncing invoices containing
Copilot products that don't yet exist in QuickBooks."
checked={setting.settingState.createInvoiceItemFlag}
onChange={() =>
setting.changeSettings(
'createInvoiceItemFlag',
!setting.settingState.createInvoiceItemFlag,
)
}
/>
</div>

{/* Product Mapping table */}
<ProductMappingTable
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE "qb_settings" DROP COLUMN "create_invoice_item_flag";
Loading
Loading