Skip to content

Commit

Permalink
feat: reservation detail
Browse files Browse the repository at this point in the history
  • Loading branch information
KonecnyDavid committed Aug 8, 2024
1 parent 307756b commit 1c65c72
Show file tree
Hide file tree
Showing 23 changed files with 425 additions and 60 deletions.
36 changes: 35 additions & 1 deletion package-lock.json

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

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@
"dependencies": {
"@headlessui/react": "^2.1.2",
"@heroicons/react": "^2.1.5",
"@hookform/resolvers": "^3.9.0",
"@tauri-apps/api": "^1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.52.2",
"react-router-dom": "^6.26.0",
"tailwind-merge": "^2.4.0"
"tailwind-merge": "^2.4.0",
"zod": "^3.23.8"
},
"devDependencies": {
"@eslint/js": "^9.8.0",
Expand Down
6 changes: 3 additions & 3 deletions src/components/blocks/Agenda/Agenda.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ export const getPosition = (agendaFrom: Date, time: Date) => {
return hours * hourHeight + ((time.getMinutes() / 60) * hourHeight);
};

const events: Event[] = [
{id: "asdasdasda-adasda", title: "Meeting with John", start: new Date(2024, 7, 5, 14, 0), end: new Date(2024, 7, 5, 16, 0)},
{id: "asdsadasd-asdas", title: "Meeting with John", start: new Date(2024, 7, 5, 17, 0), end: new Date(2024, 7, 5, 18, 30)},
export const events: Event[] = [
{id: "0", title: "Meeting with John", start: new Date(2024, 7, 5, 14, 0), end: new Date(2024, 7, 5, 16, 0), user: "John Doe"},
{id: "1", title: "Meeting with John", start: new Date(2024, 7, 5, 17, 0), end: new Date(2024, 7, 5, 18, 30), user: "John Doe"},
]

const Agenda = () => {
Expand Down
1 change: 1 addition & 0 deletions src/components/blocks/Agenda/AgendaEvent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export interface Event {
title: string;
start: Date;
end: Date;
user: string;
}

const AgendaEvent = ({ event, agendaFrom }: { event: Event, agendaFrom: Date }) => {
Expand Down
39 changes: 34 additions & 5 deletions src/components/blocks/Reservation/Experiments/Experiment.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,44 @@
import Pill from "../../../primitives/Pills/Pill.tsx";
import Pill, {PillVariant} from "../../../primitives/Pills/Pill.tsx";
import {twMerge} from "tailwind-merge";


export type ExperimentState = "active" | "done" | "failed" | "prepared";

interface ExperimentProps {
state: "active" | "done" | "failed" | "prepared";
state: ExperimentState;
}

const resolveState = (state: ExperimentState): { pill: PillVariant, classes: string } => {
switch (state) {
case "active":
return {
pill: "inverted",
classes: "bg-cyan-500"
};
case "done":
return {
pill: "primary",
classes: ""
};
case "failed":
return {
pill: "error",
classes: ""
};
case "prepared":
return {
pill: "secondary",
classes: ""
};
}
}

const Experiment = ({state}: ExperimentProps) => {
console.log(state)
const {pill, classes} = resolveState(state);
return (
<div className="rounded-lg border border-gray-300 flex flex-row items-center p-6 gap-6">
<Pill title="Active" variant="primary" />
<div
className={twMerge("rounded-lg flex flex-row items-center p-2 gap-2 cursor-pointer", classes, state !== "active" ? "border border-gray-300" : "")}>
<Pill title="Active" variant={pill}/>
Experiment 123
</div>
)
Expand Down
15 changes: 15 additions & 0 deletions src/components/blocks/Reservation/Experiments/ExperimentData.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Pill from "../../../primitives/Pills/Pill.tsx";
import H4 from "../../../primitives/headings/H4.tsx";

const ExperimentData = () => {
return (
<div className="rounded-lg border border-gray-300 p-6">
<H4>Data</H4>
<div className="flex flex-row gap-2"><strong>Status:</strong> <Pill title="Synchronizing" variant="primary" /></div>
<div><strong>Loaded:</strong> 24.5 GB (245 Files)</div>
<div><strong>Transferred:</strong> 19.6 GB (200 Files)</div>
</div>
)
}

export default ExperimentData;
42 changes: 42 additions & 0 deletions src/components/blocks/Reservation/Experiments/ExperimentForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import {FormProvider, SubmitHandler, useForm} from "react-hook-form";
import Button from "../../../primitives/buttons/Button.tsx";
import {zodResolver} from "@hookform/resolvers/zod";
import {z} from "zod";
import TextInput from "../../../primitives/form/TextInput.tsx";
import TextAreaInput from "../../../primitives/form/TextAreaInput.tsx";
import ExperimentData from "./ExperimentData.tsx";

const schema = z.object({
name: z.string(),
start: z.date(),
end: z.date(),
note: z.string().optional()
})

type ExperimentFormValues = z.infer<typeof schema>;

const ExperimentForm = () => {
const methods = useForm<ExperimentFormValues>({
resolver: zodResolver(schema),
})
const onSubmit: SubmitHandler<ExperimentFormValues> = (data) => console.log(data)

return (
<FormProvider {...methods}>
<form onSubmit={methods.handleSubmit(onSubmit)}>
<div className="flex flex-col gap-2">
<TextInput<ExperimentFormValues> fieldName="name" label="Name"/>
<div className="flex flex-row gap-2">
<TextInput<ExperimentFormValues> fieldName="start" label="Start Time"/>
<TextInput<ExperimentFormValues> fieldName="end" label="End Time"/>
</div>
<TextAreaInput<ExperimentFormValues> fieldName="note" rows={4} label="Note"/>
<ExperimentData />
<Button type="submit" className="w-full">End Experiment</Button>
</div>
</form>
</FormProvider>
)
}

export default ExperimentForm;
26 changes: 20 additions & 6 deletions src/components/blocks/Reservation/Experiments/Experiments.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
import Experiment from "./Experiment.tsx";
import H3 from "../../../primitives/headings/H3.tsx";
import Button from "../../../primitives/buttons/Button.tsx";
import ExperimentForm from "./ExperimentForm.tsx";

const Experiments = () => {
return (
<div className="flex flex-row">
<div className="rounded-l-lg border border-r-0 border-gray-300 p-6">
<Experiment state="active"/>
</div>
<div className="rounded-r-lg border border-gray-300 p-6 flex-1">
asdasdadasdas
<div>
<H3>Experiments</H3>
<div className="flex flex-row">
<div className="rounded-l-lg border border-r-0 border-gray-300 p-6 flex flex-col gap-2">
<Experiment state="done"/>
<Experiment state="failed"/>
<Experiment state="active"/>
<Experiment state="prepared"/>
<div className="mt-auto">
<Button variant="primary" className="w-full">
Add experiment
</Button>
</div>
</div>
<div className="rounded-r-lg border border-gray-300 p-6 flex-1">
<ExperimentForm />
</div>
</div>
</div>
)
Expand Down
12 changes: 9 additions & 3 deletions src/components/blocks/Reservation/ReservationInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import User from "../User/User.tsx";
import TimeSpan from "../TimeSpan/TimeSpan.tsx";
import {Event} from "../Agenda/AgendaEvent.tsx";

const ReservationInfo = () => {
interface ReservationInfoProps {
reservation: Event;
}

const ReservationInfo = ({reservation}: ReservationInfoProps) => {
return (
<div className="rounded-lg border border-gray-300 p-6">
<span>25. 4. 2024 | 14:00 - 15:00</span>
<User />
<TimeSpan start={reservation.start} end={reservation.end} />
<User name={reservation.user} />
</div>
)
}
Expand Down
16 changes: 16 additions & 0 deletions src/components/blocks/TimeSpan/TimeSpan.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import {ClockIcon} from "@heroicons/react/24/outline";
import {formatTime} from "../../../utils/format.ts";

interface TimeSpanProps {
start: Date;
end: Date;
}

const TimeSpan = ({start, end}: TimeSpanProps) => {
return (<div className="flex flex-row items-center gap-2">
<ClockIcon className="h-6 w-6"/>
<span className="font-bold">{start.getDate()}.{start.getDate()}. {formatTime(start, {showMinutes: true})} - {formatTime(end, {showMinutes: true})}</span>
</div>)
}

export default TimeSpan;
9 changes: 6 additions & 3 deletions src/components/blocks/User/User.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import {UserIcon} from "@heroicons/react/24/solid";

interface UserProps {
name: string;
}

const User = () => {
const User = ({name}: UserProps) => {
return (
<div className="flex flex-row items-center gap-4">
<div className="flex flex-row items-center gap-2">
<UserIcon className="h-6 w-6"/>
<span>David Konečný</span>
<span className="font-bold">{name}</span>
</div>
)
}
Expand Down
41 changes: 41 additions & 0 deletions src/components/dialog/BaseDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {Dialog, DialogBackdrop, DialogPanel} from '@headlessui/react'
import {useSetDialog} from "../../contexts/DialogContextProvider.tsx";
import {ReactNode} from "react";

export interface BaseDialogProps {
title: string;
content: ReactNode;
buttons: ReactNode;
}

const BaseDialog = ({title, content, buttons}: BaseDialogProps) => {
const setDialog = useSetDialog();
return (
<Dialog open={true} onClose={() => setDialog(null)} className="relative z-50">
<DialogBackdrop
transition
className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity data-[closed]:opacity-0 data-[enter]:duration-300 data-[leave]:duration-200 data-[enter]:ease-out data-[leave]:ease-in"
/>
<div className="fixed inset-0 z-10 w-screen overflow-y-auto">
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
<DialogPanel
transition
className="relative transform overflow-hidden rounded-lg bg-white text-left shadow-xl transition-all data-[closed]:translate-y-4 data-[closed]:opacity-0 data-[enter]:duration-300 data-[leave]:duration-200 data-[enter]:ease-out data-[leave]:ease-in sm:my-8 sm:w-full sm:max-w-lg data-[closed]:sm:translate-y-0 data-[closed]:sm:scale-95"
>
<div className="bg-white p-4">
<h3 className="font-bold text-2xl">{title}</h3>
<div>
{content}
</div>
</div>
<div className="bg-gray-50 p-4">
{buttons}
</div>
</DialogPanel>
</div>
</div>
</Dialog>
)
}

export default BaseDialog;
46 changes: 46 additions & 0 deletions src/components/dialog/ConfirmEndExperimentDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {useSetDialog} from "../../contexts/DialogContextProvider.tsx";
import Button from "../primitives/buttons/Button.tsx";
import {useNavigate} from "react-router-dom";
import {Event} from "../blocks/Agenda/AgendaEvent.tsx";
import TimeSpan from "../blocks/TimeSpan/TimeSpan.tsx";
import User from "../blocks/User/User.tsx";
import BaseDialog from "./BaseDialog.tsx";

const ConfirmIdentityDialog = ({reservation}: { reservation: Event }) => {
const setDialog = useSetDialog();
const navigate = useNavigate();

const onConfirm = () => {
setDialog(null);
navigate("/agenda");
}

return (
<BaseDialog
title={`End ${reservation.title}`}
content={
<div>
<TimeSpan start={reservation.start} end={reservation.end}/>
<User name={reservation.user}/>
<div className="mt-4">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean mollis felis urna, a luctus ex
egestas
mollis. Mauris at purus tempus risus interdum euismod. Sed mauris turpis, molestie ut tristique
sed,
aliquet pulvinar est. Sed mauris lectus, imperdiet ut commodo vel, mattis in ex. Suspendisse
eget
purus
sed nulla lacinia fringilla. Ut vestibulum odio nulla, in euismod sem tincidunt rutrum. Nunc
iaculis
risus at urna mollis, non vehicula risus sagittis.
</div>
</div>}
buttons={
<Button type="button" onClick={onConfirm}>
Confirm
</Button>
}/>
)
}

export default ConfirmIdentityDialog;
Loading

0 comments on commit 1c65c72

Please sign in to comment.