Skip to content

Commit

Permalink
Add attendance dashboard
Browse files Browse the repository at this point in the history
  • Loading branch information
MangoSwirl committed Aug 18, 2024
1 parent 5104638 commit df6a7fd
Show file tree
Hide file tree
Showing 17 changed files with 817 additions and 44 deletions.
27 changes: 27 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"@auth/sveltekit": "^0.3.7",
"@napi-rs/canvas": "^0.1.44",
"@prisma/client": "^5.6.0",
"@tanstack/svelte-query": "^5.51.21",
"@vercel/blob": "^0.23.3",
"@vercel/speed-insights": "^1.0.3",
"dotenv": "^16.3.1",
Expand Down
141 changes: 111 additions & 30 deletions src/lib/assets/images/tool-thumbnails/AttendanceThumbnail.svelte
Original file line number Diff line number Diff line change
@@ -1,37 +1,118 @@
<svg width="234" height="147" viewBox="0 0 234 147" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_301_375)">
<g clip-path="url(#clip0_502_130)">
<rect width="234" height="147" class="light-gray" />
<rect x="96" y="-12" width="59" height="20" rx="3" class="light-gray-hover" />
<rect x="159" y="-12" width="59" height="20" rx="3" class="light-gray-hover" />
<rect x="96" y="13" width="59" height="20" rx="3" class="light-gray-hover" />
<rect x="159" y="13" width="59" height="20" rx="3" class="light-gray-hover" />
<rect x="96" y="38" width="59" height="20" rx="3" class="light-gray-hover" />
<rect x="159" y="38" width="59" height="20" rx="3" class="light-gray-hover" />
<rect x="96" y="63" width="59" height="20" rx="3" class="light-gray-hover" />
<rect x="159" y="63" width="59" height="20" rx="3" class="light-gray-hover" />
<rect x="96" y="88" width="59" height="20" rx="3" class="light-gray-hover" />
<rect x="159" y="88" width="59" height="20" rx="3" class="light-gray-hover" />
<rect x="96" y="113" width="59" height="20" rx="3" class="light-gray-hover" />
<rect x="159" y="113" width="59" height="20" rx="3" class="light-gray-hover" />
<rect x="96" y="138" width="59" height="20" rx="3" class="light-gray-hover" />
<rect x="159" y="138" width="59" height="20" rx="3" class="light-gray-hover" />
<circle cx="25" cy="-1" r="10" class="light-gray-hover" />
<rect x="39" y="-4.5" width="38" height="7" rx="1" class="light-gray-hover" />
<circle cx="25" cy="23.6667" r="10" class="light-gray-hover" />
<rect x="39" y="20.1667" width="38" height="7" rx="1" class="light-gray-hover" />
<circle cx="25" cy="48.3333" r="10" class="light-gray-hover" />
<rect x="39" y="44.8333" width="38" height="7" rx="1" class="light-gray-hover" />
<circle cx="25" cy="73" r="10" class="light-gray-hover" />
<rect x="39" y="69.5" width="38" height="7" rx="1" class="light-gray-hover" />
<circle cx="25" cy="97.6667" r="10" class="light-gray-hover" />
<rect x="39" y="94.1667" width="38" height="7" rx="1" class="light-gray-hover" />
<circle cx="25" cy="122.333" r="10" class="light-gray-hover" />
<rect x="39" y="118.833" width="38" height="7" rx="1" class="light-gray-hover" />
<circle cx="25" cy="147" r="10" class="light-gray-hover" />
<rect x="39" y="143.5" width="38" height="7" rx="1" class="light-gray-hover" />
<mask
id="mask0_502_130"
style="mask-type:luminance"
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="234"
height="147"
>
<path d="M234 0H0V147H234V0Z" fill="white" />
</mask>
<g mask="url(#mask0_502_130)">
<path
d="M160.639 0H77.4124C76.0801 0 75 1.07892 75 2.40984C75 3.74075 76.0801 4.81967 77.4124 4.81967H160.639C161.971 4.81967 163.052 3.74075 163.052 2.40984C163.052 1.07892 161.971 0 160.639 0Z"
class="light-gray-hover"
/>
<path
d="M160.639 38H77.4124C76.0801 38 75 39.0789 75 40.4098C75 41.7408 76.0801 42.8197 77.4124 42.8197H160.639C161.971 42.8197 163.052 41.7408 163.052 40.4098C163.052 39.0789 161.971 38 160.639 38Z"
class="light-gray-hover"
/>
<path
d="M160.639 76H77.4124C76.0801 76 75 77.0789 75 78.4098C75 79.7408 76.0801 80.8197 77.4124 80.8197H160.639C161.971 80.8197 163.052 79.7408 163.052 78.4098C163.052 77.0789 161.971 76 160.639 76Z"
class="light-gray-hover"
/>
<path
d="M160.639 114H77.4124C76.0801 114 75 115.079 75 116.41C75 117.741 76.0801 118.82 77.4124 118.82H160.639C161.971 118.82 163.052 117.741 163.052 116.41C163.052 115.079 161.971 114 160.639 114Z"
class="light-gray-hover"
/>
<path
d="M100.33 8.43445H77.4124C76.0801 8.43445 75 9.51337 75 10.8443C75 12.1752 76.0801 13.2541 77.4124 13.2541H100.33C101.662 13.2541 102.742 12.1752 102.742 10.8443C102.742 9.51337 101.662 8.43445 100.33 8.43445Z"
class="light-gray-hover"
/>
<path
d="M100.33 46.4344H77.4124C76.0801 46.4344 75 47.5134 75 48.8443C75 50.1752 76.0801 51.2541 77.4124 51.2541H100.33C101.662 51.2541 102.742 50.1752 102.742 48.8443C102.742 47.5134 101.662 46.4344 100.33 46.4344Z"
class="light-gray-hover"
/>
<path
d="M100.33 122.434H77.4124C76.0801 122.434 75 123.513 75 124.844C75 126.175 76.0801 127.254 77.4124 127.254H100.33C101.662 127.254 102.742 126.175 102.742 124.844C102.742 123.513 101.662 122.434 100.33 122.434Z"
class="light-gray-hover"
/>
<path
d="M122.041 8.43445H109.979C108.647 8.43445 107.567 9.51337 107.567 10.8443C107.567 12.1752 108.647 13.2541 109.979 13.2541H122.041C123.374 13.2541 124.454 12.1752 124.454 10.8443C124.454 9.51337 123.374 8.43445 122.041 8.43445Z"
class="light-gray-hover"
/>
<path
d="M122.041 46.4344H109.979C108.647 46.4344 107.567 47.5134 107.567 48.8443C107.567 50.1752 108.647 51.2541 109.979 51.2541H122.041C123.374 51.2541 124.454 50.1752 124.454 48.8443C124.454 47.5134 123.374 46.4344 122.041 46.4344Z"
class="light-gray-hover"
/>
<path
d="M122.041 122.434H109.979C108.647 122.434 107.567 123.513 107.567 124.844C107.567 126.175 108.647 127.254 109.979 127.254H122.041C123.374 127.254 124.454 126.175 124.454 124.844C124.454 123.513 123.374 122.434 122.041 122.434Z"
class="light-gray-hover"
/>
<path
d="M178.732 8.43445H131.691C130.358 8.43445 129.278 9.51337 129.278 10.8443C129.278 12.1752 130.358 13.2541 131.691 13.2541H178.732C180.064 13.2541 181.144 12.1752 181.144 10.8443C181.144 9.51337 180.064 8.43445 178.732 8.43445Z"
class="light-gray-hover"
/>
<path
d="M178.732 46.4344H131.691C130.358 46.4344 129.278 47.5134 129.278 48.8443C129.278 50.1752 130.358 51.2541 131.691 51.2541H178.732C180.064 51.2541 181.144 50.1752 181.144 48.8443C181.144 47.5134 180.064 46.4344 178.732 46.4344Z"
class="light-gray-hover"
/>
<path
d="M124.454 84.4344H77.4124C76.0801 84.4344 75 85.5134 75 86.8443C75 88.1752 76.0801 89.2541 77.4124 89.2541H124.454C125.786 89.2541 126.866 88.1752 126.866 86.8443C126.866 85.5134 125.786 84.4344 124.454 84.4344Z"
class="light-gray-hover"
/>
</g>
<path
d="M69 7C69 16.3888 61.3888 24 52 24C42.6112 24 35 16.3888 35 7C35 -2.38884 42.6112 -10 52 -10C61.3888 -10 69 -2.38884 69 7Z"
class="light-gray-hover"
/>
<path
d="M69 45C69 54.3888 61.3888 62 52 62C42.6112 62 35 54.3888 35 45C35 35.6112 42.6112 28 52 28C61.3888 28 69 35.6112 69 45Z"
class="light-gray-hover"
/>
<path
d="M69 83C69 92.3888 61.3888 100 52 100C42.6112 100 35 92.3888 35 83C35 73.6112 42.6112 66 52 66C61.3888 66 69 73.6112 69 83Z"
class="light-gray-hover"
/>
<path
d="M69 121C69 130.389 61.3888 138 52 138C42.6112 138 35 130.389 35 121C35 111.611 42.6112 104 52 104C61.3888 104 69 111.611 69 121Z"
class="light-gray-hover"
/>
<path
d="M69 159C69 168.389 61.3888 176 52 176C42.6112 176 35 168.389 35 159C35 149.611 42.6112 142 52 142C61.3888 142 69 149.611 69 159Z"
class="light-gray-hover"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M60.1384 -1.64439C61.0466 -1.01565 61.2731 0.230249 60.6444 1.13842L50.9521 15.1384C50.5795 15.6766 49.967 15.9985 49.3124 16C48.6578 16.0015 48.0438 15.6826 47.6687 15.1462L43.361 8.98615C42.728 8.08096 42.9487 6.834 43.8538 6.201C44.759 5.56799 46.006 5.78865 46.639 6.69384L49.2994 10.4983L57.3556 -1.13842C57.9844 -2.04659 59.2303 -2.27312 60.1384 -1.64439Z"
class="light-gray"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M60.1384 36.3556C61.0466 36.9843 61.2731 38.2302 60.6444 39.1384L50.9521 53.1384C50.5795 53.6766 49.967 53.9985 49.3124 54C48.6578 54.0015 48.0438 53.6826 47.6687 53.1462L43.361 46.9862C42.728 46.081 42.9487 44.834 43.8538 44.201C44.759 43.568 46.006 43.7886 46.639 44.6938L49.2994 48.4983L57.3556 36.8616C57.9844 35.9534 59.2303 35.7269 60.1384 36.3556Z"
class="light-gray"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M60.1384 74.3556C61.0466 74.9843 61.2731 76.2302 60.6444 77.1384L50.9521 91.1384C50.5795 91.6766 49.967 91.9985 49.3124 92C48.6578 92.0015 48.0438 91.6826 47.6687 91.1462L43.361 84.9862C42.728 84.081 42.9487 82.834 43.8538 82.201C44.759 81.568 46.006 81.7886 46.639 82.6938L49.2994 86.4983L57.3556 74.8616C57.9844 73.9534 59.2303 73.7269 60.1384 74.3556Z"
class="light-gray"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M60.1384 112.356C61.0466 112.984 61.2731 114.23 60.6444 115.138L50.9521 129.138C50.5795 129.677 49.967 129.998 49.3124 130C48.6578 130.002 48.0438 129.683 47.6687 129.146L43.361 122.986C42.728 122.081 42.9487 120.834 43.8538 120.201C44.759 119.568 46.006 119.789 46.639 120.694L49.2994 124.498L57.3556 112.862C57.9844 111.953 59.2303 111.727 60.1384 112.356Z"
class="light-gray"
/>
</g>
<defs>
<clipPath id="clip0_301_375">
<clipPath id="clip0_502_130">
<rect width="234" height="147" fill="white" />
</clipPath>
</defs>
Expand Down
5 changes: 5 additions & 0 deletions src/lib/server/util/permission/localizedPermissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,10 @@ export const localizedPermissions: LocalizedPermissionTree = {
view: "View one's own lab certification",
edit: "Edit one's own lab certification"
}
},
attendance: {
'*': 'Full access to attendance',
view: 'View attendance',
edit: 'Edit attendance'
}
};
20 changes: 15 additions & 5 deletions src/routes/+layout.svelte
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
<script lang="ts">
import { browser } from '$app/environment';
import { QueryClient, QueryClientProvider } from '@tanstack/svelte-query';
import { MagnoliaUIRoot } from 'magnolia-ui-svelte';
</script>
<MagnoliaUIRoot>
<!-- <ProgressBar color="var(--victory-purple)" /> -->
const queryClient = new QueryClient({
defaultOptions: {
queries: {
enabled: browser
}
}
});
</script>

<slot />
</MagnoliaUIRoot>
<QueryClientProvider client={queryClient}>
<MagnoliaUIRoot>
<slot />
</MagnoliaUIRoot>
</QueryClientProvider>
69 changes: 69 additions & 0 deletions src/routes/api/people/filtered/+server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import type { RequestHandler } from './$types';
import prisma from '$lib/server/util/prisma';
import { DateTime } from 'luxon';
import { error } from '@sveltejs/kit';

const getFilteredPeople = async (search: string, noAttendanceOnDay: DateTime | null) => {
const people = await prisma.person.findMany({
select: {
id: true,
name: true
},
where: {
AND: {
OR: [
{
name: {
contains: search,
mode: 'insensitive'
}
},
{
email: {
contains: search,
mode: 'insensitive'
}
}
],
attendanceLogEntries: noAttendanceOnDay
? {
none: {
timestamp: {
lte: noAttendanceOnDay.endOf('day').toJSDate(),
gte: noAttendanceOnDay.startOf('day').toJSDate()
}
}
}
: undefined
}
},
take: 10
});

return people;
};

export const GET: RequestHandler = async ({ url }) => {
const { searchParams } = new URL(url);
const search = searchParams.get('search') ?? '';
const noAttendanceOnDayString = searchParams.get('noAttendanceOnDay');

let noAttendanceOnDay: DateTime | null = null;

if (noAttendanceOnDayString) {
noAttendanceOnDay = DateTime.fromFormat(noAttendanceOnDayString, 'yyyy-MM-dd');
if (!noAttendanceOnDay.isValid) {
throw error(400, 'Invalid date');
}
}

const people = await getFilteredPeople(search, noAttendanceOnDay);

return new Response(JSON.stringify(people), {
headers: {
'Content-Type': 'application/json'
}
});
};

export type FilteredPerson = Awaited<ReturnType<typeof getFilteredPeople>>[number];
7 changes: 7 additions & 0 deletions src/routes/tools/attendance/(calendar)/day/+page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { redirect } from '@sveltejs/kit';
import { DateTime } from 'luxon';

export const load = () => {
// Redirect to today's date if no date is specified
throw redirect(307, `/tools/attendance/day/${DateTime.now().toFormat('yyyy-MM-dd')}`);
};
Loading

0 comments on commit df6a7fd

Please sign in to comment.