Skip to content

Commit

Permalink
Reformat and touch up some things.
Browse files Browse the repository at this point in the history
Squashed commit of the following:

commit 0ff9748a62ac22ad261eeb7c6f40f470011ffd4e
Author: Ethan Swan <[email protected]>
Date:   Sun Sep 3 19:54:05 2023 -0500

    Add and run prettier.

commit 0f143ccbf7c90ce4110e36ac412752668738c9b5
Author: Ethan Swan <[email protected]>
Date:   Sun Sep 3 19:40:54 2023 -0500

    Remove obsolete log page.

commit d3fc08dc56543ca73b1bd4fb2d98570b9b2ad6fc
Author: Ethan Swan <[email protected]>
Date:   Sun Sep 3 19:40:33 2023 -0500

    Touch up all kinds of stuff.
  • Loading branch information
eswan18 committed Sep 4, 2023
1 parent 8489200 commit ef3fa07
Show file tree
Hide file tree
Showing 85 changed files with 1,438 additions and 1,067 deletions.
5 changes: 1 addition & 4 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
{
"extends": [
"next/core-web-vitals",
"plugin:storybook/recommended"
]
"extends": ["next/core-web-vitals", "plugin:storybook/recommended"]
}
9 changes: 9 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/public
/.vercel
/.tsbuild
/build
/.next
/node_modules
.infisical.json
/.vercel_build
/.github
Empty file added .prettierrc.json
Empty file.
2 changes: 1 addition & 1 deletion .storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const config: StorybookConfig = {
"@storybook/addon-essentials",
"@storybook/addon-onboarding",
"@storybook/addon-interactions",
"@storybook/addon-styling"
"@storybook/addon-styling",
],
framework: {
name: "@storybook/nextjs",
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ You can also run the front end locally but use the staging or prod API backend.
./pull_env --prod
```


## Docker

The current deploy process doesn't require a docker image, but I experimented with hosting in Kubernetes so I've set up the infrastructure needed.
Expand Down
2 changes: 1 addition & 1 deletion api_keepalive.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ while true; do
fi
sleep 30
done
```
```
48 changes: 30 additions & 18 deletions app/(protected)/dashboard/NewWorkoutPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,50 +1,62 @@
'use client'
"use client";

import { Workout, WorkoutType } from "@/lib/resources/apiTypes";

import { createNewWorkout } from "@/lib/resources/workouts/createNewWorkout";
import { useRouter } from 'next/navigation';
import { useRouter } from "next/navigation";

async function createAndStartWorkout(workoutTypeId: string): Promise<Workout> {
const workout: Workout = {
workout_type_id: workoutTypeId,
status: 'in-progress',
start_time: new Date() // start the workout right now.
}
return await createNewWorkout({workout});
status: "in-progress",
start_time: new Date(), // start the workout right now.
};
return await createNewWorkout({ workout });
}

export default function NewWorkoutPanel({workoutTypes}: { workoutTypes: WorkoutType[] }) {
export default function NewWorkoutPanel({
workoutTypes,
}: {
workoutTypes: WorkoutType[];
}) {
const router = useRouter();
const newWorkoutCards = workoutTypes.slice(0, 4).map((workoutType, index) => {
const onClick = () => {
if (!workoutType.id) throw new Error('Workout type id is null')
createAndStartWorkout(workoutType.id).then(workout => router.push(`/live/workouts/${workout.id}`));
if (!workoutType.id) throw new Error("Workout type id is null");
createAndStartWorkout(workoutType.id).then((workout) =>
router.push(`/live/workouts/${workout.id}`),
);
};
return <NewWorkoutButton name={workoutType.name} onClick={onClick} key={index} />
})
return (
<NewWorkoutButton name={workoutType.name} onClick={onClick} key={index} />
);
});
return (
<div className="w-full pt-2">
{ workoutTypes && <h2 className="text-gray-900 text-lg lg:text-2xl">New Workout by Type</h2> }
{workoutTypes && (
<h2 className="text-gray-900 text-lg lg:text-2xl">
New Workout by Type
</h2>
)}
<div className="flex flex-row flex-wrap mt-2 gap-2 lg:gap-4">
{ newWorkoutCards }
{newWorkoutCards}
</div>
</div>
)
);
}

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

function NewWorkoutButton({name, onClick}: NewWorkoutButtonProps) {
function NewWorkoutButton({ name, onClick }: NewWorkoutButtonProps) {
return (
<button className="flex flex-col font-bold" onClick={onClick}>
<div className="w-32 h-16 rounded-lg shadow-lg flex flex-row justify-between items-center px-2">
<p>{name}</p>
<i className="fi fi-sr-arrow-alt-circle-right text-3xl inline-flex align-[-0.2rem] text-gold" />
</div>
</button>
)
}
);
}
54 changes: 35 additions & 19 deletions app/(protected)/dashboard/RecentWorkoutsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,57 +3,73 @@ import Link from "next/link";

type RecentWorkoutsPanelProps = {
wktsWithDetails: WorkoutWithDetails[];
}
};

export default async function RecentWorkoutsPanel({wktsWithDetails}: RecentWorkoutsPanelProps) {
export default async function RecentWorkoutsPanel({
wktsWithDetails,
}: RecentWorkoutsPanelProps) {
// This should create a few cards representing recent workouts.
// Sort the workouts by date, and then take the first 3.
wktsWithDetails.sort((a, b) => {
const aDate = new Date(a.workout.start_time ?? "1970-01-01");
const bDate = new Date(b.workout.start_time ?? "1970-01-01");
return bDate.getTime() - aDate.getTime();
})
});
return (
<div className="w-full">
<h2 className="text-2xl">Recent Workouts</h2>
<div className="flex flex-row gap-2 lg:gap-4">
{
wktsWithDetails.map(({workout}) => <RecentWorkoutCard key={workout.id} workout={workout} />)
}
<div className="flex flex-row gap-2 lg:gap-4 flex-wrap">
{wktsWithDetails.length > 0 ? (
wktsWithDetails.map(({ workout }) => (
<RecentWorkoutCard key={workout.id} workout={workout} />
))
) : (
<p className="text-gray-500 pt-2">None yet!</p>
)}
</div>
</div>
)
);
}

function formatDate(date: Date): string {
// Extract date components
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hour = String(date.getHours()).padStart(2, '0');
const minute = String(date.getMinutes()).padStart(2, '0');
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
const hour = String(date.getHours()).padStart(2, "0");
const minute = String(date.getMinutes()).padStart(2, "0");

const today = new Date();
if (date.getFullYear() === today.getFullYear() && date.getMonth() === today.getMonth() && date.getDate() === today.getDate()) {
if (
date.getFullYear() === today.getFullYear() &&
date.getMonth() === today.getMonth() &&
date.getDate() === today.getDate()
) {
return `Today (${hour}:${minute})`;
} else if (date.getFullYear() === today.getFullYear() && date.getMonth() === today.getMonth() && date.getDate() === today.getDate() - 1) {
} else if (
date.getFullYear() === today.getFullYear() &&
date.getMonth() === today.getMonth() &&
date.getDate() === today.getDate() - 1
) {
return "Yesterday";
}

// Combine components into a single string
return `${year}-${month}-${day}`;
}

async function RecentWorkoutCard({workout}: {workout: WorkoutWithType}) {
const startTime = workout.start_time ? new Date(workout.start_time) : undefined;
async function RecentWorkoutCard({ workout }: { workout: WorkoutWithType }) {
const startTime = workout.start_time
? new Date(workout.start_time)
: undefined;
const startTimeText = startTime ? formatDate(startTime) : "----";
const name = workout.workout_type_name ?? "Custom Workout";
return (
<Link href={`/workouts/${workout.id}`}>
<div className="rounded-lg p-2 lg:p-4 shadow-lg w-32 h-32 flex flex-col gap-2">
<p className="text-sm">{ startTimeText }</p>
<p className="text-sm">{startTimeText}</p>
<h2 className="text-gray-900 text-base lg:text-lg">{name}</h2>
</div>
</Link>
)
}
);
}
10 changes: 4 additions & 6 deletions app/(protected)/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@ import { getAllWorkoutsWithDetails } from "@/lib/resources/derived/workoutWithDe
import NewWorkoutPanel from "./NewWorkoutPanel";
import RecentWorkoutsPanel from "./RecentWorkoutsPanel";


const N_EXERCISES = 4;


export default async function DashboardPage() {
const workoutTypes = await getAllWorkoutTypes();
const allWorkouts = await getAllWorkoutsWithDetails({limit: N_EXERCISES});
const allWorkouts = await getAllWorkoutsWithDetails({ limit: N_EXERCISES });

return (
<main className="flex min-h-screen flex-col justify-start gap-10 p-10 lg:p-16">
<main className="flex flex-col justify-start gap-10 p-10 lg:p-16">
<div className="mb-4 text-gray-900 dark:text-gray-100">
<h1 className="text-2xl lg:text-4xl">Welcome!</h1>
</div>
Expand All @@ -23,5 +21,5 @@ export default async function DashboardPage() {
<NewWorkoutPanel workoutTypes={workoutTypes} />
</div>
</main>
)
}
);
}
10 changes: 6 additions & 4 deletions app/(protected)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@ import { getCurrentUser } from "@/lib/session";
import { redirect } from "next/navigation";

interface ProtectedLayoutProps {
children: React.ReactNode
children: React.ReactNode;
}

export default async function ProtectedLayout({ children }: ProtectedLayoutProps) {
export default async function ProtectedLayout({
children,
}: ProtectedLayoutProps) {
const user = await getCurrentUser();
if (!user) {
console.log("User is not logged in. Redirecting to login page.");
// In the long run, it would be nice for this to return the user to the specific page they requested.
redirect("api/auth/signin?callbackUrl=/dashboard")
redirect("api/auth/signin?callbackUrl=/dashboard");
}

return <>{children}</>;
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
'use client'
"use client";

export default function CreateNewExerciseGroupWidget({ onClick }: { onClick: () => void}) {
export default function CreateNewExerciseGroupWidget({
onClick,
}: {
onClick: () => void;
}) {
return (
<div className="rounded-lg p-2 lg:p-4 mt-3 whitespace-nowrap flex justify-start">
<button
className='flex flex-row justify-center items-center text-gold
py-2 m-2 gap-2 text-lg'
className="flex flex-row justify-center items-center text-gold
py-2 m-2 gap-2 text-lg"
onClick={onClick}
>
<i className="fi fi-sr-square-plus inline-flex align-[-0.2rem] text-2xl" />
<p>New exercise</p>
</button>
</div>
)
}
);
}
Loading

0 comments on commit ef3fa07

Please sign in to comment.