Skip to content

Commit 3b56f39

Browse files
committed
Added exports page with first draft exports
1 parent 910a262 commit 3b56f39

File tree

6 files changed

+259
-4
lines changed

6 files changed

+259
-4
lines changed

components/nav.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,15 @@ const Nav = () => {
128128
<Link href='/issues' className="navitems">
129129
Issues
130130
</Link>
131-
{roleData?.appRole == "admin" && (<Link href='/status-of-summaries' className="navitems">
132-
Summaries
133-
</Link>)}
131+
{roleData?.appRole == "admin" && (<>
132+
<Link href='/status-of-summaries' className="navitems">
133+
Summaries
134+
</Link>
135+
<Link href='/admin-tools' className="navitems">
136+
Admin Tools
137+
</Link>
138+
</>
139+
)}
134140
</div>
135141
<div>{latestTag}</div>
136142
<div>

pages/admin-tools.tsx

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { useState } from "react";
2+
import type { NextPage } from "next";
3+
import styles from '../styles/admintools.module.css';
4+
import { exportTags, exportUsers } from '../utils/exportUtils';
5+
6+
const AdminTools: NextPage = () => {
7+
const [loading, setLoading] = useState(false);
8+
9+
const handleExport = async (entity: 'tags' | 'users', format: 'csv' | 'pdf' | 'json') => {
10+
setLoading(true);
11+
if (entity === 'tags') {
12+
await exportTags(format);
13+
} else if (entity === 'users') {
14+
await exportUsers(format);
15+
}
16+
setLoading(false);
17+
};
18+
19+
// Simplified and more scalable structure for export operations
20+
const exportOperations = [
21+
{
22+
entity: 'Tags',
23+
formats: ['csv', 'pdf', 'json'],
24+
},
25+
{
26+
entity: 'Users',
27+
formats: ['csv', 'pdf', 'json'],
28+
},
29+
// Example of how to add a new entity
30+
// {
31+
// entity: 'NewEntity',
32+
// formats: ['csv', 'pdf'],
33+
// },
34+
];
35+
36+
return (
37+
<div className={styles.container}>
38+
{!loading && (
39+
<div>
40+
<h1>Admin Tools</h1>
41+
{exportOperations.map((operation: any) => (
42+
<div key={operation.entity} className={styles.column}>
43+
<h2 className={styles.columnHeading}>{operation.entity}</h2>
44+
{operation.formats.map((format: any) => (
45+
<button
46+
key={format}
47+
className={styles.button}
48+
onClick={() => handleExport(operation.entity.toLowerCase(), format)}
49+
>
50+
Export {operation.entity} ({format.toUpperCase()})
51+
</button>
52+
))}
53+
</div>
54+
))}
55+
</div>
56+
)}
57+
</div>
58+
);
59+
};
60+
61+
export default AdminTools;

pages/api/convertToPdf.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//convertToPdf.js
12
import axios from 'axios';
23

34
export default async function handler(req, res) {

pages/issues.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const Issues: NextPage = () => {
2020
async function getIssues() {
2121
setLoading(true);
2222
const issues = await fetchIssues();
23-
console.log(issues)
23+
//console.log(issues)
2424
const open = issues.filter((issue: Issue) => issue.state === 'open');
2525
const closed = issues.filter((issue: Issue) => issue.state === 'closed');
2626
setOpenIssues(open);

styles/admintools.module.css

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/* admintools.module.css */
2+
.container {
3+
display: flex;
4+
flex-direction: column;
5+
margin-top: 4.5em; /* Leaving space for the top fixed navbar */
6+
height: calc(100vh - 4.5em);
7+
justify-content: center; /* Centers content vertically */
8+
align-items: center; /* Centers content horizontally */
9+
}
10+
11+
@media (max-width: 600px) {
12+
.container {
13+
display: flex;
14+
flex-direction: column;
15+
margin-top: 120px;
16+
align-items: center;
17+
}
18+
}
19+
20+
.heading {
21+
font-size: 2rem;
22+
font-weight: bold;
23+
margin-bottom: 1rem;
24+
}
25+
26+
.subheading {
27+
font-size: 1.5rem;
28+
margin-bottom: 2rem;
29+
}
30+
31+
.buttonContainer {
32+
display: flex;
33+
flex-wrap: wrap;
34+
justify-content: center;
35+
gap: 1rem;
36+
}
37+
38+
.button {
39+
background-color: #0070f3;
40+
color: white;
41+
border: none;
42+
padding: 0.75rem 1.5rem;
43+
font-size: 1rem;
44+
cursor: pointer;
45+
transition: background-color 0.3s ease;
46+
margin: 5px;
47+
}
48+
49+
.button:hover {
50+
background-color: #0056b3;
51+
}
52+
53+
.column-flex {
54+
display: flex;
55+
flex-direction: column;
56+
}
57+
58+
.row-flex-space-between {
59+
display: flex;
60+
flex-direction: row;
61+
justify-content: space-between;
62+
}
63+
64+
.column {
65+
display: flex;
66+
flex-direction: column;
67+
align-items: center; /* Aligns buttons and headings in the center of the column */
68+
gap: 0.5rem; /* Adjusts the space between items in the column */
69+
}
70+
71+
.columnHeading {
72+
font-size: 1.25rem; /* Adjust size as needed */
73+
margin-bottom: 1rem; /* Adds some space between the heading and the first button */
74+
}

utils/exportUtils.ts

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// exportUtils.ts
2+
import { supabase } from "../lib/supabaseClient";
3+
import axios from 'axios';
4+
5+
const formatDate = (dateString: string): string => {
6+
const date = new Date(dateString);
7+
const day = date.getDate();
8+
const months = ["January", "February", "March", "April", "May", "June",
9+
"July", "August", "September", "October", "November", "December"];
10+
const month = months[date.getMonth()];
11+
const year = date.getFullYear();
12+
13+
return `${day} ${month} ${year}`;
14+
};
15+
16+
const convertToMarkdown = (data: any[]): string => {
17+
let markdown = '';
18+
const headers = Object.keys(data[0]).join(' | ');
19+
markdown += `| ${headers} |\n| ${new Array(headers.length).fill('-').join(' | ')} |\n`;
20+
21+
data.forEach((item) => {
22+
const formattedItem = Object.entries(item).map(([key, value]) => {
23+
if (key === 'created_at') {
24+
return formatDate(value as string);
25+
}
26+
return value;
27+
});
28+
const values = formattedItem.join(' | ');
29+
markdown += `| ${values} |\n`;
30+
});
31+
32+
return markdown;
33+
};
34+
35+
const convertToCsv = (data: any[]): string => {
36+
const headers = Object.keys(data[0]).join(',');
37+
const rows = data.map((item) =>
38+
Object.entries(item).map(([key, value]) => {
39+
if (key === 'created_at') {
40+
return `"${formatDate(value as string)}"`;
41+
}
42+
return `"${value}"`;
43+
}).join(',')
44+
);
45+
return `${headers}\n${rows.join('\n')}`;
46+
};
47+
48+
const exportAsCsv = async (data: any[]) => {
49+
const csvData = convertToCsv(data);
50+
const blob = new Blob([csvData], { type: 'text/csv;charset=utf-8' });
51+
const link = document.createElement('a');
52+
link.href = URL.createObjectURL(blob);
53+
link.download = 'Export.csv'; // Consider making the filename dynamic
54+
link.click();
55+
};
56+
57+
const exportAsPdf = async (data: any[]) => {
58+
const markdown = convertToMarkdown(data);
59+
const pdfResponse = await axios.post('/api/convertToPdf', { markdown }, {
60+
responseType: 'blob',
61+
});
62+
const url = window.URL.createObjectURL(new Blob([pdfResponse.data]));
63+
const link = document.createElement('a');
64+
link.href = url;
65+
link.setAttribute('download', 'Export.pdf'); // Consider making the filename dynamic
66+
document.body.appendChild(link);
67+
link.click();
68+
link.remove();
69+
};
70+
71+
const exportAsJson = async (data: any[]) => {
72+
console.log("Json", data)
73+
};
74+
75+
const exportStrategies = {
76+
csv: exportAsCsv,
77+
pdf: exportAsPdf,
78+
json: exportAsJson,
79+
};
80+
81+
const exportData = async (format: 'csv' | 'pdf' | 'json', data: any[]) => {
82+
const exportStrategy = exportStrategies[format];
83+
if (!exportStrategy) {
84+
throw new Error(`Export format '${format}' is not supported.`);
85+
}
86+
await exportStrategy(data);
87+
};
88+
89+
export const exportTags = async (format: 'csv' | 'pdf' | 'json') => {
90+
try {
91+
const { data, error } = await supabase.from('tags').select('created_at, type, tag');
92+
if (error) {
93+
console.error('Error exporting tags:', error);
94+
return;
95+
}
96+
await exportData(format, data);
97+
} catch (error) {
98+
console.error('Error exporting tags:', error);
99+
}
100+
};
101+
102+
export const exportUsers = async (format: 'csv' | 'pdf' | 'json') => {
103+
try {
104+
const { data, error } = await supabase.from('users').select('created_at, global_name');
105+
if (error) {
106+
console.error('Error exporting users:', error);
107+
return;
108+
}
109+
await exportData(format, data);
110+
} catch (error) {
111+
console.error('Error exporting users:', error);
112+
}
113+
};

0 commit comments

Comments
 (0)