Skip to content

Commit

Permalink
feat: WIP frontend
Browse files Browse the repository at this point in the history
  • Loading branch information
SebassNoob committed May 6, 2024
1 parent 57da19e commit 5bdfc34
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@ import { APIClient } from '@api/api_client';
import { Service } from '@/app/services/types';
import { use, useMemo, useState } from 'react';
import { parseServerError } from '@utils/.';
import './styles.css';
import { ExportsCard, downloadFile, type DownloadFileHeaders } from '../ExportsCard/ExportsCard';

type ExportsProps = {
type AttendanceExportsProps = {
names: string[];
range: [Date | null, Date | null];
};

interface ExportsFormProps {
interface AttendanceExportsFormProps {
allServices: Promise<Pick<Service, 'name' | 'service_id'>[]>;
}

export function ExportsForm({ allServices }: ExportsFormProps) {
export function AttendanceExportsForm({ allServices }: AttendanceExportsFormProps) {
const [enabledDateSelection, setEnabledDateSelection] = useState(false);
const [loading, setLoading] = useState(false);
const services = use(allServices);
Expand All @@ -35,7 +35,7 @@ export function ExportsForm({ allServices }: ExportsFormProps) {
[services],
);

const form = useForm<ExportsProps>({
const form = useForm<AttendanceExportsProps>({
initialValues: {
names: [],
range: [null, null],
Expand Down Expand Up @@ -70,7 +70,7 @@ export function ExportsForm({ allServices }: ExportsFormProps) {
},
});

const handleSubmit = async (values: ExportsProps) => {
const handleSubmit = async (values: AttendanceExportsProps) => {
const transformedValues = {
id: values.names.map((name) => serviceData[name]),
start_date: values.range[0]?.toISOString(),
Expand Down Expand Up @@ -125,17 +125,7 @@ export function ExportsForm({ allServices }: ExportsFormProps) {
return;
}

const responseData = response.data as ArrayBuffer;
const blob = new Blob([responseData], { type: response.headers['content-type'] });

const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = response.headers['content-disposition'].split('=')[1];
a.click();
a.remove();

URL.revokeObjectURL(url);
downloadFile(response.data as ArrayBuffer, response.headers as DownloadFileHeaders);

notifications.show({
title: 'Success',
Expand All @@ -145,33 +135,35 @@ export function ExportsForm({ allServices }: ExportsFormProps) {
};

return (
<form onSubmit={form.onSubmit(handleSubmit)} className='exports-form'>
<TagsInput
label='Services'
placeholder='Select services to export'
data={Object.keys(serviceData)}
{...form.getInputProps('names')}
/>

<Checkbox
label='Select date range'
checked={enabledDateSelection}
onChange={() => {
form.setValues({ range: [null, null] });
setEnabledDateSelection(!enabledDateSelection);
}}
/>
{enabledDateSelection && (
<DatePickerInput
type='range'
placeholder='Choose a date range...'
{...form.getInputProps('range')}
<ExportsCard>
<form onSubmit={form.onSubmit(handleSubmit)} className='exports-form'>
<TagsInput
label='Services'
placeholder='Select services to export'
data={Object.keys(serviceData)}
{...form.getInputProps('names')}
/>
)}

<Button type='submit' variant='outline' color='green' loading={loading}>
Export Data
</Button>
</form>
<Checkbox
label='Select date range'
checked={enabledDateSelection}
onChange={() => {
form.setValues({ range: [null, null] });
setEnabledDateSelection(!enabledDateSelection);
}}
/>
{enabledDateSelection && (
<DatePickerInput
type='range'
placeholder='Choose a date range...'
{...form.getInputProps('range')}
/>
)}

<Button type='submit' variant='outline' color='green' loading={loading}>
Export Data
</Button>
</form>
</ExportsCard>
);
}
29 changes: 29 additions & 0 deletions interapp-frontend/src/app/exports/ExportsCard/ExportsCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Card, Stack } from '@mantine/core';
import { AxiosResponseHeaders } from 'axios';
import { type ReactNode } from 'react';

export interface DownloadFileHeaders extends AxiosResponseHeaders {
'content-type': string;
'content-disposition': string;
}

export function downloadFile(data: ArrayBuffer, headers: DownloadFileHeaders) {
const blob = new Blob([data], { type: headers['content-type'] });

const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = headers['content-disposition'].split('=')[1];
a.click();
a.remove();

URL.revokeObjectURL(url);
}

export function ExportsCard({ children }: { children: ReactNode }) {
return (
<Card shadow='sm' padding='lg' radius='md' withBorder>
<Stack gap='sm'>{children}</Stack>
</Card>
);
}
6 changes: 0 additions & 6 deletions interapp-frontend/src/app/exports/ExportsForm/styles.css

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
'use client';
import { ExportsCard, downloadFile, type DownloadFileHeaders } from '../ExportsCard/ExportsCard';
import { Select, Button, Group, Text } from '@mantine/core';
import { useForm } from '@mantine/form';
import { AxiosInstance } from 'axios';
import { notifications } from '@mantine/notifications';

interface ServiceExportsProps {
type: 'user_id' | 'username' | 'service_hours';
order: 'ASC' | 'DESC';
}

interface UserFriendlyServiceExportsProps {
type: (typeof types)[number];
order: (typeof orders)[number];
}

const types = ['User ID', 'Username', 'CCA Hours'] as const;
const orders = ['Ascending', 'Descending'] as const;

const test = async (apiClient: AxiosInstance) => {
const response = await apiClient.get('/exports/service_hours', {
params: {
type: 'username',
order: 'ASC',
},
responseType: 'arraybuffer',
});
console.log(response.data);
const responseData = response.data as ArrayBuffer;
const blob = new Blob([responseData], { type: response.headers['content-type'] });

const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = response.headers['content-disposition'].split('=')[1];
a.click();
a.remove();

URL.revokeObjectURL(url);
console.log(a.href);
};

const normaliseServiceExportsProps = (
props: UserFriendlyServiceExportsProps,
): ServiceExportsProps => {
let type: ServiceExportsProps['type'];
let order: ServiceExportsProps['order'];

switch (props.type) {
case 'User ID':
type = 'user_id';
break;
case 'Username':
type = 'username';
break;
case 'CCA Hours':
type = 'service_hours';
break;
}

switch (props.order) {
case 'Ascending':
order = 'ASC';
break;
case 'Descending':
order = 'DESC';
break;
}

return { type, order };
};

const initialType = types[0];
const initialOrder = orders[0];

export function ServiceHoursExportsForm() {
const form = useForm<UserFriendlyServiceExportsProps>({
initialValues: {
type: initialType,
order: initialOrder,
},
});

const handleSubmit = async (values: UserFriendlyServiceExportsProps) => {
console.log(values);
};

return (
<ExportsCard>
<form onSubmit={form.onSubmit(handleSubmit)}>
<Group justify='center'>
<Text>Sort users by...</Text>
<Select
label='Type'
defaultValue={initialType}
data={types}
allowDeselect={false}
placeholder='Select type'
{...form.getInputProps('type')}
/>
<Select
label='Order'
defaultValue={initialOrder}
data={orders}
allowDeselect={false}
placeholder='Select order'
{...form.getInputProps('order')}
/>
</Group>
<Button type='submit'>Export</Button>
</form>
</ExportsCard>
);
}
10 changes: 7 additions & 3 deletions interapp-frontend/src/app/exports/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ export const dynamic = 'force-dynamic'; // nextjs needs this to build properly

import APIClient from '@api/api_client';
import { useMemo } from 'react';
import { Title, Text } from '@mantine/core';
import { Title, Text, Group } from '@mantine/core';

import { type Service } from '@/app/services/types';
import { AxiosInstance } from 'axios';
import './styles.css';
import { ExportsForm } from './ExportsForm/ExportsForm';
import { AttendanceExportsForm } from './AttendanceExportsForm/AttendanceExportsForm';
import { ServiceHoursExportsForm } from './ServiceHoursExportsForm/ServiceHoursExportsForm';
import { ClientError } from '@utils/.';

const fetchServices = async (apiClient: AxiosInstance) => {
Expand Down Expand Up @@ -35,7 +36,10 @@ export default function Exports() {
<div className='exports-page'>
<Title order={1}>Exports</Title>
<Text>Export data as an Excel sheet here.</Text>
<ExportsForm allServices={services} />
<Group justify='center'>
<AttendanceExportsForm allServices={services} />
<ServiceHoursExportsForm />
</Group>
</div>
);
}

0 comments on commit 5bdfc34

Please sign in to comment.