Skip to content

Commit

Permalink
Proposals tables export (#5060)
Browse files Browse the repository at this point in the history
* ADded export to csv for my work tables

* Initial export space proposals database

* Fixed username issue

* Fixed proposals export

* Fixed broken type issues

* refactor to export proposals

* Renamed work to my-work

* Fixed broken type issues

* Refactored export my proposals

* Fixed issue with view block not created

* Added url column for my proposals

* Move export proposals button to sidebar
  • Loading branch information
Devorein authored Nov 23, 2024
1 parent 4754bdf commit b769282
Show file tree
Hide file tree
Showing 14 changed files with 359 additions and 18 deletions.
8 changes: 8 additions & 0 deletions charmClient/apis/proposalsApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,12 @@ export class ProposalsApi {
exportProposalsReviewers({ spaceId }: { spaceId: string }) {
return http.GET<string>(`/api/spaces/${spaceId}/proposals/reviewers/export`);
}

exportUserProposals({ spaceId }: { spaceId: string }) {
return http.GET<string>(`/api/spaces/${spaceId}/proposals/my-work/export`);
}

exportFilteredProposals({ spaceId }: { spaceId: string }) {
return http.GET<string>(`/api/spaces/${spaceId}/proposals/export`);
}
}
2 changes: 1 addition & 1 deletion charmClient/hooks/proposals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ export function useResetEvaluationAppealReview({ evaluationId }: { evaluationId:
}

export function useGetUserProposals(params: { spaceId: MaybeString }) {
return useGET<GetUserProposalsResponse>(params.spaceId ? `/api/spaces/${params.spaceId}/proposals/work` : null);
return useGET<GetUserProposalsResponse>(params.spaceId ? `/api/spaces/${params.spaceId}/proposals/my-work` : null);
}

export function useGetProposalsReviewers(params: { spaceId: MaybeString }) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import type { PageMeta } from '@charmverse/core/pages';
import { ClickAwayListener, Collapse } from '@mui/material';
import type { Dispatch, SetStateAction } from 'react';
import { memo, useEffect, useState } from 'react';
import { memo, useCallback, useEffect, useState } from 'react';

import charmClient from 'charmClient';
import { Button } from 'components/common/Button';
import { useCurrentSpace } from 'hooks/useCurrentSpace';
import type { Board, IPropertyTemplate } from 'lib/databases/board';
import type { BoardView, IViewType } from 'lib/databases/boardView';
import type { Card } from 'lib/databases/card';
Expand Down Expand Up @@ -38,6 +41,7 @@ interface Props {
selectedPropertyId: string | null;
setSelectedPropertyId: Dispatch<SetStateAction<string | null>>;
sidebarView?: SidebarView;
isProposal?: boolean;
isReward?: boolean;
}

Expand All @@ -50,6 +54,24 @@ function ViewSidebar(props: Props) {
function goToSidebarHome() {
setSidebarView('view-options');
}
const { space: currentSpace } = useCurrentSpace();

const exportToCSV = useCallback(() => {
if (currentSpace) {
charmClient.proposals
.exportFilteredProposals({
spaceId: currentSpace.id
})
.then((csvContent) => {
const blob = new Blob([csvContent], { type: 'text/csv' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'Proposals.csv';
a.click();
});
}
}, [currentSpace?.id]);

Check warning on line 74 in components/common/DatabaseEditor/components/viewSidebar/viewSidebar.tsx

View workflow job for this annotation

GitHub Actions / Test

React Hook useCallback has a missing dependency: 'currentSpace'. Either include it or remove the dependency array

Check warning on line 74 in components/common/DatabaseEditor/components/viewSidebar/viewSidebar.tsx

View workflow job for this annotation

GitHub Actions / Test

React Hook useCallback has a missing dependency: 'currentSpace'. Either include it or remove the dependency array

Check warning on line 74 in components/common/DatabaseEditor/components/viewSidebar/viewSidebar.tsx

View workflow job for this annotation

GitHub Actions / Test apps

React Hook useCallback has a missing dependency: 'currentSpace'. Either include it or remove the dependency array

Check warning on line 74 in components/common/DatabaseEditor/components/viewSidebar/viewSidebar.tsx

View workflow job for this annotation

GitHub Actions / Test apps

React Hook useCallback has a missing dependency: 'currentSpace'. Either include it or remove the dependency array

Check warning on line 74 in components/common/DatabaseEditor/components/viewSidebar/viewSidebar.tsx

View workflow job for this annotation

GitHub Actions / Test apps

React Hook useCallback has a missing dependency: 'currentSpace'. Either include it or remove the dependency array

Check warning on line 74 in components/common/DatabaseEditor/components/viewSidebar/viewSidebar.tsx

View workflow job for this annotation

GitHub Actions / Test app

React Hook useCallback has a missing dependency: 'currentSpace'. Either include it or remove the dependency array

Check warning on line 74 in components/common/DatabaseEditor/components/viewSidebar/viewSidebar.tsx

View workflow job for this annotation

GitHub Actions / Validate code

React Hook useCallback has a missing dependency: 'currentSpace'. Either include it or remove the dependency array

useEffect(() => {
if (!props.isOpen) {
Expand Down Expand Up @@ -148,6 +170,11 @@ function ViewSidebar(props: Props) {
isReward={props.isReward}
/>
)}
{props.isProposal && (
<Button variant='outlined' size='small' onClick={exportToCSV} sx={{ my: 1, mx: 2 }}>
Export to CSV
</Button>
)}
</StyledSidebar>
</Collapse>
</ClickAwayListener>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import { Apps } from '@mui/icons-material';
import FormatListBulletedIcon from '@mui/icons-material/FormatListBulleted';
import GroupIcon from '@mui/icons-material/GroupWorkOutlined';
import ArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
import PreviewIcon from '@mui/icons-material/Preview';
import TaskOutlinedIcon from '@mui/icons-material/TaskOutlined';
import { ListItemIcon, ListItemText, MenuItem, Typography } from '@mui/material';
import { capitalize } from '@root/lib/utils/strings';
import { usePopupState } from 'material-ui-popup-state/hooks';
import { FcGoogle } from 'react-icons/fc';
import { RiFolder2Line } from 'react-icons/ri';

import { Button } from 'components/common/Button';
import { PageIcon } from 'components/common/PageIcon';
import { usePages } from 'hooks/usePages';
import type { Board, DataSourceType, IPropertyTemplate } from 'lib/databases/board';
Expand Down Expand Up @@ -101,7 +98,6 @@ export function ViewSidebarSelect({
hidePropertiesRow
}: Props) {
const { pages } = usePages();

const withGroupBy = view?.fields.viewType.match(/board/) || view?.fields.viewType === 'table';
const currentGroup = board?.fields.cardProperties.find((prop) => prop.id === groupByProperty?.id)?.name;
const currentLayout = view?.fields.viewType;
Expand Down
2 changes: 1 addition & 1 deletion components/common/DatabaseEditor/store/cards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ export function sortCards(
cards: Card[],
board: Pick<Board, 'fields'>,
activeView: BoardView,
members: Record<string, Member>,
members: Record<string, Pick<Member, 'username'>>,
cardTitles: Record<string, { title: string }>,
localSort?: ISortOption[] | null
): Card[] {
Expand Down
2 changes: 2 additions & 0 deletions components/proposals/ProposalsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { debounce } from 'lodash';
import { useCallback, useMemo, useState } from 'react';
import { FormattedMessage } from 'react-intl';

import charmClient from 'charmClient';
import { useTrashPages } from 'charmClient/hooks/pages';
import { Button } from 'components/common/Button';
import { CharmEditor } from 'components/common/CharmEditor';
Expand Down Expand Up @@ -368,6 +369,7 @@ export function ProposalsPage({ title }: { title: string }) {
closeSidebar={() => {
setShowSidebar(false);
}}
isProposal
hideLayoutOptions
hideSourceOptions
hideGroupOptions
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,62 @@
import { ThumbUpOutlined as ApprovedIcon, HighlightOff as RejectedIcon } from '@mui/icons-material';
import ProposalIcon from '@mui/icons-material/TaskOutlined';
import { Box, Button, Card, TableBody, TableCell, TableHead, TableRow, Typography } from '@mui/material';
import { Box, Card, TableBody, TableCell, TableHead, TableRow, Typography } from '@mui/material';
import { Stack } from '@mui/system';
import type { CustomColumn, UserProposal } from '@root/lib/proposals/getUserProposals';
import { useRouter } from 'next/router';
import { useCallback } from 'react';

import charmClient from 'charmClient';
import { Button } from 'components/common/Button';
import Link from 'components/common/Link';
import { evaluationIcons } from 'components/settings/proposals/constants';
import { useCurrentSpace } from 'hooks/useCurrentSpace';

import { CustomColumnTableCells } from './CustomColumnTableCells';
import { OpenButton, StyledTable, StyledTableRow } from './ProposalsTable';

export function ActionableProposalsTable({
proposals,
customColumns
customColumns,
totalProposals
}: {
proposals: UserProposal[];
customColumns: CustomColumn[];
totalProposals: number;
}) {
const { space } = useCurrentSpace();

const router = useRouter();

const exportToCSV = useCallback(() => {
if (space) {
charmClient.proposals.exportUserProposals({ spaceId: space.id }).then((csvContent) => {
const blob = new Blob([csvContent], { type: 'text/csv' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'My Proposals.csv';
a.click();
});
}
}, [!!space?.id]);

Check warning on line 42 in components/proposals/components/UserProposalsTables/ActionableProposalsTable.tsx

View workflow job for this annotation

GitHub Actions / Test

React Hook useCallback has a missing dependency: 'space'. Either include it or remove the dependency array

Check warning on line 42 in components/proposals/components/UserProposalsTables/ActionableProposalsTable.tsx

View workflow job for this annotation

GitHub Actions / Test

React Hook useCallback has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked

Check warning on line 42 in components/proposals/components/UserProposalsTables/ActionableProposalsTable.tsx

View workflow job for this annotation

GitHub Actions / Test

React Hook useCallback has a missing dependency: 'space'. Either include it or remove the dependency array

Check warning on line 42 in components/proposals/components/UserProposalsTables/ActionableProposalsTable.tsx

View workflow job for this annotation

GitHub Actions / Test

React Hook useCallback has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked

Check warning on line 42 in components/proposals/components/UserProposalsTables/ActionableProposalsTable.tsx

View workflow job for this annotation

GitHub Actions / Test apps

React Hook useCallback has a missing dependency: 'space'. Either include it or remove the dependency array

Check warning on line 42 in components/proposals/components/UserProposalsTables/ActionableProposalsTable.tsx

View workflow job for this annotation

GitHub Actions / Test apps

React Hook useCallback has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked

Check warning on line 42 in components/proposals/components/UserProposalsTables/ActionableProposalsTable.tsx

View workflow job for this annotation

GitHub Actions / Test apps

React Hook useCallback has a missing dependency: 'space'. Either include it or remove the dependency array

Check warning on line 42 in components/proposals/components/UserProposalsTables/ActionableProposalsTable.tsx

View workflow job for this annotation

GitHub Actions / Test apps

React Hook useCallback has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked

Check warning on line 42 in components/proposals/components/UserProposalsTables/ActionableProposalsTable.tsx

View workflow job for this annotation

GitHub Actions / Test apps

React Hook useCallback has a missing dependency: 'space'. Either include it or remove the dependency array

Check warning on line 42 in components/proposals/components/UserProposalsTables/ActionableProposalsTable.tsx

View workflow job for this annotation

GitHub Actions / Test apps

React Hook useCallback has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked

Check warning on line 42 in components/proposals/components/UserProposalsTables/ActionableProposalsTable.tsx

View workflow job for this annotation

GitHub Actions / Test app

React Hook useCallback has a missing dependency: 'space'. Either include it or remove the dependency array

Check warning on line 42 in components/proposals/components/UserProposalsTables/ActionableProposalsTable.tsx

View workflow job for this annotation

GitHub Actions / Test app

React Hook useCallback has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked

Check warning on line 42 in components/proposals/components/UserProposalsTables/ActionableProposalsTable.tsx

View workflow job for this annotation

GitHub Actions / Validate code

React Hook useCallback has a missing dependency: 'space'. Either include it or remove the dependency array

Check warning on line 42 in components/proposals/components/UserProposalsTables/ActionableProposalsTable.tsx

View workflow job for this annotation

GitHub Actions / Validate code

React Hook useCallback has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked

return (
<Stack gap={1}>
<Typography variant='h6' fontWeight='bold'>
Ready for review
</Typography>
<Stack flexDirection='row' justifyContent='space-between' alignItems='center'>
<Typography variant='h6' fontWeight='bold'>
Ready for review
</Typography>
<Button
variant='outlined'
size='small'
onClick={exportToCSV}
disabled={totalProposals === 0}
disabledTooltip='No proposals to export'
>
Export to CSV
</Button>
</Stack>
{proposals.length ? (
<StyledTable>
<TableHead>
Expand Down Expand Up @@ -179,7 +211,7 @@ export function ActionableProposalsTable({
color='primary'
size='small'
sx={{ mr: 2, width: 75 }}
onClick={(e) => {
onClick={(e: MouseEvent) => {
e.stopPropagation();
router.push(`/${router.query.domain}/${proposal.path}`);
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ export function UserProposalsTables() {
</Grid>
) : (
<Stack gap={3}>
<ActionableProposalsTable proposals={proposals.actionable} customColumns={proposals?.customColumns} />
<ActionableProposalsTable
proposals={proposals.actionable}
customColumns={proposals?.customColumns}
totalProposals={proposals.actionable.length + proposals.authored.length + proposals.review_completed.length}
/>
{proposals.authored.length ? (
<ProposalsTable
proposals={proposals.authored}
Expand Down
63 changes: 63 additions & 0 deletions lib/proposals/exportMyProposals.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { prisma } from '@charmverse/core/prisma-client';
import { baseUrl } from '@root/config/constants';

import { getUserProposals } from './getUserProposals';

export async function exportMyProposals({ spaceId, userId }: { spaceId: string; userId: string }) {
const space = await prisma.space.findUniqueOrThrow({
where: {
id: spaceId
},
select: {
domain: true
}
});
const userProposals = await getUserProposals({ spaceId, userId });

let csvContent = '';
const rows: string[][] = [
[
'Title',
'Status',
'Current step',
'Your review',
'Approved',
'Declined',
'URL',
...(userProposals.customColumns?.map((column) => column.title) ?? [])
]
];

const allProposals = [...userProposals.actionable, ...userProposals.authored, ...userProposals.review_completed];

allProposals.forEach((proposal) => {
const row = [
proposal.title || 'Untitled',
proposal.currentEvaluation?.result
? proposal.currentEvaluation.result === 'pass'
? 'Passed'
: 'Declined'
: 'In progress',
proposal.status === 'draft' ? 'Draft' : proposal.currentEvaluation?.title || 'Evaluation',
proposal.userReviewResult || '-',
proposal.totalPassedReviewResults?.toString() || '-',
proposal.totalFailedReviewResults?.toString() || '-',
`${baseUrl}/${space.domain}/${proposal.path}`,
...(userProposals.customColumns?.map((column) => {
const customValue = proposal.customColumns?.find((c) => c.formFieldId === column.formFieldId)?.value;
if (column.type === 'select' || column.type === 'multiselect') {
return column.options.find((opt) => opt.id === customValue)?.name || '-';
}
return (customValue as string) || '-';
}) ?? [])
];
rows.push(row);
});

rows.forEach((row) => {
const encodedRow = row.join('\t');
csvContent += `${encodedRow}\r\n`;
});

return csvContent;
}
Loading

0 comments on commit b769282

Please sign in to comment.