Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Shift schedule page #69

Merged
merged 25 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
b6ec04b
feat: create shift schedule page, layout, and table
seandreassen Oct 20, 2024
9bd2ccf
feat: add recurring checkbox and sections to display skill icons
seandreassen Oct 21, 2024
7b27078
feat: move register section to component and improve styling
seandreassen Oct 21, 2024
588ed0a
feat: make table scale for smaller screens and improve styling
seandreassen Oct 24, 2024
cdae953
refactor: improve scaling on small screens
seandreassen Oct 25, 2024
569f9ba
feat: add skeleton for schedule table
seandreassen Oct 26, 2024
d2979b5
feat: add administrator menu to clear shift schedule
seandreassen Oct 27, 2024
1631083
refactor: improve code quality by adding changes requested in pr
seandreassen Oct 28, 2024
4a62834
Merge branch 'dev' into shift-schedule-page
seandreassen Oct 28, 2024
51d47fc
style: fix formatting for lint and format workflow
seandreassen Oct 28, 2024
b184826
style: add 1 empty line to pass lint and format workflow
seandreassen Oct 28, 2024
dc167fc
feat: make administrator menu collapsible and less distracting
seandreassen Oct 28, 2024
4e9f6df
Merge branch 'dev' into shift-schedule-page
seandreassen Oct 28, 2024
f660846
style: add 1 space to pass lint and format workflow
seandreassen Oct 28, 2024
fb846a8
refactor: change collapsible arrow direction when opening or closing …
seandreassen Oct 28, 2024
8e4944d
feat: display day and time of shift in dialog
seandreassen Oct 29, 2024
22c4241
fix: make dialog title fit within dialog on small screens
seandreassen Oct 29, 2024
7d6bdc5
Merge branch 'dev' into shift-schedule-page
seandreassen Oct 30, 2024
131cd2f
fix: add label to administrator menu button and fix formatting
seandreassen Oct 30, 2024
782dc88
refactor: make changes requested in pr
seandreassen Oct 31, 2024
56147b3
refactor: change display of timeslot based on language
seandreassen Oct 31, 2024
07a57ce
fix: fix lighthouse error
seandreassen Oct 31, 2024
8f70617
refactor: add changes requested in pr
seandreassen Nov 4, 2024
0118dd1
refactor: change table spacing to use classname instead of index
seandreassen Nov 7, 2024
08d0705
refactor: move aria label and remove unnecessary span
seandreassen Nov 7, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified bun.lockb
Binary file not shown.
30 changes: 30 additions & 0 deletions messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"events": "Events",
"storage": "Storage",
"about": "About",
"shiftSchedule": "Shift Schedule",
"changeLocale": "Change language",
"toggleTheme": "Toggle theme",
"light": "Light",
Expand Down Expand Up @@ -123,5 +124,34 @@
"returnByDescription": "Select how long you would like to borrow the item for.",
"submit": "Submit"
}
},
"shiftSchedule": {
"title": "Shift Schedule",
"administratorMenu": {
"administratorMenu": "Administrator Menu",
seandreassen marked this conversation as resolved.
Show resolved Hide resolved
"clearShiftSchedule": "Clear shift schedule"
},
"scheduleTable": {
"time": "Time",
"monday": "Monday",
seandreassen marked this conversation as resolved.
Show resolved Hide resolved
"tuesday": "Tuesday",
"wednesday": "Wednesday",
"thursday": "Thursday",
"friday": "Friday",
"scheduleCellContent": {
"empty": "Closed",
"member": "person",
"members": "people",
"present": "on shift"
},
"scheduleCellDialog": {
"onShift": "On shift",
"empty": "No-one on shift",
"registerSection": {
"recurring": "Recurring",
"register": "Register"
}
}
}
}
}
30 changes: 30 additions & 0 deletions messages/no.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"events": "Hendelser",
"storage": "Lager",
"about": "Om oss",
"shiftSchedule": "Vaktliste",
"changeLocale": "Bytt språk",
"toggleTheme": "Bytt tema",
"light": "Lys",
Expand Down Expand Up @@ -123,5 +124,34 @@
"returnByDescription": "Velg hvor lenge du ønsker å låne gjenstanden(e)",
"submit": "Send"
}
},
"shiftSchedule": {
"title": "Vaktliste",
"administratorMenu": {
"administratorMenu": "Administrator-meny",
"clearShiftSchedule": "Tøm vaktliste"
},
"scheduleTable": {
"time": "Tid",
"monday": "Mandag",
"tuesday": "Tirsdag",
"wednesday": "Onsdag",
"thursday": "Torsdag",
"friday": "Fredag",
"scheduleCellContent": {
"empty": "Lukket",
"member": "person",
"members": "personer",
"present": "på vakt"
},
"scheduleCellDialog": {
"onShift": "På vakt",
"empty": "Ingen på vakt",
"registerSection": {
"recurring": "Gjentagende",
"register": "Registrer"
}
}
}
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"@hookform/resolvers": "^3.9.0",
"@lucia-auth/adapter-drizzle": "^1.1.0",
"@radix-ui/react-avatar": "^1.1.0",
"@radix-ui/react-checkbox": "^1.1.2",
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-label": "^2.1.0",
Expand Down
22 changes: 22 additions & 0 deletions src/app/[locale]/(default)/shift-schedule/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { useTranslations } from "next-intl";
import { unstable_setRequestLocale } from "next-intl/server";

type ShiftScheduleHeaderProps = {
children: React.ReactNode,
params: { locale: string }
};

export default function ShiftScheduleHeaderLayout({
children,
params: { locale },
}: ShiftScheduleHeaderProps) {
unstable_setRequestLocale(locale);
const t = useTranslations('shiftSchedule');

return (
<>
<h1 className='relative mb-8 text-center'>{t('title')}</h1>
{children}
</>
);
}
9 changes: 9 additions & 0 deletions src/app/[locale]/(default)/shift-schedule/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ScheduleTableSkeleton } from "@/components/shift-schedule/ScheduleTableSkeleton";

export default function ShiftScheduleSkeleton() {
return (
<>
<ScheduleTableSkeleton />
</>
);
seandreassen marked this conversation as resolved.
Show resolved Hide resolved
}
31 changes: 31 additions & 0 deletions src/app/[locale]/(default)/shift-schedule/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { ScheduleTable } from '@/components/shift-schedule/ScheduleTable';
import { getTranslations, unstable_setRequestLocale } from 'next-intl/server';
import { shiftScheduleMochData } from '@/mock-data/shiftSchedule';
import { AdministratorMenu } from '@/components/shift-schedule/AdministratorMenu';

export async function generateMetadata({
params: { locale },
}: {
params: { locale: string };
}) {
const t = await getTranslations({ locale, namespace: 'layout' });

return {
title: t('shiftSchedule'),
};
}

export default function ShiftSchedulePage({
params: { locale },
}: {
params: { locale: string };
}) {
unstable_setRequestLocale(locale);

return (
<>
<AdministratorMenu />
<ScheduleTable week={shiftScheduleMochData} />
</>
);
}
24 changes: 24 additions & 0 deletions src/components/shift-schedule/AdministratorMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Button } from '@/components/ui/Button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card';
import { Trash2Icon } from 'lucide-react';
import { useTranslations } from 'next-intl';

function AdministratorMenu() {
const t = useTranslations('shiftSchedule.administratorMenu');

return (
<Card className='m-8'>
<CardHeader>
<CardTitle>{t('administratorMenu')}</CardTitle>
</CardHeader>
<CardContent>
<Button variant='link' className='flex flex-row space-x-3 space-y-0'>
<Trash2Icon />
<p>{t('clearShiftSchedule')}</p>
</Button>
seandreassen marked this conversation as resolved.
Show resolved Hide resolved
</CardContent>
</Card>
);
}

export { AdministratorMenu };
22 changes: 22 additions & 0 deletions src/components/shift-schedule/RegisterSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Button } from '@/components/ui/Button';
import { Checkbox } from '@/components/ui/Checkbox';
import { Label } from '@/components/ui/Label';
import { useTranslations } from 'next-intl';

function RegisterSection({ className }: { className?: string }) {
const t = useTranslations(
'shiftSchedule.scheduleTable.scheduleCellDialog.registerSection',
);

return (
<section className={className}>
<section className='flex flex-row space-x-2'>
<Label htmlFor='recurring'>{t('recurring')}: </Label>
<Checkbox id='recurring' />
</section>
<Button>{t('register')}</Button>
</section>
);
}

export { RegisterSection };
20 changes: 20 additions & 0 deletions src/components/shift-schedule/ScheduleCell.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ScheduleCellContent } from '@/components/shift-schedule/ScheduleCellContent';
import { ScheduleCellDialog } from '@/components/shift-schedule/ScheduleCellDialog';
import type { ScheduleCellProps } from '@/components/shift-schedule/ScheduleTable';
import { Dialog, DialogContent, DialogTrigger } from '@/components/ui/Dialog';
import React from 'react';
seandreassen marked this conversation as resolved.
Show resolved Hide resolved

function ScheduleCell({ members }: ScheduleCellProps) {
return (
<Dialog>
<DialogTrigger asChild>
<ScheduleCellContent members={members} />
</DialogTrigger>
<DialogContent className='w-1/3 min-w-80 p-3'>
<ScheduleCellDialog members={members} />
</DialogContent>
</Dialog>
);
}

export { ScheduleCell };
70 changes: 70 additions & 0 deletions src/components/shift-schedule/ScheduleCellContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import type { ScheduleCellProps } from '@/components/shift-schedule/ScheduleTable';
import { TableCell } from '@/components/ui/Table';
import { cx } from '@/lib/utils';
import { UserIcon, UsersIcon } from 'lucide-react';
import { useTranslations } from 'next-intl';
import type React from 'react';
seandreassen marked this conversation as resolved.
Show resolved Hide resolved

function ScheduleCellContent({ members }: ScheduleCellProps) {
const t = useTranslations('shiftSchedule.scheduleTable.scheduleCellContent');

const colorStyle =
seandreassen marked this conversation as resolved.
Show resolved Hide resolved
members.length === 0
? 'bg-accent/50 hover:bg-accent dark:bg-accent/40 dark:hover:bg-accent/60 text-accent-foreground'
: 'bg-foreground/20 hover:bg-foreground/25';

let memberCountIcon: React.ReactNode;
const memberCountIconStyle = 'w-7 h-7';
let memberCount: React.ReactNode;
const memberCountStyle = 'flex align-bottom space-x-1 space-y-0';
let skillIcons: React.ReactNode;

// Set member count icon, member count, and skill icons based on amount of members present
if (members.length === 0) {
seandreassen marked this conversation as resolved.
Show resolved Hide resolved
// Empty shift
memberCount = <p className='leading-none'>{t('empty')}</p>;
} else {
// At least 1 person on shift
skillIcons = (
<section className='leading-none'>[skill icons total]</section>
);
seandreassen marked this conversation as resolved.
Show resolved Hide resolved

if (members.length === 1) {
seandreassen marked this conversation as resolved.
Show resolved Hide resolved
// 1 person on shit
memberCountIcon = <UserIcon className={memberCountIconStyle} />;
memberCount = (
<div className={memberCountStyle}>
<p className='leading-none'>
1 {t('member')} {t('present')}
</p>
</div>
);
} else {
// 2 or more people on shift
memberCountIcon = <UsersIcon className={memberCountIconStyle} />;
memberCount = (
<div className={memberCountStyle}>
<p className='leading-none'>
{members.length} {t('members')} {t('present')}
</p>
</div>
);
}
}

return (
<TableCell className='h-20 min-w-52 flex-1 border p-1.5'>
<div
className={cx(colorStyle, 'flex size-full space-x-2 rounded-md p-3')}
>
{memberCountIcon}
<section className='flex flex-1 flex-col space-y-3'>
{memberCount}
{skillIcons}
</section>
</div>
</TableCell>
);
}

export { ScheduleCellContent };
41 changes: 41 additions & 0 deletions src/components/shift-schedule/ScheduleCellDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { RegisterSection } from '@/components/shift-schedule/RegisterSection';
import type { ScheduleCellProps } from '@/components/shift-schedule/ScheduleTable';
import { DialogHeader, DialogTitle } from '@/components/ui/Dialog';
import { useTranslations } from 'next-intl';

function ScheduleCellDialog({ members }: ScheduleCellProps) {
const t = useTranslations('shiftSchedule.scheduleTable.scheduleCellDialog');

let membersDisplay: React.ReactNode;
seandreassen marked this conversation as resolved.
Show resolved Hide resolved

if (members.length === 0) {
membersDisplay = <p className='leading-none'>{t('empty')}</p>;
} else {
membersDisplay = (
<>
{members.map((member) => (
<section key={member.name} className=''>
seandreassen marked this conversation as resolved.
Show resolved Hide resolved
<p>{member.name}</p>
<section className='ml-5'>[skill icons]</section>
</section>
))}
</>
);
}

return (
<>
<DialogHeader>
<DialogTitle className='font-semibold text-3xl'>
{t('onShift')}
</DialogTitle>
</DialogHeader>
<section className='flex justify-between space-x-8 px-2 pb-1'>
<section className='flex flex-1 flex-col'>{membersDisplay}</section>
<RegisterSection className='mt-auto flex w-fit flex-col space-y-3' />
</section>
</>
);
}

export { ScheduleCellDialog };
13 changes: 13 additions & 0 deletions src/components/shift-schedule/ScheduleCellSkeleton.tsx
seandreassen marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Skeleton } from '@/components/ui/Skeleton';
import { TableCell } from '@/components/ui/Table';
import type React from 'react';

function ScheduleCellSkeleton() {
return (
<TableCell className='h-20 min-w-52 flex-1 border p-1.5'>
<Skeleton className='size-full' />
</TableCell>
);
}

export { ScheduleCellSkeleton };
Loading