Skip to content

Commit

Permalink
Add more on workout types.
Browse files Browse the repository at this point in the history
Squashed commit of the following:

commit feb75ac4eb2dbe56a311337d22fbccdac0c3e603
Author: Ethan Swan <[email protected]>
Date:   Mon Sep 4 19:10:49 2023 -0500

    ...

commit f34f9af8f4bb3bab45647b641069e5c2dfd00a18
Author: Ethan Swan <[email protected]>
Date:   Mon Sep 4 18:30:32 2023 -0500

    Start wiring up MoreWorkoutTypesModal.

commit 8690b7962628b57b0189757a63cd184a640c8729
Author: Ethan Swan <[email protected]>
Date:   Mon Sep 4 16:43:32 2023 -0500

    Start working on workout types modal.
  • Loading branch information
eswan18 committed Sep 5, 2023
1 parent f698646 commit 863ac9b
Show file tree
Hide file tree
Showing 5 changed files with 228 additions and 8 deletions.
91 changes: 91 additions & 0 deletions app/(protected)/dashboard/MoreWorkoutTypesModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import ClientModal from "@/components/ClientModal";
import { Workout, WorkoutType } from "@/lib/resources/apiTypes";
import { useRouter } from "next/navigation";

type MoreWorkoutTypesModalProps = {
workoutTypes: WorkoutType[];
handleClose: () => void;
createAndStartWorkout: (
workoutTypeId: string | undefined,
) => Promise<Workout>;
};

export default function MoreWorkoutTypesModal({
workoutTypes,
handleClose,
createAndStartWorkout,
}: MoreWorkoutTypesModalProps) {
const router = useRouter();
// Start by alphabetizing the workout types.
workoutTypes.sort((a, b) => {
if (!a.name || !b.name) throw new Error("Workout type name is null");
return a.name.localeCompare(b.name);
});
const rows = workoutTypes.map((workoutType) => {
const onClick = () => {
createAndStartWorkout(workoutType.id).then((workout) => {
router.push(`/live/workouts/${workout.id}`);
});
};
return (
<tr
className="cursor-pointer even:bg-gray-50 border-y-[1px] border-gray-200"
onClick={onClick}
key={workoutType.id}
title={workoutType.name}
>
<td className="px-2 py-1">{workoutType.name}</td>
<td>
<i className="fi fi-rr-arrow-alt-circle-right text-xl inline-flex align-[-0.2rem] text-gold px-1" />
</td>
</tr>
);
});
const createAndStartCustomWorkout = () => {
createAndStartWorkout(undefined).then((workout) => {
router.push(`/live/workouts/${workout.id}`);
});
};
const createNewWorkoutType = () => {
router.push("/workout-types/new");
};
return (
<ClientModal handleClose={handleClose}>
<div className="flex flex-col max-w-md h-128 m-auto gap-6">
<h2 className="text-lg lg:text-2xl font-bold">Start a Workout</h2>
<div className="flex flex-row justify-between items-center w-full gap-2 lg:gap-4 lg:pb-4">
<div className="flex flex-col px-2">
<h3 className="text-lg">Workout Types</h3>
<div className="w-48 h-48 overflow-y-scroll flex-grow flex-shrink-0 mx-auto">
<table className="w-full table-auto border-collapse">
<tbody>{rows}</tbody>
</table>
</div>
</div>
<div>
<p className="text-lg text-gray-500">OR</p>
</div>
<div className="w-[40%] flex-shrink flex flex-col items-center text-gold text-sm gap-6 max-w-xl">
<button onClick={createNewWorkoutType}>
<div className="flex flex-row justify-between items-center gap-2 px-2">
<i className="fi fi-rr-square-plus inline-flex align-[-0.5rem] text-3xl" />
<p className="text-left">
Create a <span className="font-bold">new</span> type of
workout
</p>
</div>
</button>
<button onClick={createAndStartCustomWorkout}>
<div className="flex flex-row justify-between items-center gap-2 px-2">
<i className="fi fi-rr-square-plus inline-flex align-[-0.5rem] text-3xl" />
<p className="text-left">
Start a <span className="font-bold">custom</span> workout
</p>
</div>
</button>
</div>
</div>
</div>
</ClientModal>
);
}
51 changes: 45 additions & 6 deletions app/(protected)/dashboard/NewWorkoutPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@
import { Workout, WorkoutType } from "@/lib/resources/apiTypes";

import { createWorkout } from "@/lib/resources/workouts";
import { useState } from "react";
import { useRouter } from "next/navigation";
import MoreWorkoutTypesModal from "./MoreWorkoutTypesModal";

async function createAndStartWorkout(workoutTypeId: string): Promise<Workout> {
async function createAndStartWorkout(
workoutTypeId: string | undefined,
): Promise<Workout> {
const workout: Workout = {
workout_type_id: workoutTypeId,
status: "in-progress",
Expand All @@ -19,8 +23,12 @@ export default function NewWorkoutPanel({
}: {
workoutTypes: WorkoutType[];
}) {
const [modal, setModal] = useState<React.ReactNode | null>(null);
const router = useRouter();
const newWorkoutCards = workoutTypes.slice(0, 4).map((workoutType, index) => {
// a temporary thing to test what happens with more workouts
workoutTypes = workoutTypes.concat(workoutTypes).concat(workoutTypes);
workoutTypes = workoutTypes.concat(workoutTypes).concat(workoutTypes);
const newWorkoutCards = workoutTypes.slice(0, 3).map((workoutType, index) => {
const onClick = () => {
if (!workoutType.id) throw new Error("Workout type id is null");
createAndStartWorkout(workoutType.id).then((workout) =>
Expand All @@ -31,27 +39,58 @@ export default function NewWorkoutPanel({
<NewWorkoutButton name={workoutType.name} onClick={onClick} key={index} />
);
});
const onMoreClick = () => {
setModal(
<MoreWorkoutTypesModal
workoutTypes={workoutTypes}
handleClose={() => setModal(null)}
createAndStartWorkout={createAndStartWorkout}
/>,
);
};
return (
<div className="w-full pt-2">
{workoutTypes && <h2 className="text-lg lg:text-2xl">New Workout</h2>}
<div className="flex flex-row flex-wrap mt-2 gap-2 lg:gap-4">
{newWorkoutCards}
{/* <NewWorkoutButton name="More" onClick={onMoreClick} showAsDots /> */}
<MoreWorkoutsButton onClick={onMoreClick} />
</div>
{modal}
</div>
);
}

type NewWorkoutButtonProps = {
name: string;
onClick: () => void;
showAsDots?: boolean;
};

function NewWorkoutButton({ name, onClick }: NewWorkoutButtonProps) {
function NewWorkoutButton({
name,
onClick,
showAsDots = false,
}: NewWorkoutButtonProps) {
return (
<button className="flex flex-col" onClick={onClick}>
<div className="w-32 h-16 rounded-lg shadow-md dark:shadow-sm shadow-gold dark:shadow-gold flex flex-row justify-between items-center px-4 text-gray-800 dark:text-gray-300 bg-white dark:bg-gray-900">
<p className="pr-3 text-left">{name}</p>
{showAsDots ? (
<i className="fi fi-rs-circle-ellipsis text-3xl inline-flex align-[-0.2rem] text-gold" />
) : (
<i className="fi fi-rr-arrow-alt-circle-right text-3xl inline-flex align-[-0.2rem] text-gold" />
)}
</div>
</button>
);
}

function MoreWorkoutsButton({ onClick }: { onClick: () => void }) {
return (
<button className="flex flex-col" onClick={onClick}>
<div className="w-32 h-16 rounded-lg shadow-md dark:shadow-sm shadow-gold dark:shadow-gold flex flex-row justify-between items-center px-2 text-gray-800 dark:text-gray-300 bg-white dark:bg-gray-900">
<p className="px-2">{name}</p>
<i className="fi fi-sr-arrow-alt-circle-right text-3xl inline-flex align-[-0.2rem] text-gold" />
<div className="h-16 rounded-lg flex flex-row justify-between items-center px-4 dark:text-gray-300 text-gold">
<p className="pr-3 font-bold">More Workouts...</p>
</div>
</button>
);
Expand Down
4 changes: 2 additions & 2 deletions app/(protected)/dashboard/RecentWorkoutsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,13 @@ async function RecentWorkoutCard({
const exerciseCount = exercises.length;

const startTimeText = startTime ? formatDate(startTime) : "----";
const name = workout.workout_type_name ?? "Custom";
const name = workout.workout_type_name ?? "Custom Workout";
return (
<Link href={`/workouts/${workout.id}`}>
<div className="rounded-lg p-2 lg:p-3 shadow-md dark:shadow-sm shadow-gold w-32 h-32 flex flex-col bg-white dark:bg-gray-900 dark:shadow-gold text-gray-600 dark:text-gray-400">
<div className="flex-grow">
<p className="text-sm">{startTimeText}</p>
<h2 className="text-gray-800 dark:text-gray-300 text-base lg:text-lg font-bold">
<h2 className="text-gray-800 dark:text-gray-300 text-base lg:text-lg font-bold leading-normal lg:leading-tight">
{name}
</h2>
</div>
Expand Down
76 changes: 76 additions & 0 deletions app/(protected)/workout-types/new/NewWorkoutTypeForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
"use client";

import { useState } from "react";
import { useRouter } from "next/navigation";
import Link from "next/link";
import Input from "@/components/forms/Input";
import LoadingSpinner from "@/components/LoadingSpinner";
import { WorkoutType } from "@/lib/resources/apiTypes";

export default function NewWorkoutTypeForm() {
const router = useRouter();
const [loading, setLoading] = useState(false);
const [name, setName] = useState("");
const [notes, setNotes] = useState("");
const onNameChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setName(event.target.value);
};
const onNotesChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
setNotes(e.target.value);
};

const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
setLoading(true);
/* todo ... */
};

return (
<form className="flex flex-col w-full" onSubmit={handleSubmit}>
<h1 className="text-2xl font-bold">New Workout Type</h1>
<label
htmlFor="name"
className="mb-3 text-gray-700 dark:text-gray-100 flex flex-col"
>
<p className="mb-1">Workout Type Name</p>
<input
className="dark:text-gray-900 rounded-md border border-gray-300"
id="name"
type="text"
name="name"
value={name}
onChange={onNameChange}
/>
</label>
<label
htmlFor="notes"
className="mb-3 text-gray-700 dark:text-gray-100 flex flex-col"
>
<p className="mb-1">Notes (optional)</p>
<textarea
className="dark:text-gray-900 rounded-md border border-gray-300"
id="notes"
name="notes"
value={notes}
onChange={onNotesChange}
/>
</label>

<div className="flex w-full justify-center font-bold">
{loading ? (
<LoadingSpinner />
) : (
<button
className="flex flex-row justify-center items-center
rounded-full text-white bg-gold
py-2 px-4 m-2 gap-2"
type="submit"
>
<p>Create</p>
<i className="text-lg fi fi-sr-arrow-circle-right inline-flex align-[-0.2rem]" />
</button>
)}
</div>
</form>
);
}
14 changes: 14 additions & 0 deletions app/(protected)/workout-types/new/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import NewWorkoutTypeForm from "./NewWorkoutTypeForm";

export const metadata = {
title: "New Workout Type",
description: "Create a new type of workout",
};

export default async function NewWorkoutTypePage() {
return (
<main className="flex flex-col items-center justify-start">
<NewWorkoutTypeForm />
</main>
);
}

0 comments on commit 863ac9b

Please sign in to comment.