Skip to content

Commit e0fcc62

Browse files
destin-estrelaDestin Estrela
andauthored
Marketplace manage students (#55)
* Removed unused icons in sidebar * Query courses at app level * Settings Page * Fixed using wrong variable for listing mutations * Added sidebar for each course and changed add listing button * Switched to tab layout for course overview * Removed extranous right border * Refactored listings into own component * Add students to course with error checking * Smoother delete animation for listings * Polished adding listing animation * Pay Students column selector (missing mutation) * Removed faulty test * Functional award student points mutation call * Update student values and sort payable students alphabetically * Polished add student button * Fix prettier semicolon crashing ci * Fixed more eslint errors * Purchases page. Fulfilling and unfulfilling. * Hide quantity if less than 2. * Handle fulfill promises * Ignore .vs files * Listing card styling Co-authored-by: Destin Estrela <[email protected]>
1 parent 0fdcd9f commit e0fcc62

File tree

9 files changed

+512
-5
lines changed

9 files changed

+512
-5
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,4 @@ amplify-gradle-config.json
4444
amplifytools.xcconfig
4545
.secret-*
4646
.vscode
47+
.vs

src/Components/MarketHome/ListingCard.scss

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,33 @@
44

55
.center {
66
margin: auto;
7-
margin-top: 15%;
8-
width: 50%;
7+
display: flex;
8+
justify-content: center;
99
}
1010

1111
.listing-card-outer {
1212
display: inline-block;
1313
}
1414

15+
16+
.purchase-card {
17+
border: 1px solid rgb(253, 253, 253);
18+
margin-left: 8%;
19+
margin-right: 8%;
20+
margin-top: 1em;
21+
margin-bottom: 1em;
22+
padding: 2%;
23+
box-sizing: border-box;
24+
box-shadow: 0 3px 5px rgba(0, 0, 0, 0.3);
25+
border-radius: 8px;
26+
overflow: hidden;
27+
background: rgb(253, 253, 253);
28+
width: auto;
29+
height: auto;
30+
vertical-align: top;
31+
position: relative;
32+
}
33+
1534
.listing-card {
1635
border: 1px solid rgb(255, 255, 255);
1736
margin: 12px;

src/Components/MarketHome/MarketHome.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { createStyles, makeStyles, Tab, Theme } from '@material-ui/core';
33
import { TabContext, TabList, TabPanel } from '@material-ui/lab';
44
import React, { useState } from 'react';
55
import { ListingTab } from './ListingTab';
6+
import { PurchaseTab } from './PurchaseTab';
67
import { StudentsTab } from './StudentsTab';
78

89
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -29,7 +30,7 @@ const useStyles = makeStyles((theme: Theme) =>
2930
function MarketHome() {
3031
const classes = useStyles();
3132

32-
const [value, setValue] = useState('3');
33+
const [value, setValue] = useState('2');
3334

3435
const handleChange = (event: React.ChangeEvent<Record<string, unknown>>, newValue: string) => {
3536
setValue(newValue);
@@ -59,7 +60,9 @@ function MarketHome() {
5960
<TabPanel value="1">
6061
<ListingTab />
6162
</TabPanel>
62-
<TabPanel value="2">Purchases</TabPanel>
63+
<TabPanel value="2">
64+
<PurchaseTab />
65+
</TabPanel>
6366
<TabPanel value="3">
6467
<StudentsTab />
6568
</TabPanel>
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/* eslint-disable @typescript-eslint/restrict-template-expressions */
2+
import { Button } from 'aws-amplify-react';
3+
import { ApolloError } from '@apollo/client';
4+
import { CircularProgress } from '@material-ui/core';
5+
import {
6+
FulfillMutation,
7+
ReceiptInfoFragment,
8+
useFulfillMutation,
9+
} from '../../__generated__/types';
10+
import ReceiptMenu from './SimplyMenu';
11+
12+
type Props = {
13+
purchase: ReceiptInfoFragment;
14+
displayStudentName: boolean;
15+
displayListingName: boolean;
16+
editPurchase: any;
17+
};
18+
19+
function determineMiddleText(displayListingName: boolean, displayStudentName: boolean) {
20+
if (displayListingName && displayStudentName) {
21+
return ' purchased ';
22+
}
23+
if (displayListingName) {
24+
return 'Purchased ';
25+
}
26+
return '';
27+
}
28+
29+
export function PurchaseCard({
30+
purchase,
31+
displayListingName,
32+
displayStudentName,
33+
editPurchase,
34+
}: Props) {
35+
const middleText = determineMiddleText(displayListingName, displayStudentName);
36+
37+
const onMutationCompleted = (data: FulfillMutation) => {
38+
console.log('Mutation Completed');
39+
editPurchase(data.fulfillPurchase);
40+
};
41+
42+
const onMutationError = (e: ApolloError) => {
43+
console.log(e.message);
44+
};
45+
46+
const [fulfill, { loading: mutationLoading }] = useFulfillMutation({
47+
onCompleted: onMutationCompleted,
48+
onError: onMutationError,
49+
});
50+
51+
const onClickFulfill = () => {
52+
fulfill({
53+
variables: {
54+
fulfilled: true,
55+
course: purchase.course,
56+
receiptId: purchase.receiptId,
57+
},
58+
}).catch((e) => console.log(e));
59+
};
60+
61+
const onClickUnFulfill = () => {
62+
fulfill({
63+
variables: {
64+
fulfilled: false,
65+
course: purchase.course,
66+
receiptId: purchase.receiptId,
67+
},
68+
}).catch((e) => console.log(e));
69+
};
70+
71+
return (
72+
<div className="purchase-card">
73+
<ReceiptMenu onClickUnfulfill={onClickUnFulfill} fulfilled={purchase.fulfilled} />
74+
{!purchase.fulfilled ? (
75+
<div>
76+
<Button
77+
style={{
78+
outline: '0px',
79+
backgroundColor: 'green',
80+
border: '0px',
81+
float: 'right',
82+
color: 'grey',
83+
}}
84+
color="primary"
85+
aria-label="Fulfill"
86+
onClick={onClickFulfill}
87+
>
88+
<span>Fulfill </span>
89+
{mutationLoading ? <CircularProgress size={15} /> : <></>}
90+
</Button>
91+
</div>
92+
) : (
93+
<></>
94+
)}
95+
96+
<h6>{`${
97+
displayStudentName ? `${purchase.student.firstName} ${purchase.student.lastName}` : ''
98+
}${middleText}${displayListingName ? purchase.listingName : ''}`}</h6>
99+
<p>{`${purchase.purchaseDate}`}</p>
100+
{purchase.quantity > 1 ? <p>Quantity: {`${purchase.quantity}`}</p> : <></>}
101+
<p>
102+
Spent: {`${purchase.pointsSpent}`} Point{purchase.pointsSpent > 1 ? 's' : ''}
103+
</p>
104+
{purchase.note !== '' ? <p>Note: {`${purchase.note}`}</p> : <></>}
105+
</div>
106+
);
107+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/* eslint-disable no-nested-ternary */
2+
/* eslint-disable @typescript-eslint/no-unsafe-return */
3+
/* eslint-disable @typescript-eslint/restrict-template-expressions */
4+
import { CircularProgress } from '@material-ui/core';
5+
import { useEffect, useState } from 'react';
6+
import { useParams } from 'react-router-dom';
7+
8+
import { ReceiptInfoFragment, useRecentQuery } from '../../__generated__/types';
9+
import { PurchaseCard } from './PurchaseCard';
10+
11+
const sortPurchases = (a: ReceiptInfoFragment, b: ReceiptInfoFragment) => {
12+
if (a.fulfilled === b.fulfilled) {
13+
return 0;
14+
}
15+
return a.fulfilled ? 1 : -1;
16+
};
17+
18+
export function PurchaseTab() {
19+
const { classId } = useParams<Record<string, string>>();
20+
const [purchases, setPurchases] = useState<ReceiptInfoFragment[]>([]);
21+
22+
const { loading, error, data } = useRecentQuery({
23+
variables: {
24+
course: classId,
25+
fetch: 50,
26+
},
27+
});
28+
29+
useEffect(() => {
30+
if (data) {
31+
setPurchases([...data.recentPurchases].sort(sortPurchases));
32+
}
33+
}, [data]);
34+
35+
const editPurchase = (edited: ReceiptInfoFragment) => {
36+
// 1. Make a shallow copy of the items
37+
const items = [...purchases];
38+
const itemIndex = items.findIndex((purchase) => purchase.receiptId === edited.receiptId);
39+
items[itemIndex] = edited;
40+
41+
setPurchases(items);
42+
};
43+
44+
if (loading || !data) {
45+
return (
46+
<div className="center">
47+
<CircularProgress size={150} />
48+
</div>
49+
);
50+
}
51+
52+
if (error) {
53+
return <s>{error}</s>;
54+
}
55+
56+
return (
57+
<div>
58+
{purchases.map((purchase) => {
59+
return (
60+
<PurchaseCard
61+
purchase={purchase}
62+
displayListingName
63+
displayStudentName
64+
editPurchase={editPurchase}
65+
/>
66+
);
67+
})}
68+
</div>
69+
);
70+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { Button, Menu, MenuItem } from '@material-ui/core';
2+
import React from 'react';
3+
import MoreVertIcon from '@material-ui/icons/MoreVert';
4+
5+
type Props = {
6+
onClickUnfulfill: any;
7+
fulfilled: boolean;
8+
};
9+
10+
export default function SimpleMenu({ onClickUnfulfill, fulfilled }: Props) {
11+
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
12+
13+
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
14+
setAnchorEl(event.currentTarget);
15+
};
16+
17+
const handleClose = () => {
18+
setAnchorEl(null);
19+
};
20+
21+
const handleUnfulfill = () => {
22+
onClickUnfulfill();
23+
handleClose();
24+
};
25+
26+
return (
27+
<div>
28+
<Button
29+
aria-controls="simple-menu"
30+
aria-haspopup="true"
31+
onClick={handleClick}
32+
style={{
33+
float: 'right',
34+
color: 'grey',
35+
clear: 'both',
36+
}}
37+
color="primary"
38+
>
39+
<MoreVertIcon />
40+
</Button>
41+
<Menu
42+
id="simple-menu"
43+
anchorEl={anchorEl}
44+
keepMounted
45+
open={Boolean(anchorEl)}
46+
onClose={handleClose}
47+
>
48+
{fulfilled ? <MenuItem onClick={handleUnfulfill}>Unfulfill</MenuItem> : <></>}
49+
<MenuItem onClick={handleClose}>Refund</MenuItem>
50+
</Menu>
51+
</div>
52+
);
53+
}

src/Components/MarketHome/StudentsTab.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ export function StudentsTab() {
8686
}
8787

8888
if (error) {
89-
return <s>error</s>;
89+
return <s>{error}</s>;
9090
}
9191

9292
rowData = students.map((student: StudentInfoFragment) => {

src/Components/MarketHome/market.graphql

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,44 @@ query Students($courseId: String!) {
5858
...StudentInfo
5959
}
6060
}
61+
62+
fragment ReceiptInfo on Receipt {
63+
studentId
64+
fulfilled
65+
listingName
66+
listingId
67+
course
68+
note
69+
purchaseDate
70+
pointsSpent
71+
receiptId
72+
quantity
73+
student {
74+
firstName
75+
lastName
76+
}
77+
}
78+
79+
mutation Purchase($course: String!, $listingId: String!, $quantity: Int!, $note: String!) {
80+
purchase(course: $course, listingId: $listingId, quantity: $quantity, note: $note) {
81+
...ReceiptInfo
82+
}
83+
}
84+
85+
query Unfulfilled($course: String!) {
86+
unfulfilledPurchases(course: $course) {
87+
...ReceiptInfo
88+
}
89+
}
90+
91+
query Recent($course: String!, $fetch: Int!) {
92+
recentPurchases(course: $course, fetch: $fetch) {
93+
...ReceiptInfo
94+
}
95+
}
96+
97+
mutation Fulfill($receiptId: String!, $course: String!, $fulfilled: Boolean!) {
98+
fulfillPurchase(receiptId: $receiptId, course: $course, fulfilled: $fulfilled) {
99+
...ReceiptInfo
100+
}
101+
}

0 commit comments

Comments
 (0)