Skip to content

Commit 7d5f045

Browse files
authored
Global activity (#121)
Implement a global activity log and ability to search for a user's activity
1 parent 44f5dd9 commit 7d5f045

File tree

8 files changed

+415
-238
lines changed

8 files changed

+415
-238
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,5 @@ yarn-error.log*
3838

3939
# typescript
4040
*.tsbuildinfo
41+
42+
.vscode

src/components/activity/ActivityView.tsx

Lines changed: 35 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,54 @@
1-
import { Box, Button, Collapse, Flex, Spinner, Text, useDisclosure } from '@chakra-ui/react';
1+
import { Box, Button, Collapse, Flex, Spinner, useDisclosure } from '@chakra-ui/react';
22
import { UserRole } from '@prisma/client';
33
import { useSession } from 'next-auth/react';
44
import { SessionUser } from '../../pages/api/auth/[...nextauth]';
5-
import { trpc } from '../../utils/trpc';
5+
import { TicketWithNames } from '../../server/trpc/router/ticket';
66
import ActivityTable from './ActivityTable';
77

8+
interface ActivityViewProps {
9+
helpedTickets: TicketWithNames[];
10+
createdTickets: TicketWithNames[];
11+
}
12+
813
/**
914
* Displays the activity log for the current user
1015
*/
11-
const ActivityView = () => {
16+
const ActivityView = (props: ActivityViewProps) => {
17+
const { helpedTickets, createdTickets } = props;
1218
const { data: session } = useSession();
1319
const user = session?.user as SessionUser;
1420
const { isOpen: isHelpedTableOpen, onToggle: toggleHelpedTable } = useDisclosure();
1521
const { isOpen: isCreatedTableOpen, onToggle: toggleCreatedTable } = useDisclosure();
1622

17-
const { data: userTickets, isLoading: isTicketsLoading } = trpc.ticket.getTicketsWithUserId.useQuery(
18-
{ userId: user.id, shouldSortByCreatedAt: true },
19-
{ refetchOnWindowFocus: false, enabled: !!user },
20-
);
21-
2223
return (
23-
<>
24-
<Flex ml={4} mr={4} mt={4} mb={10} flexDirection='column'>
25-
<Text fontSize='3xl' fontWeight='semibold' mb={3}>
26-
Activity Log
27-
</Text>
28-
{isTicketsLoading ? (
29-
<Spinner />
30-
) : (
31-
<>
32-
<Flex>
33-
<Button mr={4} onClick={toggleHelpedTable} hidden={user.role === UserRole.STUDENT}>
34-
{isHelpedTableOpen ? 'Hide ' : 'Show '} helped tickets
35-
</Button>
36-
<Button onClick={toggleCreatedTable}>{isCreatedTableOpen ? 'Hide ' : 'Show '} created tickets</Button>
37-
</Flex>
24+
<Flex mr={4} flexDirection='column'>
25+
<Flex>
26+
<Button mr={4} onClick={toggleHelpedTable} hidden={user.role === UserRole.STUDENT}>
27+
{isHelpedTableOpen ? 'Hide ' : 'Show '} helped tickets
28+
</Button>
29+
<Button onClick={toggleCreatedTable}>{isCreatedTableOpen ? 'Hide ' : 'Show '} created tickets</Button>
30+
</Flex>
3831

39-
<Box hidden={user.role === UserRole.STUDENT}>
40-
<Collapse in={isHelpedTableOpen} animateOpacity>
41-
<ActivityTable
42-
tickets={userTickets?.helpedTickets ?? []}
43-
title='Your helped tickets'
44-
shouldShowCreatedBy={true}
45-
/>
46-
</Collapse>
47-
</Box>
32+
<Box hidden={user.role === UserRole.STUDENT}>
33+
<Collapse in={isHelpedTableOpen} animateOpacity>
34+
<ActivityTable
35+
tickets={helpedTickets}
36+
title={`${helpedTickets.length} helped tickets`}
37+
shouldShowCreatedBy={true}
38+
/>
39+
</Collapse>
40+
</Box>
4841

49-
<Box>
50-
<Collapse in={isCreatedTableOpen} animateOpacity>
51-
<ActivityTable
52-
tickets={userTickets?.createdTickets ?? []}
53-
title='Your created tickets'
54-
shouldShowCreatedBy={false}
55-
/>
56-
</Collapse>
57-
</Box>
58-
</>
59-
)}
60-
</Flex>
61-
</>
42+
<Box>
43+
<Collapse in={isCreatedTableOpen} animateOpacity>
44+
<ActivityTable
45+
tickets={createdTickets}
46+
title={`${createdTickets.length} created tickets`}
47+
shouldShowCreatedBy={false}
48+
/>
49+
</Collapse>
50+
</Box>
51+
</Flex>
6252
);
6353
};
6454

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { useState } from 'react';
2+
import { Button, Flex, Input, Spinner, Text, useToast } from '@chakra-ui/react';
3+
import { trpc } from '../../utils/trpc';
4+
import { EMAIL_REGEX } from '../../utils/constants';
5+
import dynamic from 'next/dynamic';
6+
import { TicketWithNames } from '../../server/trpc/router/ticket';
7+
import Link from 'next/link';
8+
9+
/** Allows staff to search for user's log */
10+
const GlobalLog = () => {
11+
const ActivityView = dynamic(() => import('../../components/activity/ActivityView'));
12+
const [email, setEmail] = useState('');
13+
const [isLoading, setIsLoading] = useState(false);
14+
const [createdTickets, setCreatedTickets] = useState<TicketWithNames[] | undefined>();
15+
const [helpedTickets, setHelpedTickets] = useState<TicketWithNames[] | undefined>();
16+
const toast = useToast();
17+
const { refetch: fetchTicketsWithEmail } = trpc.ticket.getTicketsWithUserEmail.useQuery(
18+
{ userEmail: email, shouldSortByCreatedAt: true },
19+
{ refetchOnWindowFocus: false, enabled: false },
20+
);
21+
22+
const handleLookup = async () => {
23+
if (!email.match(EMAIL_REGEX)) {
24+
toast({
25+
title: 'Invalid email',
26+
description: 'Please enter a valid email',
27+
status: 'error',
28+
duration: 5000,
29+
isClosable: true,
30+
position: 'top-right',
31+
});
32+
return;
33+
}
34+
35+
setIsLoading(() => true);
36+
const ticketData = await fetchTicketsWithEmail();
37+
setIsLoading(() => false);
38+
39+
if (ticketData.data === null || ticketData.data === undefined) {
40+
toast({
41+
title: 'No user found',
42+
description: 'Please enter a valid staff/student email',
43+
status: 'error',
44+
duration: 5000,
45+
isClosable: true,
46+
position: 'top-right',
47+
});
48+
return;
49+
}
50+
51+
setCreatedTickets(ticketData.data.createdTickets);
52+
setHelpedTickets(ticketData.data.helpedTickets);
53+
};
54+
55+
return (
56+
<Flex mt={4} ml={4} mr={4} flexDirection='column'>
57+
<Text fontSize='3xl' fontWeight='semibold' mb={3}>
58+
Global Log
59+
</Text>
60+
<Text mb={3}>
61+
<Link href='/activity/global'>Click here to view all tickets</Link> or search for a user log below
62+
</Text>
63+
<Input id='email' placeholder='Email' onChange={e => setEmail(e.target.value)} />
64+
<Button mb={2} mt={2} colorScheme='yellow' disabled={!email.match(EMAIL_REGEX)} onClick={handleLookup}>
65+
Search
66+
</Button>
67+
{isLoading && <Spinner />}
68+
{createdTickets !== undefined && helpedTickets !== undefined && (
69+
<ActivityView createdTickets={createdTickets} helpedTickets={helpedTickets} />
70+
)}
71+
</Flex>
72+
);
73+
};
74+
75+
export default GlobalLog;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { Flex, Spinner, Text } from '@chakra-ui/react';
2+
import { useSession } from 'next-auth/react';
3+
import dynamic from 'next/dynamic';
4+
import { trpc } from '../../utils/trpc';
5+
6+
const PersonalLog = () => {
7+
const ActivityView = dynamic(() => import('../../components/activity/ActivityView'));
8+
const { data: session } = useSession();
9+
const user = session?.user;
10+
11+
const { data: userTickets, isLoading: isTicketsLoading } = trpc.ticket.getTicketsWithUserId.useQuery(
12+
{ userId: user!.id, shouldSortByCreatedAt: true },
13+
{ refetchOnWindowFocus: false, enabled: !!user },
14+
);
15+
16+
return (
17+
<Flex ml={4} mr={4} flexDirection='column'>
18+
<Text mt={4} fontSize='3xl' fontWeight='semibold' mb={3}>
19+
Personal Log
20+
</Text>
21+
{isTicketsLoading ? (
22+
<Spinner />
23+
) : (
24+
<ActivityView
25+
createdTickets={userTickets?.createdTickets ?? []}
26+
helpedTickets={userTickets?.helpedTickets ?? []}
27+
/>
28+
)}
29+
</Flex>
30+
);
31+
};
32+
33+
export default PersonalLog;

0 commit comments

Comments
 (0)