Skip to content

Commit

Permalink
Merge pull request #3 from timia2109/feature/mealplan-management
Browse files Browse the repository at this point in the history
Feature/mealplan management
  • Loading branch information
timia2109 authored Aug 3, 2024
2 parents 2f0a450 + 97c5b9f commit b12c5a8
Show file tree
Hide file tree
Showing 31 changed files with 598 additions and 118 deletions.
6 changes: 0 additions & 6 deletions public/locales/de/common.json

This file was deleted.

5 changes: 0 additions & 5 deletions public/locales/de/index.json

This file was deleted.

3 changes: 0 additions & 3 deletions public/locales/en/common.json

This file was deleted.

14 changes: 14 additions & 0 deletions src/actions/leaveMealPlanAction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"use server";

import { leaveMealPlan } from "@/dal/mealPlans/leaveMealPlan";
import { getUserId } from "@/functions/user/getUserId";
import { getLinkWithLocale } from "@/functions/user/redirectWithLocale";
import { revalidatePath } from "next/cache";

export async function leaveMealPlanAction(mealPlanId: string) {
const user = await getUserId();
if (user == null) return;

await leaveMealPlan(mealPlanId, user);
revalidatePath(getLinkWithLocale("/manage"));
}
17 changes: 17 additions & 0 deletions src/actions/setDefaultMealPlan.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"use server";

import { setMealPlanAsDefault } from "@/dal/mealPlans/setMealPlanAsDefault";
import { getUserId } from "@/functions/user/getUserId";
import { getLinkWithLocale } from "@/functions/user/redirectWithLocale";
import { revalidatePath } from "next/cache";

export async function setDefaultMealPlan(mealPlanId: string) {
const userId = await getUserId();
if (userId == null)
return {
message: "Login expected",
};

await setMealPlanAsDefault(userId, mealPlanId);
revalidatePath(getLinkWithLocale("/manage"));
}
28 changes: 28 additions & 0 deletions src/app/[locale]/(userArea)/manage/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { MealPlanEntry } from "@/components/mealEntries/MealPlanEntry";
import { getMealPlans } from "@/dal/mealPlans/getMealPlans";
import { getUserId } from "@/functions/user/getUserId";
import { getScopedI18n } from "@/locales/server";

export default async function ManageMealPlansPage() {
const userId = await getUserId(true);
const mealPlanAssignments = await getMealPlans(userId);
const t = await getScopedI18n("manageMealPlans");

return (
<div className="container mx-auto">
<h1 className="mb-1 text-3xl font-extrabold">{t("manage")}</h1>
<title>{t("manage")}</title>
<div>
{mealPlanAssignments.map((assignment) => (
<MealPlanEntry
key={assignment.mealPlanId}
mealPlanAssignment={assignment}
mealPlan={assignment.mealPlan}
withActions
withUsers
/>
))}
</div>
</div>
);
}
2 changes: 2 additions & 0 deletions src/app/[locale]/(userArea)/mealPlan/[[...selector]]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { MealPlanEntry } from "@/components/mealEntries/MealPlanEntry";
import { MealPlanContainer } from "@/components/mealPlan/MealPlanContainer";
import { getMealPlan } from "@/dal/mealPlans/getMealPlan";
import { createKeyDate } from "@/functions/dateTime/createKeyDate";
Expand All @@ -21,6 +22,7 @@ export default async function MealPlanPage({ params }: Props) {

return (
<div className="container mx-auto">
<MealPlanEntry mealPlan={mealPlan} withUsers />
<MealPlanContainer keyDate={keyDate} mealPlan={mealPlan} />
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function InvitePage() {
return <div>InvitePage</div>;
}
6 changes: 5 additions & 1 deletion src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Footer } from "@/components/common/Footer";
import Head from "next/head";
import "./style.css";

Expand Down Expand Up @@ -38,7 +39,10 @@ export default async function LocaleLayout({
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
/>
</Head>
<body className="bg-base-100">{children}</body>
<body className="flex h-screen flex-col justify-between bg-base-100">
<div className="mb-5">{children}</div>
<Footer />
</body>
</html>
);
}
20 changes: 20 additions & 0 deletions src/app/not-found.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Link from "next/link";

export default function NotFound() {
return (
<div className="hero min-h-screen bg-base-200">
<title>Not found</title>
<div className="hero-content text-center">
<div className="max-w-md">
<h1 className="bg-gradient-to-r from-pink-500 to-purple-600 bg-clip-text text-5xl font-bold text-transparent">
404 Not Found
</h1>
<p className="py-6">Could not find requested resource.</p>
<Link className="btn btn-outline btn-primary" href="/">
Return Home
</Link>
</div>
</div>
</div>
);
}
41 changes: 41 additions & 0 deletions src/components/common/ConfirmationModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"use client";

import { useScopedI18n } from "@/locales/client";
import type { FC, PropsWithChildren } from "react";

type Props = {
onConfirm: () => void;
onCancel: () => void;
};

/** Generic confirm modal. Gets visible, when its rendered */
export const ConfirmationModal: FC<PropsWithChildren<Props>> = ({
children,
onCancel,
onConfirm,
}) => {
const t = useScopedI18n("confirmModal");

return (
<dialog className="modal" open>
<div className="modal-box">
{children}
<div className="modal-action">
<form method="dialog">
<div className="join">
<button className="btn btn-primary join-item" onClick={onConfirm}>
{t("confirm")}
</button>
<button
className="btn btn-outline btn-secondary join-item"
onClick={onCancel}
>
{t("cancel")}
</button>
</div>
</form>
</div>
</div>
</dialog>
);
};
7 changes: 3 additions & 4 deletions src/components/common/Footer.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { useScopedI18n } from "@/locales/client";
import Image from "next/image";
import { getScopedI18n } from "@/locales/server";
import { env } from "../../env/client.mjs";

export async function Footer() {
const t = useScopedI18n("landing");
const t = await getScopedI18n("landing");

return (
<footer
Expand Down Expand Up @@ -32,7 +31,7 @@ export async function Footer() {
target="_blank"
rel="noreferrer"
>
<Image
<img
alt="GitHub Repo stars"
src="https://img.shields.io/github/stars/timia2109/simple-meal-plan?style=social"
width={76}
Expand Down
21 changes: 21 additions & 0 deletions src/components/common/NameProfileImage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { FC } from "react";

type Props = {
name: string;
};

const initialsRegex = /(\w).+ (\w)/;

export const NameProfileImage: FC<Props> = ({ name }) => {
const regexResult = name.match(initialsRegex);
const initials =
regexResult != null ? regexResult[1] + regexResult[2] : name[0] + name[1];

return (
<div className="avatar placeholder">
<div className="w-10 rounded-full bg-neutral text-neutral-content">
<span>{initials}</span>
</div>
</div>
);
};
99 changes: 46 additions & 53 deletions src/components/common/NavBar.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,56 @@
import { auth } from "@/auth";
import { getMealPlans } from "@/dal/mealPlans/getMealPlans";
import { getMealPlanLabel } from "@/functions/user/getMealPlanLabel";
import { getUserId } from "@/functions/user/getUserId";
import { getLinkWithLocale } from "@/functions/user/redirectWithLocale";
import { getScopedI18n } from "@/locales/server";
import Image from "next/image";
import { getI18n } from "@/locales/server";
import Link from "next/link";
import { redirect } from "next/navigation";
import { ProfileImage } from "./ProfileImage";

async function InnerMenu() {
const mealPlans = await getMealPlans(await getUserId(true));
const t = await getI18n();

return (
<>
<li>
<details>
<summary>{t("landing.myMealPlans")}</summary>
<ul className="p-2">
{mealPlans.map((mealPlan) => (
<li key={mealPlan.mealPlanId}>
<Link
href={
mealPlan.userDefault
? getLinkWithLocale("/mealPlan")
: getLinkWithLocale(`/mealPlan/${mealPlan.mealPlanId}`)
}
>
{getMealPlanLabel(mealPlan.mealPlan, t)}
</Link>
</li>
))}
</ul>
</details>
</li>
<li>
<Link href={getLinkWithLocale("/manage")}>
{t("manageMealPlans.manage")}
</Link>
</li>
</>
);
}

export async function NavBar() {
const currentUser = await auth();
if (currentUser == null) redirect("/");

const t = await getScopedI18n("landing");
const t = await getI18n();

return (
<div className="navbar bg-base-100">
<div className="navbar mb-3 bg-base-100 shadow-md">
<div className="navbar-start">
<div className="dropdown">
<div tabIndex={0} role="button" className="btn btn-ghost lg:hidden">
Expand All @@ -35,68 +73,23 @@ export async function NavBar() {
tabIndex={0}
className="menu dropdown-content menu-sm z-[1] mt-3 w-52 rounded-box bg-base-100 p-2 shadow"
>
<li>
<a>Item 1</a>
</li>
<li>
<a>Parent</a>
<ul className="p-2">
<li>
<a>Submenu 1</a>
</li>
<li>
<a>Submenu 2</a>
</li>
</ul>
</li>
<li>
<a>Item 3</a>
</li>
<InnerMenu />
</ul>
</div>
<Link
href={getLinkWithLocale("/mealPlan")}
className="btn btn-ghost text-xl"
>
{t("title")}
{t("landing.title")}
</Link>
</div>
<div className="navbar-center hidden lg:flex">
<ul className="menu menu-horizontal px-1">
<li>
<a>Item 1</a>
</li>
<li>
<details>
<summary>Parent</summary>
<ul className="p-2">
<li>
<a>Submenu 1</a>
</li>
<li>
<a>Submenu 2</a>
</li>
</ul>
</details>
</li>
<li>
<a>Item 3</a>
</li>
<InnerMenu />
</ul>
</div>
<div className="navbar-end">
{currentUser.user?.image && (
<div className="avatar">
<div className="w-10 rounded-full ring ring-primary ring-offset-2 ring-offset-base-100">
<Image
src={currentUser.user.image}
width={32}
height={32}
alt="User profile image"
/>
</div>
</div>
)}
{currentUser.user && <ProfileImage user={currentUser.user} size={40} />}
</div>
</div>
);
Expand Down
27 changes: 27 additions & 0 deletions src/components/common/ProfileImage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { User } from "next-auth";
import Image from "next/image";
import type { FC } from "react";
import { NameProfileImage } from "./NameProfileImage";

export type UserLike = Pick<User, "name" | "image">;

type Props = {
user: UserLike;
size: number;
};

export const ProfileImage: FC<Props> = ({ user, size }) => {
if (user.image == null) return <NameProfileImage name={user.name ?? "??"} />;

return (
<div className="avatar">
<Image
src={user.image}
alt={user.name ?? "??"}
width={size}
height={size}
className="w-10 rounded-full ring ring-primary ring-offset-2 ring-offset-base-100"
/>
</div>
);
};
Loading

0 comments on commit b12c5a8

Please sign in to comment.