mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-19 14:20:31 +00:00
Compare commits
9 Commits
print-reso
...
plaid-env-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9174203320 | ||
|
|
11cc4ffb0a | ||
|
|
c279b982e6 | ||
|
|
b1bf932e88 | ||
|
|
aa897212ab | ||
|
|
890903e08b | ||
|
|
16b2a33cf6 | ||
|
|
382d4ab028 | ||
|
|
85f26e1079 |
127
.github/workflows/build-deploy-develop-container.yaml
vendored
Normal file
127
.github/workflows/build-deploy-develop-container.yaml
vendored
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
# This workflow will build a docker container, publish it to Github Registry.
|
||||||
|
name: Build and Deploy Develop Docker Container
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- develop
|
||||||
|
|
||||||
|
env:
|
||||||
|
WEBAPP_IMAGE_NAME: bigcapitalhq/webapp
|
||||||
|
SERVER_IMAGE_NAME: bigcapitalhq/server
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-publish-webapp:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
name: Build and deploy webapp container
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
environment: production
|
||||||
|
steps:
|
||||||
|
- name: Prepare
|
||||||
|
run: |
|
||||||
|
platform=${{ matrix.platform }}
|
||||||
|
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
# Login to Container registry.
|
||||||
|
- name: Log in to the Container registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Extract metadata (tags, labels) for Docker
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
|
||||||
|
with:
|
||||||
|
images: ${{ env.WEBAPP_IMAGE_NAME }}
|
||||||
|
|
||||||
|
# Builds and push the Docker image.
|
||||||
|
- name: Build and push Docker image
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
id: build
|
||||||
|
with:
|
||||||
|
context: ./
|
||||||
|
file: ./packages/webapp/Dockerfile
|
||||||
|
platforms: linux/amd64
|
||||||
|
push: true
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
tags: bigcapitalhq/webapp:develop
|
||||||
|
|
||||||
|
- name: Export digest
|
||||||
|
run: |
|
||||||
|
mkdir -p /tmp/digests
|
||||||
|
digest="${{ steps.build.outputs.digest }}"
|
||||||
|
touch "/tmp/digests/${digest#sha256:}"
|
||||||
|
|
||||||
|
- name: Upload digest
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: digests-webapp
|
||||||
|
path: /tmp/digests/*
|
||||||
|
if-no-files-found: error
|
||||||
|
retention-days: 1
|
||||||
|
# Send notification to Slack channel.
|
||||||
|
- name: Slack Notification built and published webapp container successfully.
|
||||||
|
uses: rtCamp/action-slack-notify@v2
|
||||||
|
env:
|
||||||
|
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
|
||||||
|
|
||||||
|
build-publish-server:
|
||||||
|
name: Build and deploy server container
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Prepare
|
||||||
|
run: |
|
||||||
|
platform=${{ matrix.platform }}
|
||||||
|
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
# Login to Container registry.
|
||||||
|
- name: Log in to the Container registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
|
||||||
|
# Builds and push the Docker image.
|
||||||
|
- name: Build and push Docker image
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
id: build
|
||||||
|
with:
|
||||||
|
context: ./
|
||||||
|
file: ./packages/server/Dockerfile
|
||||||
|
platforms: linux/amd64
|
||||||
|
push: true
|
||||||
|
tags: bigcapitalhq/server:develop
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
|
||||||
|
- name: Export digest
|
||||||
|
run: |
|
||||||
|
mkdir -p /tmp/digests
|
||||||
|
digest="${{ steps.build.outputs.digest }}"
|
||||||
|
touch "/tmp/digests/${digest#sha256:}"
|
||||||
|
|
||||||
|
- name: Upload digest
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: digests-server
|
||||||
|
path: /tmp/digests/*
|
||||||
|
if-no-files-found: error
|
||||||
|
retention-days: 1
|
||||||
|
|
||||||
|
# Send notification to Slack channel.
|
||||||
|
- name: Slack Notification built and published server container successfully.
|
||||||
|
uses: rtCamp/action-slack-notify@v2
|
||||||
|
env:
|
||||||
|
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
|
||||||
@@ -114,6 +114,13 @@ services:
|
|||||||
- NEW_RELIC_LICENSE_KEY=${NEW_RELIC_LICENSE_KEY}
|
- NEW_RELIC_LICENSE_KEY=${NEW_RELIC_LICENSE_KEY}
|
||||||
- NEW_RELIC_APP_NAME=${NEW_RELIC_APP_NAME}
|
- NEW_RELIC_APP_NAME=${NEW_RELIC_APP_NAME}
|
||||||
|
|
||||||
|
# S3
|
||||||
|
- S3_REGION=${S3_REGION}
|
||||||
|
- S3_ACCESS_KEY_ID=${S3_ACCESS_KEY_ID}
|
||||||
|
- S3_SECRET_ACCESS_KEY=${S3_SECRET_ACCESS_KEY}
|
||||||
|
- S3_ENDPOINT=${S3_ENDPOINT}
|
||||||
|
- S3_BUCKET=${S3_BUCKET}
|
||||||
|
|
||||||
database_migration:
|
database_migration:
|
||||||
container_name: bigcapital-database-migration
|
container_name: bigcapital-database-migration
|
||||||
build:
|
build:
|
||||||
|
|||||||
@@ -204,10 +204,7 @@ module.exports = {
|
|||||||
plaid: {
|
plaid: {
|
||||||
env: process.env.PLAID_ENV || 'sandbox',
|
env: process.env.PLAID_ENV || 'sandbox',
|
||||||
clientId: process.env.PLAID_CLIENT_ID,
|
clientId: process.env.PLAID_CLIENT_ID,
|
||||||
secretDevelopment: process.env.PLAID_SECRET_DEVELOPMENT,
|
secret: process.env.PLAID_SECRET,
|
||||||
secretSandbox: process.env.PLAID_SECRET_SANDBOX,
|
|
||||||
redirectSandBox: process.env.PLAID_SANDBOX_REDIRECT_URI,
|
|
||||||
redirectDevelopment: process.env.PLAID_DEVELOPMENT_REDIRECT_URI,
|
|
||||||
linkWebhook: process.env.PLAID_LINK_WEBHOOK,
|
linkWebhook: process.env.PLAID_LINK_WEBHOOK,
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -218,6 +215,7 @@ module.exports = {
|
|||||||
key: process.env.LEMONSQUEEZY_API_KEY,
|
key: process.env.LEMONSQUEEZY_API_KEY,
|
||||||
storeId: process.env.LEMONSQUEEZY_STORE_ID,
|
storeId: process.env.LEMONSQUEEZY_STORE_ID,
|
||||||
webhookSecret: process.env.LEMONSQUEEZY_WEBHOOK_SECRET,
|
webhookSecret: process.env.LEMONSQUEEZY_WEBHOOK_SECRET,
|
||||||
|
redirectTo: `${process.env.BASE_URL}/setup`,
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -70,10 +70,7 @@ export class PlaidClientWrapper {
|
|||||||
baseOptions: {
|
baseOptions: {
|
||||||
headers: {
|
headers: {
|
||||||
'PLAID-CLIENT-ID': config.plaid.clientId,
|
'PLAID-CLIENT-ID': config.plaid.clientId,
|
||||||
'PLAID-SECRET':
|
'PLAID-SECRET': config.plaid.secret,
|
||||||
config.plaid.env === 'development'
|
|
||||||
? config.plaid.secretDevelopment
|
|
||||||
: config.plaid.secretSandbox,
|
|
||||||
'Plaid-Version': '2020-09-14',
|
'Plaid-Version': '2020-09-14',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Service } from 'typedi';
|
|||||||
import { createCheckout } from '@lemonsqueezy/lemonsqueezy.js';
|
import { createCheckout } from '@lemonsqueezy/lemonsqueezy.js';
|
||||||
import { SystemUser } from '@/system/models';
|
import { SystemUser } from '@/system/models';
|
||||||
import { configureLemonSqueezy } from './utils';
|
import { configureLemonSqueezy } from './utils';
|
||||||
|
import config from '@/config';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class LemonSqueezyService {
|
export class LemonSqueezyService {
|
||||||
@@ -28,7 +29,7 @@ export class LemonSqueezyService {
|
|||||||
},
|
},
|
||||||
productOptions: {
|
productOptions: {
|
||||||
enabledVariants: [variantId],
|
enabledVariants: [variantId],
|
||||||
redirectUrl: `http://localhost:4000/dashboard/billing/`,
|
redirectUrl: config.lemonSqueezy.redirectTo,
|
||||||
receiptButtonText: 'Go to Dashboard',
|
receiptButtonText: 'Go to Dashboard',
|
||||||
receiptThankYouNote: 'Thank you for signing up to Lemon Stand!',
|
receiptThankYouNote: 'Thank you for signing up to Lemon Stand!',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ import InvoiceMailDialog from '@/containers/Sales/Invoices/InvoiceMailDialog/Inv
|
|||||||
import EstimateMailDialog from '@/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialog';
|
import EstimateMailDialog from '@/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialog';
|
||||||
import ReceiptMailDialog from '@/containers/Sales/Receipts/ReceiptMailDialog/ReceiptMailDialog';
|
import ReceiptMailDialog from '@/containers/Sales/Receipts/ReceiptMailDialog/ReceiptMailDialog';
|
||||||
import PaymentMailDialog from '@/containers/Sales/PaymentReceives/PaymentMailDialog/PaymentMailDialog';
|
import PaymentMailDialog from '@/containers/Sales/PaymentReceives/PaymentMailDialog/PaymentMailDialog';
|
||||||
import { ConnectBankDialog } from '@/containers/CashFlow/ConnectBankDialog';
|
|
||||||
import { ExportDialog } from '@/containers/Dialogs/ExportDialog';
|
import { ExportDialog } from '@/containers/Dialogs/ExportDialog';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -97,7 +96,6 @@ export default function DialogsContainer() {
|
|||||||
<NotifyPaymentReceiveViaSMSDialog
|
<NotifyPaymentReceiveViaSMSDialog
|
||||||
dialogName={DialogsName.NotifyPaymentViaForm}
|
dialogName={DialogsName.NotifyPaymentViaForm}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<BadDebtDialog dialogName={DialogsName.BadDebtForm} />
|
<BadDebtDialog dialogName={DialogsName.BadDebtForm} />
|
||||||
<SMSMessageDialog dialogName={DialogsName.SMSMessageForm} />
|
<SMSMessageDialog dialogName={DialogsName.SMSMessageForm} />
|
||||||
<RefundCreditNoteDialog dialogName={DialogsName.RefundCreditNote} />
|
<RefundCreditNoteDialog dialogName={DialogsName.RefundCreditNote} />
|
||||||
@@ -148,8 +146,6 @@ export default function DialogsContainer() {
|
|||||||
<EstimateMailDialog dialogName={DialogsName.EstimateMail} />
|
<EstimateMailDialog dialogName={DialogsName.EstimateMail} />
|
||||||
<ReceiptMailDialog dialogName={DialogsName.ReceiptMail} />
|
<ReceiptMailDialog dialogName={DialogsName.ReceiptMail} />
|
||||||
<PaymentMailDialog dialogName={DialogsName.PaymentMail} />
|
<PaymentMailDialog dialogName={DialogsName.PaymentMail} />
|
||||||
<ConnectBankDialog dialogName={DialogsName.ConnectBankCreditCard} />
|
|
||||||
|
|
||||||
<ExportDialog dialogName={DialogsName.Export} />
|
<ExportDialog dialogName={DialogsName.Export} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
FeatureCan,
|
FeatureCan,
|
||||||
} from '@/components';
|
} from '@/components';
|
||||||
import { useRefreshCashflowAccounts } from '@/hooks/query';
|
import { useRefreshCashflowAccounts } from '@/hooks/query';
|
||||||
|
import { useOpenPlaidConnect } from '@/hooks/utils/useOpenPlaidConnect';
|
||||||
import { CashflowAction, AbilitySubject } from '@/constants/abilityOption';
|
import { CashflowAction, AbilitySubject } from '@/constants/abilityOption';
|
||||||
|
|
||||||
import withDialogActions from '@/containers/Dialog/withDialogActions';
|
import withDialogActions from '@/containers/Dialog/withDialogActions';
|
||||||
@@ -39,6 +40,9 @@ function CashFlowAccountsActionsBar({
|
|||||||
}) {
|
}) {
|
||||||
const { refresh } = useRefreshCashflowAccounts();
|
const { refresh } = useRefreshCashflowAccounts();
|
||||||
|
|
||||||
|
// Opens the Plaid popup.
|
||||||
|
const { openPlaidAsync, isPlaidLoading } = useOpenPlaidConnect();
|
||||||
|
|
||||||
// Handle refresh button click.
|
// Handle refresh button click.
|
||||||
const handleRefreshBtnClick = () => {
|
const handleRefreshBtnClick = () => {
|
||||||
refresh();
|
refresh();
|
||||||
@@ -64,7 +68,7 @@ function CashFlowAccountsActionsBar({
|
|||||||
};
|
};
|
||||||
// Handle connect button click.
|
// Handle connect button click.
|
||||||
const handleConnectToBank = () => {
|
const handleConnectToBank = () => {
|
||||||
openDialog(DialogsName.ConnectBankCreditCard);
|
openPlaidAsync();
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -116,6 +120,7 @@ function CashFlowAccountsActionsBar({
|
|||||||
className={Classes.MINIMAL}
|
className={Classes.MINIMAL}
|
||||||
text={'Connect to Bank / Credit Card'}
|
text={'Connect to Bank / Credit Card'}
|
||||||
onClick={handleConnectToBank}
|
onClick={handleConnectToBank}
|
||||||
|
disabled={isPlaidLoading}
|
||||||
/>
|
/>
|
||||||
<NavbarDivider />
|
<NavbarDivider />
|
||||||
</FeatureCan>
|
</FeatureCan>
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import React from 'react';
|
|
||||||
import { Dialog, DialogSuspense } from '@/components';
|
|
||||||
import withDialogRedux from '@/components/DialogReduxConnect';
|
|
||||||
import { compose } from '@/utils';
|
|
||||||
|
|
||||||
const ConnectBankDialogBody = React.lazy(
|
|
||||||
() => import('./ConnectBankDialogBody'),
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Connect bank dialog.
|
|
||||||
*/
|
|
||||||
function ConnectBankDialogRoot({ dialogName, payload = {}, isOpen }) {
|
|
||||||
return (
|
|
||||||
<Dialog
|
|
||||||
name={dialogName}
|
|
||||||
title={'Securly connect your bank or credit card.'}
|
|
||||||
isOpen={isOpen}
|
|
||||||
canEscapeJeyClose={true}
|
|
||||||
autoFocus={true}
|
|
||||||
>
|
|
||||||
<DialogSuspense>
|
|
||||||
<ConnectBankDialogBody dialogName={dialogName} />
|
|
||||||
</DialogSuspense>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ConnectBankDialog = compose(withDialogRedux())(
|
|
||||||
ConnectBankDialogRoot,
|
|
||||||
);
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import * as R from 'ramda';
|
|
||||||
import { Form, Formik, FormikHelpers } from 'formik';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import { ConnectBankDialogContent } from './ConnectBankDialogContent';
|
|
||||||
import { useGetPlaidLinkToken } from '@/hooks/query';
|
|
||||||
import { useSetBankingPlaidToken } from '@/hooks/state/banking';
|
|
||||||
import withDialogActions from '@/containers/Dialog/withDialogActions';
|
|
||||||
import { CLASSES } from '@/constants';
|
|
||||||
import { AppToaster } from '@/components';
|
|
||||||
import { Intent } from '@blueprintjs/core';
|
|
||||||
import { DialogsName } from '@/constants/dialogs';
|
|
||||||
|
|
||||||
const initialValues: ConnectBankDialogForm = {
|
|
||||||
serviceProvider: 'plaid',
|
|
||||||
};
|
|
||||||
|
|
||||||
interface ConnectBankDialogForm {
|
|
||||||
serviceProvider: 'plaid';
|
|
||||||
}
|
|
||||||
|
|
||||||
function ConnectBankDialogBodyRoot({
|
|
||||||
// #withDialogActions
|
|
||||||
closeDialog,
|
|
||||||
}) {
|
|
||||||
const { mutateAsync: getPlaidLinkToken } = useGetPlaidLinkToken();
|
|
||||||
const setPlaidId = useSetBankingPlaidToken();
|
|
||||||
|
|
||||||
// Handles the form submitting.
|
|
||||||
const handleSubmit = (
|
|
||||||
values: ConnectBankDialogForm,
|
|
||||||
{ setSubmitting }: FormikHelpers<ConnectBankDialogForm>,
|
|
||||||
) => {
|
|
||||||
setSubmitting(true);
|
|
||||||
getPlaidLinkToken()
|
|
||||||
.then((res) => {
|
|
||||||
setSubmitting(false);
|
|
||||||
closeDialog(DialogsName.ConnectBankCreditCard);
|
|
||||||
setPlaidId(res.data.link_token);
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
setSubmitting(false);
|
|
||||||
AppToaster.show({
|
|
||||||
message: 'Something went wrong.',
|
|
||||||
intent: Intent.DANGER,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={classNames(CLASSES.DIALOG_BODY)}>
|
|
||||||
<Formik initialValues={initialValues} onSubmit={handleSubmit}>
|
|
||||||
<Form>
|
|
||||||
<ConnectBankDialogContent />
|
|
||||||
</Form>
|
|
||||||
</Formik>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default R.compose(withDialogActions)(ConnectBankDialogBodyRoot);
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import styled from 'styled-components';
|
|
||||||
import { Stack } from '@/components';
|
|
||||||
import { TellerIcon } from '../Icons/TellerIcon';
|
|
||||||
import { YodleeIcon } from '../Icons/YodleeIcon';
|
|
||||||
import { PlaidIcon } from '../Icons/PlaidIcon';
|
|
||||||
import { BankServiceCard } from './ConnectBankServiceCard';
|
|
||||||
|
|
||||||
const TopDesc = styled('p')`
|
|
||||||
margin-bottom: 20px;
|
|
||||||
color: #5f6b7c;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export function ConnectBankDialogContent() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<TopDesc>
|
|
||||||
Connect your bank accounts and fetch the bank transactions using
|
|
||||||
one of our supported third-party service providers.
|
|
||||||
</TopDesc>
|
|
||||||
|
|
||||||
<Stack>
|
|
||||||
<BankServiceCard
|
|
||||||
title={'Plaid (US, UK & Canada)'}
|
|
||||||
icon={<PlaidIcon />}
|
|
||||||
>
|
|
||||||
Plaid gives the connection to 12,000 financial institutions across US, UK and Canada.
|
|
||||||
</BankServiceCard>
|
|
||||||
|
|
||||||
<BankServiceCard
|
|
||||||
title={'Teller (US) — Soon'}
|
|
||||||
icon={<TellerIcon />}
|
|
||||||
disabled
|
|
||||||
>
|
|
||||||
Connect instantly with more than 5,000 financial institutions across US.
|
|
||||||
</BankServiceCard>
|
|
||||||
|
|
||||||
<BankServiceCard
|
|
||||||
title={'Yodlee (Global) — Soon'}
|
|
||||||
icon={<YodleeIcon />}
|
|
||||||
disabled
|
|
||||||
>
|
|
||||||
Connect instantly with a global network of financial institutions.
|
|
||||||
</BankServiceCard>
|
|
||||||
</Stack>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
import styled from 'styled-components';
|
|
||||||
import { Group } from '@/components';
|
|
||||||
|
|
||||||
const BankServiceIcon = styled('div')`
|
|
||||||
height: 40px;
|
|
||||||
width: 40px;
|
|
||||||
border: 1px solid #c8cad0;
|
|
||||||
border-radius: 3px;
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
svg {
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
const BankServiceContent = styled(`div`)`
|
|
||||||
flex: 1 0;
|
|
||||||
`;
|
|
||||||
const BankServiceCardRoot = styled('button')`
|
|
||||||
border-radius: 3px;
|
|
||||||
border: 1px solid #c8cad0;
|
|
||||||
transition: all 0.1s ease-in-out;
|
|
||||||
background: transparent;
|
|
||||||
text-align: inherit;
|
|
||||||
padding: 14px;
|
|
||||||
|
|
||||||
&:not(:disabled) {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
&:hover:not(:disabled) {
|
|
||||||
border-color: #0153cc;
|
|
||||||
}
|
|
||||||
&:disabled {
|
|
||||||
background: #f9fdff;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
const BankServiceTitle = styled(`h3`)`
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #2d333d;
|
|
||||||
`;
|
|
||||||
const BankServiceDesc = styled('p')`
|
|
||||||
margin-top: 4px;
|
|
||||||
margin-bottom: 6px;
|
|
||||||
font-size: 13px;
|
|
||||||
color: #738091;
|
|
||||||
`;
|
|
||||||
|
|
||||||
interface BankServiceCardProps {
|
|
||||||
title: string;
|
|
||||||
children: React.ReactNode;
|
|
||||||
disabled?: boolean;
|
|
||||||
icon: React.ReactNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function BankServiceCard({
|
|
||||||
title,
|
|
||||||
children,
|
|
||||||
icon,
|
|
||||||
disabled,
|
|
||||||
}: BankServiceCardProps) {
|
|
||||||
return (
|
|
||||||
<BankServiceCardRoot disabled={disabled}>
|
|
||||||
<Group>
|
|
||||||
<BankServiceIcon>{icon}</BankServiceIcon>
|
|
||||||
<BankServiceContent>
|
|
||||||
<BankServiceTitle>{title}</BankServiceTitle>
|
|
||||||
<BankServiceDesc>{children}</BankServiceDesc>
|
|
||||||
</BankServiceContent>
|
|
||||||
</Group>
|
|
||||||
</BankServiceCardRoot>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from './ConnectBankDialog';
|
|
||||||
@@ -26,7 +26,7 @@ function SubscriptionPricing({
|
|||||||
useGetLemonSqueezyCheckout();
|
useGetLemonSqueezyCheckout();
|
||||||
|
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
getLemonCheckout({ variantId: '338516' })
|
getLemonCheckout({ variantId: '337977' })
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
const checkoutUrl = res.data.data.attributes.url;
|
const checkoutUrl = res.data.data.attributes.url;
|
||||||
window.LemonSqueezy.Url.Open(checkoutUrl);
|
window.LemonSqueezy.Url.Open(checkoutUrl);
|
||||||
|
|||||||
25
packages/webapp/src/hooks/utils/useOpenPlaidConnect.ts
Normal file
25
packages/webapp/src/hooks/utils/useOpenPlaidConnect.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { useCallback } from 'react';
|
||||||
|
import { useSetBankingPlaidToken } from '../state/banking';
|
||||||
|
import { AppToaster } from '@/components';
|
||||||
|
import { useGetPlaidLinkToken } from '../query';
|
||||||
|
import { Intent } from '@blueprintjs/core';
|
||||||
|
|
||||||
|
export const useOpenPlaidConnect = () => {
|
||||||
|
const { mutateAsync: getPlaidLinkToken, isLoading } = useGetPlaidLinkToken();
|
||||||
|
const setPlaidId = useSetBankingPlaidToken();
|
||||||
|
|
||||||
|
const openPlaidAsync = useCallback(() => {
|
||||||
|
return getPlaidLinkToken()
|
||||||
|
.then((res) => {
|
||||||
|
setPlaidId(res.data.link_token);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
AppToaster.show({
|
||||||
|
message: 'Something went wrong.',
|
||||||
|
intent: Intent.DANGER,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, [getPlaidLinkToken, setPlaidId]);
|
||||||
|
|
||||||
|
return { openPlaidAsync, isPlaidLoading: isLoading };
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user