Skip to content

Commit

Permalink
Merge pull request #655 from invoiceninja/develop
Browse files Browse the repository at this point in the history
Sync develop with main
  • Loading branch information
beganovich authored Apr 25, 2023
2 parents 55397ad + f2a3ff5 commit af859fc
Show file tree
Hide file tree
Showing 95 changed files with 1,265 additions and 1,125 deletions.
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"typescript.preferences.importModuleSpecifier": "non-relative"
"typescript.preferences.importModuleSpecifier": "shortest"
}
19 changes: 15 additions & 4 deletions src/common/generic/DesignSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,13 @@ import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useQueryClient } from 'react-query';

export function DesignSelector(props: GenericSelectorProps<Design>) {
interface Props extends GenericSelectorProps<Design> {
actionVisibility?: boolean;
}

export function DesignSelector(props: Props) {
const defaultProps: Props = { actionVisibility: true, ...props };

const [isModalVisible, setIsModalVisible] = useState(false);
const [design, setDesign] = useState<Design | null>(null);
const [errors, setErrors] = useState<ValidationBag | null>(null);
Expand Down Expand Up @@ -77,6 +83,12 @@ export function DesignSelector(props: GenericSelectorProps<Design>) {
}
};

const actionProps: { onActionClick?: () => void } = {};

if (defaultProps.actionVisibility !== false) {
actionProps.onActionClick = () => setIsModalVisible(true);
}

return (
<>
<Modal
Expand Down Expand Up @@ -122,16 +134,15 @@ export function DesignSelector(props: GenericSelectorProps<Design>) {

<DebouncedCombobox
{...props}
{...actionProps}
value="id"
endpoint="/api/v1/designs"
label="name"
defaultValue={props.value}
onChange={(design: Record<Design>) => {
design.resource && props.onChange(design.resource);
}
}
}}
actionLabel={t('new_design')}
onActionClick={() => setIsModalVisible(true)}
/>
</>
);
Expand Down
1 change: 1 addition & 0 deletions src/common/interfaces/schedule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
export interface Parameters {
date_range: string;
show_payments_table: boolean;
show_credits_table: boolean;
show_aging_table: boolean;
status: string;
clients: string[];
Expand Down
22 changes: 15 additions & 7 deletions src/components/Tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,31 @@
* @license https://www.elastic.co/licensing/elastic-license
*/

import { route } from '$app/common/helpers/route';
import { useAccentColor } from '$app/common/hooks/useAccentColor';
import { MouseEvent, useRef } from 'react';
import { Link, useLocation } from 'react-router-dom';
import { Link, Params, useLocation, useParams } from 'react-router-dom';

interface Props {
className?: string;
tabs: Tab[];
}

export type Tab = { name: string; href: string };
export type Tab = { name: string; href: string; matcher?: Matcher[] };
export type Matcher = (params: Readonly<Params<string>>) => string;

export function Tabs(props: Props) {
const accentColor = useAccentColor();
const location = useLocation();
const params = useParams();

const isActive = (link: string) => {
return location.pathname === link;
const isActive = (tab: Tab) => {
return (
location.pathname === tab.href ||
tab.matcher?.some(
(matcher) => matcher(params) === route(location.pathname, params)
)
);
};

const tabBar = useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -76,11 +84,11 @@ export function Tabs(props: Props) {
to={tab.href}
onClick={(event) => handleScroll(event)}
style={{
borderColor: isActive(tab.href) ? accentColor : 'transparent',
color: isActive(tab.href) ? accentColor : '#6B7280',
borderColor: isActive(tab) ? accentColor : 'transparent',
color: isActive(tab) ? accentColor : '#6B7280',
}}
className="whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm"
aria-current={isActive(tab.href) ? 'page' : undefined}
aria-current={isActive(tab) ? 'page' : undefined}
>
{tab.name}
</Link>
Expand Down
2 changes: 1 addition & 1 deletion src/components/cards/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export function Card(props: Props) {
return (
<div
className={classNames(
`bg-white shadow overflow-hidden rounded ${props.className}`,
`bg-white shadow rounded ${props.className} overflow-visible`,
{ 'overflow-y-auto': props.withScrollableBody }
)}
style={props.style}
Expand Down
87 changes: 52 additions & 35 deletions src/components/layouts/Default.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import { useCurrentCompanyUser } from '$app/common/hooks/useCurrentCompanyUser';
import { useEnabled } from '$app/common/guards/guards/enabled';
import { Dropdown } from '$app/components/dropdown/Dropdown';
import { DropdownElement } from '$app/components/dropdown/DropdownElement';
import { useSaveBtn } from '$app/components/layouts/common/hooks';

export interface SaveOption {
label: string;
Expand Down Expand Up @@ -355,6 +356,7 @@ export function Default(props: Props) {
];

const { isOwner } = useAdmin();
const saveBtn = useSaveBtn();

return (
<>
Expand Down Expand Up @@ -423,47 +425,62 @@ export function Default(props: Props) {
</Button>
)}

{props.onSaveClick && (
{(props.onSaveClick || saveBtn) && (
<div>
{props.onSaveClick && !props.additionalSaveOptions && (
<Button
onClick={props.onSaveClick}
disabled={props.disableSaveButton}
disableWithoutIcon
>
{props.saveButtonLabel ?? t('save')}
</Button>
)}

{props.onSaveClick && props.additionalSaveOptions && (
<div className="flex">
{(props.onSaveClick || saveBtn?.onClick) &&
!props.additionalSaveOptions && (
<Button
className="rounded-br-none rounded-tr-none px-3"
onClick={props.onSaveClick}
disabled={props.disableSaveButton}
onClick={saveBtn?.onClick || props.onSaveClick}
disabled={
saveBtn?.disableSaveButton ||
props.disableSaveButton
}
disableWithoutIcon
>
{props.saveButtonLabel ?? t('save')}
{(saveBtn?.label || props.saveButtonLabel) ??
t('save')}
</Button>
)}

<Dropdown
className="rounded-bl-none rounded-tl-none h-full px-1 border-gray-200 border-l-1 border-y-0 border-r-0"
cardActions
disabled={props.disableSaveButton}
>
{props.additionalSaveOptions.map((option, index) => (
<DropdownElement
key={index}
icon={option.icon}
disabled={props.disableSaveButton}
onClick={option.onClick}
>
{option.label}
</DropdownElement>
))}
</Dropdown>
</div>
)}
{(props.onSaveClick || saveBtn?.onClick) &&
props.additionalSaveOptions && (
<div className="flex">
<Button
className="rounded-br-none rounded-tr-none px-3"
onClick={saveBtn?.onClick || props.onSaveClick}
disabled={
saveBtn?.disableSaveButton ||
props.disableSaveButton
}
disableWithoutIcon
>
{(saveBtn?.label || props.saveButtonLabel) ??
t('save')}
</Button>

<Dropdown
className="rounded-bl-none rounded-tl-none h-full px-1 border-gray-200 border-l-1 border-y-0 border-r-0"
cardActions
disabled={
saveBtn?.disableSaveButton ||
props.disableSaveButton
}
>
{props.additionalSaveOptions.map(
(option, index) => (
<DropdownElement
key={index}
icon={option.icon}
disabled={props.disableSaveButton}
onClick={option.onClick}
>
{option.label}
</DropdownElement>
)
)}
</Dropdown>
</div>
)}
</div>
)}

Expand Down
36 changes: 30 additions & 6 deletions src/components/layouts/common/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
*/

import { useAdmin } from '$app/common/hooks/permissions/useHasPermission';
import { atom, useAtom } from 'jotai';
import { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { useLocation } from 'react-router-dom';

Expand Down Expand Up @@ -97,12 +99,10 @@ export function useSettingsRoutes() {

const advanced: SettingsRoute[] = [
{
name: t('customize_and_preview'),
href: '/settings/invoice_design/customize',
current: location.pathname.startsWith(
'/settings/invoice_design/customize'
),
enabled: isAdmin || isOwner || false,
name: t('invoice_design'),
href: '/settings/invoice_design',
current: location.pathname.endsWith('/settings/invoice_design'),
enabled: isAdmin || isOwner || false
},
{
name: t('generated_numbers'),
Expand Down Expand Up @@ -164,3 +164,27 @@ export function useSettingsRoutes() {

return { basic, advanced };
}

interface SaveButton {
onClick: () => unknown;
label?: string;
disableSaveButton?: boolean;
}

export const saveBtnAtom = atom<SaveButton | null>(null);

export function useSaveBtn(options?: SaveButton, deps: unknown[] = []) {
const [saveBtn, setSaveBtn] = useAtom(saveBtnAtom);

useEffect(() => {
if (options) {
setSaveBtn(options);
}

return () => {
setSaveBtn(null);
};
}, deps);

return saveBtn;
}
1 change: 1 addition & 0 deletions src/pages/clients/common/hooks/useScheduleStatement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export function useScheduleStatement() {
setScheduleParameters({
clients: [statement.client_id],
show_aging_table: statement.show_aging_table,
show_credits_table: statement.show_credits_table,
show_payments_table: statement.show_payments_table,
status: statement.status,
date_range:
Expand Down
8 changes: 6 additions & 2 deletions src/pages/clients/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,16 @@ const Edit = lazy(() => import('$app/pages/clients/edit/Edit'));
const Client = lazy(() => import('$app/pages/clients/show/Client'));
const Quotes = lazy(() => import('$app/pages/clients/show/pages/Quotes'));
const Payments = lazy(() => import('$app/pages/clients/show/pages/Payments'));
const RecurringInvoices = lazy(() => import('$app/pages/clients/show/pages/RecurringInvoices'));
const RecurringInvoices = lazy(
() => import('$app/pages/clients/show/pages/RecurringInvoices')
);
const Credits = lazy(() => import('$app/pages/clients/show/pages/Credits'));
const Projects = lazy(() => import('$app/pages/clients/show/pages/Projects'));
const Tasks = lazy(() => import('$app/pages/clients/show/pages/Tasks'));
const Expenses = lazy(() => import('$app/pages/clients/show/pages/Expenses'));
const RecurringExpenses = lazy(() => import('$app/pages/clients/show/pages/RecurringExpenses'));
const RecurringExpenses = lazy(
() => import('$app/pages/clients/show/pages/RecurringExpenses')
);
const Statement = lazy(() => import('$app/pages/clients/statement/Statement'));
const Invoices = lazy(() => import('$app/pages/clients/show/pages/Invoices'));

Expand Down
41 changes: 3 additions & 38 deletions src/pages/clients/show/pages/Credits.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,51 +8,16 @@
* @license https://www.elastic.co/licensing/elastic-license
*/

import { Link } from '$app/components/forms';
import creditStatus from '$app/common/constants/credit-status';
import { date } from '$app/common/helpers';
import { route } from '$app/common/helpers/route';
import { useCurrentCompanyDateFormats } from '$app/common/hooks/useCurrentCompanyDateFormats';
import { Credit } from '$app/common/interfaces/credit';
import { DataTable, DataTableColumns } from '$app/components/DataTable';
import { StatusBadge } from '$app/components/StatusBadge';
import { useTranslation } from 'react-i18next';
import { DataTable } from '$app/components/DataTable';
import { useParams } from 'react-router-dom';
import { dataTableStaleTime } from './Invoices';
import { useCreditColumns } from '$app/pages/credits/common/hooks';

export default function Credits() {
const [t] = useTranslation();
const { id } = useParams();
const { dateFormat } = useCurrentCompanyDateFormats();

const columns: DataTableColumns<Credit> = [
{
id: 'number',
label: t('number'),
format: (value, credit) => (
<Link to={route('/credits/:id/edit', { id: credit.id })}>{value}</Link>
),
},
{
id: 'status_id',
label: t('status'),
format: (value) => <StatusBadge for={creditStatus} code={value} />,
},
{
id: 'amount',
label: t('amount'),
format: (value) => value.toString().toUpperCase(),
},
{
id: 'date',
label: t('date'),
format: (value) => date(value, dateFormat),
},
{
id: 'amount',
label: t('remaining'),
},
];
const columns = useCreditColumns();

return (
<DataTable
Expand Down
Loading

0 comments on commit af859fc

Please sign in to comment.