Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Purva/export to csv #56

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open

Purva/export to csv #56

wants to merge 5 commits into from

Conversation

dagapurva3
Copy link

@dagapurva3 dagapurva3 commented Feb 1, 2025

Notion ticket link

Export to CSV

Implementation description

  • Added a button that looks like the image added
  • Used the /api/users api to fetch all the data from the users table
  • Created a downloadable link so that the csv file gets downloaded into your system
  • Added json to csv converter so that the data inside csv can be controlled directly by the api or backend

Steps to test

  1. Build and start the application (docker compose build --up)
  2. Go to /exportUsers url, see the design of the button and the funtionality, it should download a file with all the users data.
    image
  3. Check the downloaded csv and if you are able to see the columns and the rows with proper names and data

What should reviewers focus on?

Checklist

  • My PR name is descriptive and in imperative tense
  • My commit messages are descriptive and in imperative tense. My commits are atomic and trivial commits are squashed or fixup'd into non-trivial commits
  • I have run the appropriate linter(s)
  • I have requested a review from the PL, as well as other devs who have background knowledge on this PR or who will be building on top of this PR

Copy link

vercel bot commented Feb 1, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
sistema ✅ Ready (Inspect) Visit Preview 💬 Add feedback Feb 3, 2025 3:42pm

.dockerignore Outdated
@@ -0,0 +1 @@
node_modules
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would put this as well as the changes to the .gitignore into a separate PR.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert this added line

.gitignore Outdated

# Docker
.dockerignore
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should ignore the .dockerignore.

Comment on lines 47 to 109
return (
<div style={{ textAlign: 'center' }}>
<button
onClick={handleDownload}
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
height: '46.36px',
padding: '11.273px 15.031px',
gap: '16px',
backgroundColor: '#0468C1',
color: '#FFFFFF',
fontFamily: 'Poppins, sans-serif',
fontSize: '16px',
fontWeight: 600,
border: '1px solid #D2D2D2',
borderRadius: '7px',
cursor: 'pointer',
transition: 'background 0.2s ease-in-out, transform 0.1s',
}}
onMouseOver={(e) => (e.currentTarget.style.backgroundColor = '#0358A3')}
onMouseOut={(e) => (e.currentTarget.style.backgroundColor = '#0468C1')}
onMouseDown={(e) => (e.currentTarget.style.transform = 'scale(0.95)')}
onMouseUp={(e) => (e.currentTarget.style.transform = 'scale(1)')}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="26"
height="25"
viewBox="0 0 26 25"
fill="white"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M3.7443 14.583C4.3196 14.583 4.78597 15.0494 4.78597 15.6247V19.7913C4.78597 20.0676 4.89572 20.3326 5.09107 20.5279C5.28642 20.7233 5.55137 20.833 5.82764 20.833H20.411C20.6872 20.833 20.9522 20.7233 21.1475 20.5279C21.3429 20.3326 21.4526 20.0676 21.4526 19.7913V15.6247C21.4526 15.0494 21.919 14.583 22.4943 14.583C23.0696 14.583 23.536 15.0494 23.536 15.6247V19.7913C23.536 20.6201 23.2067 21.415 22.6207 22.001C22.0346 22.5871 21.2398 22.9163 20.411 22.9163H5.82764C4.99883 22.9163 4.20398 22.5871 3.61793 22.001C3.03188 21.415 2.70264 20.6201 2.70264 19.7913V15.6247C2.70264 15.0494 3.16901 14.583 3.7443 14.583Z"
fill="white"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M7.17448 9.6801C7.58128 9.2733 8.24082 9.2733 8.64762 9.6801L13.1194 14.1519L17.5911 9.6801C17.9979 9.2733 18.6575 9.2733 19.0643 9.6801C19.4711 10.0869 19.4711 10.7464 19.0643 11.1532L13.856 16.3616C13.4492 16.7684 12.7896 16.7684 12.3828 16.3616L7.17448 11.1532C6.76769 10.7464 6.76769 10.0869 7.17448 9.6801Z"
fill="white"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M13.1193 2.08301C13.6946 2.08301 14.161 2.54938 14.161 3.12467V15.6247C14.161 16.2 13.6946 16.6663 13.1193 16.6663C12.544 16.6663 12.0776 16.2 12.0776 15.6247V3.12467C12.0776 2.54938 12.544 2.08301 13.1193 2.08301Z"
fill="white"
/>
</svg>

{/* Export Text */}
<span style={{ lineHeight: 'normal' }}>Export</span>
</button>
{error && (
<p style={{ color: 'red', marginTop: '10px', fontSize: '14px' }}>
{error}
</p>
)}
</div>
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use Chakra UI for our UI components. I would recommend something like:

Suggested change
return (
<div style={{ textAlign: 'center' }}>
<button
onClick={handleDownload}
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
height: '46.36px',
padding: '11.273px 15.031px',
gap: '16px',
backgroundColor: '#0468C1',
color: '#FFFFFF',
fontFamily: 'Poppins, sans-serif',
fontSize: '16px',
fontWeight: 600,
border: '1px solid #D2D2D2',
borderRadius: '7px',
cursor: 'pointer',
transition: 'background 0.2s ease-in-out, transform 0.1s',
}}
onMouseOver={(e) => (e.currentTarget.style.backgroundColor = '#0358A3')}
onMouseOut={(e) => (e.currentTarget.style.backgroundColor = '#0468C1')}
onMouseDown={(e) => (e.currentTarget.style.transform = 'scale(0.95)')}
onMouseUp={(e) => (e.currentTarget.style.transform = 'scale(1)')}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="26"
height="25"
viewBox="0 0 26 25"
fill="white"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M3.7443 14.583C4.3196 14.583 4.78597 15.0494 4.78597 15.6247V19.7913C4.78597 20.0676 4.89572 20.3326 5.09107 20.5279C5.28642 20.7233 5.55137 20.833 5.82764 20.833H20.411C20.6872 20.833 20.9522 20.7233 21.1475 20.5279C21.3429 20.3326 21.4526 20.0676 21.4526 19.7913V15.6247C21.4526 15.0494 21.919 14.583 22.4943 14.583C23.0696 14.583 23.536 15.0494 23.536 15.6247V19.7913C23.536 20.6201 23.2067 21.415 22.6207 22.001C22.0346 22.5871 21.2398 22.9163 20.411 22.9163H5.82764C4.99883 22.9163 4.20398 22.5871 3.61793 22.001C3.03188 21.415 2.70264 20.6201 2.70264 19.7913V15.6247C2.70264 15.0494 3.16901 14.583 3.7443 14.583Z"
fill="white"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M7.17448 9.6801C7.58128 9.2733 8.24082 9.2733 8.64762 9.6801L13.1194 14.1519L17.5911 9.6801C17.9979 9.2733 18.6575 9.2733 19.0643 9.6801C19.4711 10.0869 19.4711 10.7464 19.0643 11.1532L13.856 16.3616C13.4492 16.7684 12.7896 16.7684 12.3828 16.3616L7.17448 11.1532C6.76769 10.7464 6.76769 10.0869 7.17448 9.6801Z"
fill="white"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M13.1193 2.08301C13.6946 2.08301 14.161 2.54938 14.161 3.12467V15.6247C14.161 16.2 13.6946 16.6663 13.1193 16.6663C12.544 16.6663 12.0776 16.2 12.0776 15.6247V3.12467C12.0776 2.54938 12.544 2.08301 13.1193 2.08301Z"
fill="white"
/>
</svg>
{/* Export Text */}
<span style={{ lineHeight: 'normal' }}>Export</span>
</button>
{error && (
<p style={{ color: 'red', marginTop: '10px', fontSize: '14px' }}>
{error}
</p>
)}
</div>
);
import { Button, Box, Text } from '@chakra-ui/react';
import { DownloadIcon } from '@chakra-ui/icons';
return (
<Box>
<Button
leftIcon={<DownloadIcon />}
colorScheme="blue"
variant="solid"
onClick={handleDownload}
_hover={{ bg: 'blue.600' }}
_active={{ transform: 'scale(0.95)' }}
>
Export
</Button>
{error && (
<Text color="red.500" mt={4} fontSize="sm">
{error}
</Text>
)}
</Box>
);

But moving the imports to the top of the file.

Comment on lines 21 to 45
const handleDownload = async () => {
try {
setError(null);
const response = await fetch('/api/users');

if (!response.ok) {
throw new Error(`Failed to fetch CSV: ${response.statusText}`);
}

const data = await response.json();
const csvData = convertToCSV(data);
const blob = new Blob([csvData], { type: 'text/csv' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'users.csv';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
} catch (err) {
console.error('Error downloading CSV:', err);
setError('Failed to export users. Please try again.');
}
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't love the direct DOM manipulation and think you can do without it.

Suggested change
const handleDownload = async () => {
try {
setError(null);
const response = await fetch('/api/users');
if (!response.ok) {
throw new Error(`Failed to fetch CSV: ${response.statusText}`);
}
const data = await response.json();
const csvData = convertToCSV(data);
const blob = new Blob([csvData], { type: 'text/csv' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'users.csv';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
} catch (err) {
console.error('Error downloading CSV:', err);
setError('Failed to export users. Please try again.');
}
};
const handleDownload = async () => {
try {
setError(null);
const response = await fetch('/api/users');
if (!response.ok) {
throw new Error(`Failed to fetch CSV: ${response.statusText}`);
}
const data = await response.json();
const csvData = convertToCSV(data);
const blob = new Blob([csvData], { type: 'text/csv' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'users.csv';
link.click();
URL.revokeObjectURL(url);
} catch (err) {
console.error('Error downloading CSV:', err);
setError('Failed to export users. Please try again.');
}
};

Comment on lines 11 to 16
.map((row) =>
Object.values(row)
.map((value) => `"${value}"`)
.join(',')
)
.join('\n');
Copy link
Member

@ChinemeremChigbo ChinemeremChigbo Feb 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be nice to handle potential edge cases like quotes within the values

Suggested change
.map((row) =>
Object.values(row)
.map((value) => `"${value}"`)
.join(',')
)
.join('\n');
.map((row) =>
Object.values(row)
.map((value) => `"${String(value).replace(/"/g, '""')}"`)
.join(',')
)

.gitignore Outdated
@@ -32,4 +32,4 @@ npm-debug.log*

# typescript
*.tsbuildinfo
next-env.d.ts
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert this spacing change

.dockerignore Outdated
@@ -0,0 +1 @@
node_modules
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert this added line

Copy link
Member

@ChinemeremChigbo ChinemeremChigbo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@dagapurva3 dagapurva3 requested review from a team, 135ze and aadhyaaamashru and removed request for a team February 5, 2025 01:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants