Skip to content

Commit

Permalink
Merge pull request #828 from invoiceninja/develop
Browse files Browse the repository at this point in the history
Sync develop and main
  • Loading branch information
beganovich authored Jun 23, 2023
2 parents 37510ba + 66c29b5 commit ea3ede3
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 55 deletions.
6 changes: 5 additions & 1 deletion src/components/TabGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

import classNames from 'classnames';
import { useAccentColor } from '$app/common/hooks/useAccentColor';
import React, { ReactElement, useState } from 'react';
import React, { ReactElement, useEffect, useState } from 'react';

interface Props {
children: ReactElement[];
Expand All @@ -35,6 +35,10 @@ export function TabGroup(props: Props) {
props.onTabChange?.(index);
};

useEffect(() => {
setCurrentIndex(props.defaultTabIndex || 0);
}, [props.defaultTabIndex]);

return (
<div className={props.className}>
<div className="-mb-px flex space-x-8 overflow-x-auto border-b border-gray-200">
Expand Down
46 changes: 33 additions & 13 deletions src/pages/projects/common/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
MdArchive,
MdControlPointDuplicate,
MdDelete,
MdEdit,
MdRestore,
MdTextSnippet,
} from 'react-icons/md';
Expand Down Expand Up @@ -245,9 +246,10 @@ export function useActions() {

const invoiceProject = useInvoiceProject();

const isEditPage =
location.pathname.endsWith('/edit') ||
!location.pathname.endsWith(`/projects/${id}`);
const shouldShowEditAction =
location.pathname.includes(id!) && !location.pathname.includes('/edit');

const isEditOrShowPage = location.pathname.includes(id!);

const setProject = useSetAtom(projectAtom);

Expand All @@ -261,15 +263,21 @@ export function useActions() {
toast.processing();

queryClient.fetchQuery(
route('/api/v1/tasks?project_tasks=:projectId&per_page=100', {
projectId: project.id,
}),
route(
'/api/v1/tasks?project_tasks=:projectId&per_page=100&status=active',
{
projectId: project.id,
}
),
() =>
request(
'GET',
endpoint('/api/v1/tasks?project_tasks=:projectId&per_page=100', {
projectId: project.id,
})
endpoint(
'/api/v1/tasks?project_tasks=:projectId&per_page=100&status=active',
{
projectId: project.id,
}
)
)
.then((response: GenericSingleResourceResponse<Task[]>) => {
toast.dismiss();
Expand All @@ -292,6 +300,18 @@ export function useActions() {
};

const actions = [
(project: Project) =>
shouldShowEditAction && (
<DropdownElement
onClick={() =>
navigate(route('/projects/:id/edit', { id: project.id }))
}
icon={<Icon element={MdEdit} />}
>
{t('edit')}
</DropdownElement>
),
() => shouldShowEditAction && <Divider withoutPadding />,
(project: Project) => (
<DropdownElement
onClick={() => handleInvoiceProject(project)}
Expand All @@ -308,10 +328,10 @@ export function useActions() {
{t('clone')}
</DropdownElement>
),
() => isEditPage && <Divider withoutPadding />,
() => isEditOrShowPage && <Divider withoutPadding />,
(project: Project) =>
getEntityState(project) === EntityState.Active &&
isEditPage && (
isEditOrShowPage && (
<DropdownElement
onClick={() => bulk(project.id, 'archive')}
icon={<Icon element={MdArchive} />}
Expand All @@ -322,7 +342,7 @@ export function useActions() {
(project: Project) =>
(getEntityState(project) === EntityState.Archived ||
getEntityState(project) === EntityState.Deleted) &&
isEditPage && (
isEditOrShowPage && (
<DropdownElement
onClick={() => bulk(project.id, 'restore')}
icon={<Icon element={MdRestore} />}
Expand All @@ -333,7 +353,7 @@ export function useActions() {
(project: Project) =>
(getEntityState(project) === EntityState.Active ||
getEntityState(project) === EntityState.Archived) &&
isEditPage && (
isEditOrShowPage && (
<DropdownElement
onClick={() => bulk(project.id, 'delete')}
icon={<Icon element={MdDelete} />}
Expand Down
4 changes: 3 additions & 1 deletion src/pages/projects/common/hooks/useInvoiceProject.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ export function useInvoiceProject() {

invoice.client_id = tasks.length ? tasks[0].client_id : '';

invoice.line_items = [];

tasks.forEach((task) => {
const logs = parseTimeLog(task.time_log);
const parsed: string[] = [];
Expand Down Expand Up @@ -117,7 +119,7 @@ export function useInvoiceProject() {
.join('\n')
.trim();

invoice.line_items = [item];
invoice.line_items.push(item);
});

setInvoice(invoice);
Expand Down
29 changes: 13 additions & 16 deletions src/pages/projects/show/Show.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,20 @@ import { Project } from '$app/common/interfaces/project';
import { Page } from '$app/components/Breadcrumbs';
import { InfoCard } from '$app/components/InfoCard';
import { Spinner } from '$app/components/Spinner';
import { Button, Link } from '$app/components/forms';
import { Link } from '$app/components/forms';
import { Default } from '$app/components/layouts/Default';
import { calculateTime } from '$app/pages/tasks/common/helpers/calculate-time';
import { useTranslation } from 'react-i18next';
import { useQuery } from 'react-query';
import { useParams } from 'react-router-dom';
import duration from 'dayjs/plugin/duration';
import dayjs from 'dayjs';
import { Inline } from '$app/components/Inline';
import { ResourceActions } from '$app/components/ResourceActions';
import { useActions } from '../common/hooks';
import { useActions as useProjectsActions } from '../common/hooks';
import { DataTable } from '$app/components/DataTable';
import {
defaultColumns,
useActions as useTasksActions,
useAllTaskColumns,
useTaskColumns,
useTaskFilters,
Expand Down Expand Up @@ -65,7 +65,8 @@ export default function Show() {
staleTime: Infinity,
});

const actions = useActions();
const projectActions = useProjectsActions();
const taskActions = useTasksActions();
const columns = useTaskColumns();
const formatMoney = useFormatMoney();

Expand Down Expand Up @@ -95,15 +96,11 @@ export default function Show() {
title={documentTitle}
breadcrumbs={pages}
navigationTopRight={
<Inline>
<Button to={`/projects/${id}/edit`}>{t('edit_project')}</Button>

<ResourceActions
resource={project}
label={t('more_actions')}
actions={actions}
/>
</Inline>
<ResourceActions
resource={project}
label={t('more_actions')}
actions={projectActions}
/>
}
>
<div className="grid grid-cols-3 gap-4">
Expand All @@ -129,8 +126,8 @@ export default function Show() {
{t('task_rate')}:
{formatMoney(
project.task_rate,
project.client!.country_id,
project.client!.settings.currency_id
project.client?.country_id || '',
project.client?.settings.currency_id
)}
</p>
</InfoCard>
Expand All @@ -154,7 +151,7 @@ export default function Show() {
<DataTable
resource="task"
columns={columns}
customActions={actions}
customActions={taskActions}
endpoint={`/api/v1/tasks?include=status,client,project&sort=id|desc&project_tasks=${project.id}`}
bulkRoute="/api/v1/tasks/bulk"
linkToCreate={`/tasks/create?project=${id}`}
Expand Down
69 changes: 49 additions & 20 deletions src/pages/settings/gateways/create/Create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
useCompanyGatewaysQuery,
} from '$app/common/queries/company-gateways';
import { Settings } from '$app/components/layouts/Settings';
import { ChangeEvent, useEffect, useState } from 'react';
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useGateways } from '../common/hooks/useGateways';
import { Credentials } from './components/Credentials';
Expand All @@ -35,12 +35,29 @@ import {
} from '$app/pages/clients/show/components/GatewayTypeIcon';

const gatewaysStyles = [
{ key: 'paypal_express', width: 110 },
{ key: 'mollie', width: 110 },
{ key: 'eway', width: 170 },
{ key: 'forte', width: 190 },
{ key: 'square', width: 130 },
{ key: 'checkoutcom', width: 170 },
{ name: 'paypal_express', width: 110 },
{ name: 'mollie', width: 110 },
{ name: 'eway', width: 170 },
{ name: 'forte', width: 190 },
{ name: 'square', width: 130 },
{ name: 'checkoutcom', width: 170 },
];

const gatewaysDetails = [
{ name: 'stripe', key: 'd14dd26a37cecc30fdd65700bfb55b23' },
{ name: 'stripe', key: 'd14dd26a47cecc30fdd65700bfb67b34' },
{ name: 'braintree', key: 'f7ec488676d310683fb51802d076d713' },
{ name: 'paypal_express', key: '38f2c48af60c7dd69e04248cbb24c36e' },
{ name: 'authorize', key: '3b6621f970ab18887c4f6dca78d3f8bb' },
{ name: 'mollie', key: '1bd651fb213ca0c9d66ae3c336dc77e8' },
{ name: 'gocardless', key: 'b9886f9257f0c6ee7c302f1c74475f6c' },
{ name: 'forte', key: 'kivcvjexxvdiyqtj3mju5d6yhpeht2xs' },
{ name: 'razorpay', key: 'hxd6gwg3ekb9tb3v9lptgx1mqyg69zu9' },
{ name: 'square', key: '65faab2ab6e3223dbe848b1686490baz' },
{ name: 'paytrace', key: 'bbd736b3254b0aabed6ad7fda1298c88' },
{ name: 'checkoutcom', key: '3758e7f7c6f4cecf0f4f348b9a00f456' },
{ name: 'payfast', key: 'd6814fc83f45d2935e7777071e629ef9' },
{ name: 'eway', key: '944c20175bbe6b9972c05bcfe294c2c7' },
];

export function Create() {
Expand Down Expand Up @@ -68,8 +85,10 @@ export function Create() {

const onSave = useHandleCreate(companyGateway, setErrors);

const handleChange = (value: string) => {
const handleChange = (value: string, isManualChange?: boolean) => {
setGateway(gateways.find((gateway) => gateway.id === value));

isManualChange && setTabIndex(1);
};

const defaultTab = [t('provider')];
Expand All @@ -89,11 +108,15 @@ export function Create() {

const [tabs, setTabs] = useState<string[]>(defaultTab);

const getGatewayWidth = (provider: string) => {
const providerName = provider.toLowerCase();
const getGatewayNameByKey = (key: string) => {
const gateway = gatewaysDetails.find((gateway) => gateway.key === key);

return gateway?.name || '';
};

const getGatewayWidth = (gatewayName: string) => {
const gateway = gatewaysStyles.find(
(gateway) => gateway.key === providerName
(gateway) => gateway.name === gatewayName
);

return gateway ? gateway.width : undefined;
Expand Down Expand Up @@ -173,7 +196,7 @@ export function Create() {

useEffect(() => {
if (createBySetup) {
onSave();
onSave(1);
setCreateBySetup(false);
}
}, [companyGateway]);
Expand All @@ -182,14 +205,18 @@ export function Create() {
<Settings
title={documentTitle}
breadcrumbs={pages}
onSaveClick={onSave}
onSaveClick={() => onSave(1)}
disableSaveButton={!gateway}
>
<TabGroup tabs={tabs} onTabChange={(index) => setTabIndex(index)}>
<TabGroup
tabs={tabs}
defaultTabIndex={tabIndex}
onTabChange={(index) => setTabIndex(index)}
>
<Card title={t('add_gateway')}>
<Element leftSide={t('provider')}>
<SelectField
onValueChange={(value) => handleChange(value)}
onValueChange={(value) => handleChange(value, true)}
value={gateway?.id}
errorMessage={errors?.errors.gateway_key}
withBlank
Expand Down Expand Up @@ -252,15 +279,17 @@ export function Create() {
{filteredGateways.map(
(gateway, index) =>
availableGatewayLogos.includes(
gateway.provider.toLowerCase()
getGatewayNameByKey(gateway.key)
) && (
<Card key={index} className="w-52">
<div className="flex flex-col items-center justify-between space-y-5 h-52">
<div className="flex justify-center items-center border-b border-b-gray-200 w-full h-28">
<GatewayTypeIcon
name={gateway.provider.toLowerCase()}
name={getGatewayNameByKey(gateway.key)}
style={{
width: getGatewayWidth(gateway.provider) || 150,
width:
getGatewayWidth(getGatewayNameByKey(gateway.key)) ||
150,
}}
/>
</div>
Expand All @@ -272,8 +301,8 @@ export function Create() {
)}

<Button
onClick={(event: ChangeEvent<HTMLButtonElement>) => {
event.preventDefault();
behavior="button"
onClick={() => {
setCreateBySetup(true);
handleChange(gateway.id);
}}
Expand Down
5 changes: 3 additions & 2 deletions src/pages/settings/gateways/create/hooks/useHandleCreate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export function useHandleCreate(
const queryClient = useQueryClient();
const invalidateQueryValue = useAtomValue(invalidationQueryAtom);

return () => {
return (defaultTabIndex: number) => {
toast.processing();

setErrors(undefined);
Expand All @@ -47,8 +47,9 @@ export function useHandleCreate(
toast.success('created_company_gateway');

navigate(
route('/settings/gateways/:id/edit', {
route('/settings/gateways/:id/edit?tab=:defaultTabIndex', {
id: response.data.data.id,
defaultTabIndex,
})
);
})
Expand Down
Loading

0 comments on commit ea3ede3

Please sign in to comment.