Thanks to visit codestin.com
Credit goes to github.com

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
2 changes: 1 addition & 1 deletion apps/web/src/components/cards/InvoiceCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ const InvoiceCard = ({
{showExtraInfo && (
<div className="flex w-full flex-col items-center justify-center gap-2 p-2">
{showAmount && (
<div className="line-clamp-1 w-full rounded border border-green-300/20 bg-green-300/10 px-4 py-0.5 font-semibold text-green-300">
<div className="line-clamp-1 w-full rounded border border-orange-300/20 bg-orange-300/10 px-4 py-0.5 font-semibold text-orange-300">
{Intl.NumberFormat('vi-VN', {
style: 'decimal',
}).format(items?.count || 0)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ interface Progress {
remove: Status;
}

const DeleteModal = ({ wsId, invoiceId, products }: Props) => {
const InvoiceDeleteModal = ({ wsId, invoiceId, products }: Props) => {
const router = useRouter();

const { t } = useTranslation('invoice-modal');
Expand Down Expand Up @@ -249,4 +249,4 @@ const DeleteModal = ({ wsId, invoiceId, products }: Props) => {
);
};

export default DeleteModal;
export default InvoiceDeleteModal;
4 changes: 2 additions & 2 deletions apps/web/src/components/loaders/invoices/InvoiceEditModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ interface Progress {
addProducts: Status;
}

const EditModal = ({
const InvoiceEditModal = ({
wsId,
oldProducts,
transaction,
Expand Down Expand Up @@ -475,4 +475,4 @@ const EditModal = ({
);
};

export default EditModal;
export default InvoiceEditModal;
117 changes: 57 additions & 60 deletions apps/web/src/pages/[wsId]/finance/invoices/[invoiceId].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,55 +7,58 @@ import { Divider, NumberInput, Textarea } from '@mantine/core';
import { openModal } from '@mantine/modals';
import WorkspaceUserSelector from '../../../../components/selectors/WorkspaceUserSelector';
import { Product } from '../../../../types/primitives/Product';
import 'dayjs/locale/vi';
import InvoiceProductInput from '../../../../components/inputs/InvoiceProductInput';
import { useRouter } from 'next/router';
import useSWR, { mutate } from 'swr';
import { Invoice } from '../../../../types/primitives/Invoice';
import InvoiceEditModal from '../../../../components/loaders/invoices/InvoiceEditModal';
import InvoiceDeleteModal from '../../../../components/loaders/invoices/InvoiceDeleteModal';

import useSWR, { mutate } from 'swr';
import { useSegments } from '../../../../hooks/useSegments';
import { useWorkspaces } from '../../../../hooks/useWorkspaces';
import { DateTimePicker } from '@mantine/dates';
import useTranslation from 'next-translate/useTranslation';
import moment from 'moment';
import { Transaction } from '../../../../types/primitives/Transaction';
import 'dayjs/locale/vi';
import WalletSelector from '../../../../components/selectors/WalletSelector';
import TransactionCategorySelector from '../../../../components/selectors/TransactionCategorySelector';
import { Wallet } from '../../../../types/primitives/Wallet';
import TransactionCategorySelector from '../../../../components/selectors/TransactionCategorySelector';
import { TransactionCategory } from '../../../../types/primitives/TransactionCategory';
import { DateTimePicker } from '@mantine/dates';
import 'dayjs/locale/vi';
import useTranslation from 'next-translate/useTranslation';
import { useRouter } from 'next/router';
import { Invoice } from '../../../../types/primitives/Invoice';
import { Transaction } from '../../../../types/primitives/Transaction';
import moment from 'moment';
import InvoiceDeleteModal from '../../../../components/loaders/invoices/InvoiceDeleteModal';

export const getServerSideProps = enforceHasWorkspaces;

const DetailsPage: PageWithLayoutProps = () => {
const InvoiceDetailsPage: PageWithLayoutProps = () => {
const { setRootSegment } = useSegments();
const { ws } = useWorkspaces();

const router = useRouter();
const { wsId, invoiceId } = router.query;
const { invoiceId } = router.query;

const { t } = useTranslation('invoices');

const unnamedWorkspace = t('unnamed-ws');
const invoices = t('invoices');
const loading = t('common:loading');
const checkup = t('checkup');
const finance = t('finance');

const apiPath =
wsId && invoiceId
? `/api/workspaces/${wsId}/finance/invoices/${invoiceId}`
ws?.id && invoiceId
? `/api/workspaces/${ws.id}/finance/invoices/${invoiceId}`
: null;

const productsApiPath =
wsId && invoiceId
? `/api/workspaces/${wsId}/finance/invoices/${invoiceId}/products`
ws?.id && invoiceId
? `/api/workspaces/${ws.id}/finance/invoices/${invoiceId}/products`
: null;

const { data: invoice } = useSWR<Invoice>(apiPath);
const { data: productPrices } = useSWR<Product[]>(productsApiPath);

const transactionApiPath =
wsId && invoice?.transaction_id
? `/api/workspaces/${wsId}/finance/transactions/${invoice?.transaction_id}`
ws?.id && invoice?.transaction_id
? `/api/workspaces/${ws.id}/finance/transactions/${invoice?.transaction_id}`
: null;

const { data: transaction } = useSWR<Transaction>(transactionApiPath);
Expand All @@ -68,7 +71,7 @@ const DetailsPage: PageWithLayoutProps = () => {
content: ws?.name || unnamedWorkspace,
href: `/${ws.id}`,
},
{ content: checkup, href: `/${ws.id}/finance` },
{ content: finance, href: `/${ws.id}/finance` },
{
content: invoices,
href: `/${ws.id}/finance/invoices`,
Expand All @@ -89,7 +92,7 @@ const DetailsPage: PageWithLayoutProps = () => {
unnamedWorkspace,
invoices,
loading,
checkup,
finance,
]);

const [walletId, setWalletId] = useState<string | null>(null);
Expand Down Expand Up @@ -133,16 +136,16 @@ const DetailsPage: PageWithLayoutProps = () => {
}, [transaction]);

const allProductsValid = () =>
products?.every(
products.every(
(product) =>
(product?.id?.length || 0) > 0 &&
product?.id?.length > 0 &&
product?.unit_id &&
product?.amount &&
product?.price !== undefined
);

const hasRequiredFields = () =>
products && products?.length > 0 && allProductsValid();
products.length > 0 && allProductsValid() && walletId && categoryId;

const addEmptyProduct = () => {
if (!products) return;
Expand Down Expand Up @@ -171,12 +174,12 @@ const DetailsPage: PageWithLayoutProps = () => {
products.map((p, i) => (i === index ? product : p))
);

const amount = (products || []).reduce(
const amount = products.reduce(
(acc, product) => acc + (product?.amount || 0),
0
);

const price = (products || []).reduce(
const price = products.reduce(
(acc, product) => acc + (product?.price || 0) * (product?.amount || 0),
0
);
Expand Down Expand Up @@ -272,7 +275,7 @@ const DetailsPage: PageWithLayoutProps = () => {

return (
<>
<HeaderX label={`${t('invoices')} - ${t('finance')}`} />
<HeaderX label={`${invoices} - ${finance}`} />
<div className="mt-2 flex min-h-full w-full flex-col ">
<div className="grid gap-x-8 gap-y-4 xl:grid-cols-2 xl:gap-x-16">
<button
Expand Down Expand Up @@ -415,16 +418,13 @@ const DetailsPage: PageWithLayoutProps = () => {
disabled={!transaction?.category_id}
/>

{products && products?.length > 0 && (
{products?.length > 0 && (
<div className="col-span-full">
<NumberInput
label={t('total')}
placeholder={t('total-placeholder')}
value={price + (diff || 0)}
onChange={(e) => setDiff((e || 0) - price)}
classNames={{
input: 'bg-white/5 border-zinc-300/20 font-semibold',
}}
parser={(value) => value?.replace(/\$\s?|(,*)/g, '') || ''}
formatter={(value) =>
!Number.isNaN(parseFloat(value || ''))
Expand All @@ -436,15 +436,15 @@ const DetailsPage: PageWithLayoutProps = () => {
{diff != 0 && (
<>
<button
className="mt-2 w-full rounded border border-red-300/10 bg-red-300/10 px-4 py-2 font-semibold text-red-300 transition hover:bg-red-300/20 md:col-span-2"
className="mt-2 w-full rounded border border-red-500/10 bg-red-500/10 px-4 py-2 font-semibold text-red-600 transition hover:bg-red-500/20 dark:border-red-300/10 dark:bg-red-300/10 dark:text-red-300 dark:hover:bg-red-300/20 md:col-span-2"
onClick={() => setDiff(0)}
>
{t('reorder')}
{t('reset')}
</button>
<Divider className="my-2" />
<div className="my-2 rounded border border-orange-300/10 bg-orange-300/10 p-2 text-center font-semibold text-orange-300">
<div className="my-2 rounded border border-orange-500/10 bg-orange-500/10 p-2 text-center font-semibold text-orange-400 dark:border-orange-300/10 dark:bg-orange-300/10 dark:text-orange-300">
{diff > 0 ? t('extra-pay') : t('discount')}{' '}
<span className="text-orange-100 underline decoration-orange-100 underline-offset-4">
<span className="text-orange-600 underline decoration-orange-600 underline-offset-4 dark:text-orange-100 dark:decoration-orange-100">
{Intl.NumberFormat('vi-VN', {
style: 'currency',
currency: 'VND',
Expand All @@ -462,19 +462,19 @@ const DetailsPage: PageWithLayoutProps = () => {
<div className="col-span-full">
<div className="text-2xl font-semibold">
{t('products')}{' '}
{products && products?.length > 0 && (
{products?.length > 0 && (
<>
(
<span className="text-blue-300">
<span className="text-blue-600 dark:text-blue-300">
x
{Intl.NumberFormat('vi-VN', {
style: 'decimal',
}).format(products?.length || 0)}{' '}
}).format(products?.length || 0)}
</span>{' '}
{amount != 0 && (
<>
|{' '}
<span className="text-purple-300">
<span className="text-purple-600 dark:text-purple-300">
x
{Intl.NumberFormat('vi-VN', {
style: 'decimal',
Expand All @@ -483,7 +483,7 @@ const DetailsPage: PageWithLayoutProps = () => {
</>
)}
|{' '}
<span className="text-green-300">
<span className="text-green-600 dark:text-green-300">
{Intl.NumberFormat('vi-VN', {
style: 'currency',
currency: 'VND',
Expand All @@ -492,14 +492,14 @@ const DetailsPage: PageWithLayoutProps = () => {
{diff != null && diff != 0 && (
<>
{diff > 0 ? ' + ' : ' - '}{' '}
<span className="text-red-300">
<span className="text-red-600 dark:text-red-300">
{Intl.NumberFormat('vi-VN', {
style: 'currency',
currency: 'VND',
}).format(Math.abs(diff))}
</span>
{' = '}
<span className="text-yellow-300">
<span className="text-yellow-600 dark:text-yellow-300">
{Intl.NumberFormat('vi-VN', {
style: 'currency',
currency: 'VND',
Expand All @@ -526,31 +526,28 @@ const DetailsPage: PageWithLayoutProps = () => {
</button>
</div>

{products && (
<div className="mt-4 grid gap-4">
{products.map((p, idx) => (
<InvoiceProductInput
key={p.id + idx}
wsId={ws.id}
product={p}
isLast={idx === products.length - 1}
getUniqueWarehouseIds={getUniqueWarehouseIds}
removePrice={() => removePrice(idx)}
updateProduct={(product) => updateProduct(idx, product)}
hideStock
/>
))}
</div>
)}
<div className="mt-4 grid gap-4">
{products.map((p, idx) => (
<InvoiceProductInput
key={p.id + idx}
wsId={ws.id}
product={p}
isLast={idx === products.length - 1}
getUniqueWarehouseIds={getUniqueWarehouseIds}
removePrice={() => removePrice(idx)}
updateProduct={(product) => updateProduct(idx, product)}
/>
))}
</div>
</div>
</div>
</div>
</>
);
};

DetailsPage.getLayout = function getLayout(page: ReactElement) {
InvoiceDetailsPage.getLayout = function getLayout(page: ReactElement) {
return <NestedLayout noTabs>{page}</NestedLayout>;
};

export default DetailsPage;
export default InvoiceDetailsPage;
12 changes: 8 additions & 4 deletions apps/web/src/pages/[wsId]/finance/invoices/new.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import useTranslation from 'next-translate/useTranslation';

export const getServerSideProps = enforceHasWorkspaces;

const NewPage: PageWithLayoutProps = () => {
const NewInvoicePage: PageWithLayoutProps = () => {
const { setRootSegment } = useSegments();
const { ws } = useWorkspaces();

Expand Down Expand Up @@ -84,7 +84,11 @@ const NewPage: PageWithLayoutProps = () => {
product.id.length > 0 && product?.amount && product?.price !== undefined
);

const hasRequiredFields = () => products.length > 0 && allProductsValid();
const hasRequiredFields = () =>
products.length > 0 &&
allProductsValid() &&
walletId.length > 0 &&
categoryId.length > 0;

const addEmptyProduct = () => {
setProducts((products) => [
Expand Down Expand Up @@ -414,8 +418,8 @@ const NewPage: PageWithLayoutProps = () => {
);
};

NewPage.getLayout = function getLayout(page: ReactElement) {
NewInvoicePage.getLayout = function getLayout(page: ReactElement) {
return <NestedLayout noTabs>{page}</NestedLayout>;
};

export default NewPage;
export default NewInvoicePage;
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,13 @@ const TransactionDetailsPage: PageWithLayoutProps = () => {
disabled={!transactionWallet}
>
<TransactionCategorySelector
category={
transaction?.category_id
? {
id: transaction?.category_id,
}
: null
}
categoryId={transaction?.category_id}
preventPreselected
hideLabel
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@ const fetchTransactions = async (
description:
transaction?.description ||
(transaction?.transaction_categories
? transaction?.transaction_categories?.[0]?.name
? // @ts-ignore
transaction?.transaction_categories?.name ??
transaction?.transaction_categories?.[0]?.name
: ''),
wallet_id: transaction.wallet_id,
category_id: transaction.category_id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,16 @@ const fetchWallet = async (
if (!data) return res.status(404).json({ error: 'Not found' });

const newData = {
name: data.name,
description: data.description,
currency: data.currency,
balance: data.balance,
type: data.type,
report_opt_in: data.report_opt_in,

statement_date: data.credit_wallets[0]?.statement_date,
payment_date: data.credit_wallets[0]?.payment_date,
limit: data.credit_wallets[0]?.limit,
name: data?.name,
description: data?.description ?? '',
currency: data?.currency,
balance: data?.balance,
type: data?.type,
report_opt_in: data?.report_opt_in,

statement_date: data?.credit_wallets?.[0]?.statement_date,
payment_date: data?.credit_wallets?.[0]?.payment_date,
limit: data?.credit_wallets?.[0]?.limit,
} as Wallet;

return res.status(200).json(newData);
Expand Down