diff --git a/.env.example b/.env.example
index 96d6793..37e5ef6 100644
--- a/.env.example
+++ b/.env.example
@@ -1,10 +1,5 @@
-# Since .env is gitignored, you can use .env.example to build a new `.env` file when you clone the repo.
-# Keep this file up-to-date when you add new variables to `.env`.
-
-# This file will be committed to version control, so make sure not to have any secrets in it.
-# If you are cloning this repo, create a copy of this file named `.env` and populate it with your secrets.
-
-# When adding additional env variables, the schema in /env/schema.mjs should be updated accordingly
-
-# Prisma
-DATABASE_URL=file:./db.sqlite
+DATABASE_URL=mysql://root:root@db/simple-meal-plan
+INVITATION_VALIDITY=P30D
+NEXTAUTH_SECRET=A_SECRET
+ROOT_URL=https://example.com
+NEXT_PUBLIC_PRIVACY_URL=https://example.com
\ No newline at end of file
diff --git a/README.md b/README.md
index 8e4c910..175ed6f 100644
--- a/README.md
+++ b/README.md
@@ -1,16 +1,27 @@
# Simple Meal Plan
-This is a very simple meal planner which I build in two hours.
-I want to replace the Excel Sheet, my girlfriend was using.
-Currently there a no authentication build in. So this should only deployed on private networks.
-Work in Progress.
+This is a very simple meal planner. It's main purpose is to host it as a free SaaS solution.
+You can use it [here](https://example.com). Anyway you can deploy it using Docker.
+
+I want to replace the Excel Sheet, my girlfriend was using.
+
+## Features
+
+![Example Screenshot](public/example.png)
+
+- Plan Meal for any date
+- (Tablet, Desktop): See a complete calendar of the month which is editable
+- Share meal plans with magic links to other users to collaborate (household, etc.)
+- Manage multiple meal plans per user
## Techstack
- [Next.js](https://nextjs.org)
- [Prisma](https://prisma.io)
- [Tailwind CSS](https://tailwindcss.com)
-- [tRPC](https://trpc.io)
+- [DaisyUI](https://daisyui.com)
+
+## Deployment
-## Screenshot
-![Example Screenshot](docs/example.png)
\ No newline at end of file
+You'll need a MySQL / MariaDB database.
+Have a look at [`.env.example`](./.env.example) or at the [schema](./src/env/schema.mjs)
diff --git a/docs/example.png b/docs/example.png
deleted file mode 100644
index 78192f8..0000000
Binary files a/docs/example.png and /dev/null differ
diff --git a/prisma/migrations/20240805194021_remove_userid_constrant/migration.sql b/prisma/migrations/20240805194021_remove_userid_constrant/migration.sql
new file mode 100644
index 0000000..1aa04b1
--- /dev/null
+++ b/prisma/migrations/20240805194021_remove_userid_constrant/migration.sql
@@ -0,0 +1,2 @@
+-- DropConstraint
+ALTER TABLE Account DROP KEY Account_userId_key;
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 65fa36c..45fa328 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -59,7 +59,7 @@ model User {
emailVerified DateTime?
image String?
Session Session[]
- Account Account?
+ Account Account[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -70,7 +70,7 @@ model User {
model Account {
id String @id @default(cuid()) @db.Char(25)
- userId String @unique @db.Char(25)
+ userId String @db.Char(25)
type String
provider String
providerAccountId String
diff --git a/public/example.png b/public/example.png
new file mode 100644
index 0000000..dafdf60
Binary files /dev/null and b/public/example.png differ
diff --git a/public/undraw_eating_together_re_ux62.svg b/public/undraw_eating_together_re_ux62.svg
new file mode 100644
index 0000000..31fcbf0
--- /dev/null
+++ b/public/undraw_eating_together_re_ux62.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/undraw_real_time_sync_re_nky7.svg b/public/undraw_real_time_sync_re_nky7.svg
new file mode 100644
index 0000000..e1bd0e8
--- /dev/null
+++ b/public/undraw_real_time_sync_re_nky7.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/app/[locale]/(landing)/FeatureBox.tsx b/src/app/[locale]/(landing)/FeatureBox.tsx
new file mode 100644
index 0000000..2442ee9
--- /dev/null
+++ b/src/app/[locale]/(landing)/FeatureBox.tsx
@@ -0,0 +1,40 @@
+import Image from "next/image";
+import type { FC } from "react";
+import type { Feature } from "./Features";
+import { isImageFeature } from "./Features";
+
+type Props = {
+ feature: Feature;
+};
+
+export const FeatureBox: FC = ({ feature }) => (
+
{users.map((user) => (
diff --git a/src/env/schema.mjs b/src/env/schema.mjs
index 26d117b..ed6ce3d 100644
--- a/src/env/schema.mjs
+++ b/src/env/schema.mjs
@@ -2,27 +2,43 @@
import { z } from "zod";
/**
- * Specify your server-side environment variables schema here.
- * This way you can ensure the app isn't built with invalid env vars.
+ * This are the environment variables for the server.
+ * You need to set them
*/
export const serverSchema = z.object({
- DATABASE_URL: z.string(),
+ DATABASE_URL: z.string().describe("The URL to the database"),
NODE_ENV: z.enum(["development", "test", "production"]),
- GOOGLE_CLIENT_SECRET: z.string(),
- GOOGLE_CLIENT_ID: z.string(),
- INVITATION_VALIDITY: z.string().default("P30D"),
- SESSION_VALIDITY_IN_SECONDS: z.number().default(60 * 60 * 24 * 30), // 30 days
- NEXTAUTH_SECRET: z.string(),
- ROOT_URL: z.string().url().optional(),
+ INVITATION_VALIDITY: z
+ .string()
+ .default("P30D")
+ .describe("The duration of the invitation token"),
+ SESSION_VALIDITY_IN_SECONDS: z
+ .number()
+ .default(60 * 60 * 24 * 30)
+ .describe("Session validity"), // 30 days
+ NEXTAUTH_SECRET: z.string().describe("The secret for next-auth"),
+ ROOT_URL: z
+ .string()
+ .url()
+ .optional()
+ .describe("The root URL of the server. Used to generate invitation links"),
+ ALLOW_ACCOUNT_LINKING: z.enum(["true", "false"]).default("false"),
+
+ // auth-js Providers
+ AUTH_GOOGLE_ID: z.string().optional(),
+ AUTH_GOOGLE_SECRET: z.string().optional(),
+ AUTH_FACEBOOK_ID: z.string().optional(),
+ AUTH_FACEBOOK_SECRET: z.string().optional(),
});
/**
- * Specify your client-side environment variables schema here.
- * This way you can ensure the app isn't built with invalid env vars.
- * To expose them to the client, prefix them with `NEXT_PUBLIC_`.
+ * This are the environment variables for the client.
*/
export const clientSchema = z.object({
- NEXT_PUBLIC_PRIVACY_URL: z.string().optional(),
+ NEXT_PUBLIC_PRIVACY_URL: z
+ .string()
+ .optional()
+ .describe("The URL to the privacy policy. Adds a point to the footer"),
});
/**
diff --git a/src/functions/user/onCreateUser.ts b/src/functions/user/onCreateUser.ts
index c507082..1cee4ba 100644
--- a/src/functions/user/onCreateUser.ts
+++ b/src/functions/user/onCreateUser.ts
@@ -1,10 +1,13 @@
import type { User } from "next-auth";
+import { getMealPlans } from "@/dal/mealPlans/getMealPlans";
import { createMealPlan } from "../../dal/mealPlans/createMealPlan";
/** Creates a meal plan for the current user */
export const onCreateUser: (message: { user: User }) => Promise = async ({
user,
}) => {
- await createMealPlan(user.id!, "", true);
+ const mealPlanAssignments = await getMealPlans(user.id!);
+
+ if (mealPlanAssignments.length == 0) await createMealPlan(user.id!, "", true);
};
diff --git a/src/locales/de.ts b/src/locales/de.ts
index 4b50864..b8e7b1e 100644
--- a/src/locales/de.ts
+++ b/src/locales/de.ts
@@ -4,12 +4,36 @@ export default {
title: "Simple Meal Plan",
subtitle: "Plane deine Mahlzeiten für die Woche",
signinWith: "Anmelden mit {name}",
- author: "Tim Ittermann",
- privacy: "Datenschutz",
+ author: "von Tim Ittermann",
+ privacy: "Impressum / Datenschutz",
myMealPlans: "Meine Essenspläne",
logout: "Abmelden",
profile: "Profile",
},
+ features: {
+ featureA: "Einfache Essensplanung",
+ featureADescription: "Plane super einfach den Essensplan",
+ featureAImgAlt: "Bild mit einem Screenshot der Anwendung",
+
+ featureB: "Immer und überall",
+ featureBDescription1:
+ "Diese Webseite funktioniert auf dem Computer und dem Handy.",
+ featureBDescription2: "Somit hast du deinen Essensplan immer dabei!",
+ featureBImgAlt: "Bild von einem Handy und einem Computer",
+
+ featureC: "Gemeinsam planen",
+ featureCDescription:
+ "Lade deine Mitbewohner oder Freunde ein und plane gemeinsam",
+ featureCImgAlt: "Bild von Menschen die gemeinsam essen",
+
+ featureD: "Kostenlos & Werbefrei",
+ featureDDescription:
+ "Diese Anwendung ist kostenfrei und Werbefrei, weil niemand mag Werbung.",
+
+ featureE: "Open-Source",
+ featureEDescription:
+ "Der Quellcode ist auf GitHub verfügbar. Jeder kann mithelfen diese Anwendung zu verbessern.",
+ },
mealPlan: {
defaultLabel: "Mein Essensplan",
},
diff --git a/src/locales/en.ts b/src/locales/en.ts
index 9dd3f6f..d1cc6c1 100644
--- a/src/locales/en.ts
+++ b/src/locales/en.ts
@@ -4,12 +4,34 @@ export default {
title: "Simple Meal Plan",
subtitle: "Plan your meals for the week",
signinWith: "Sign in with {name}",
- author: "Tim Ittermann",
- privacy: "Privacy",
+ author: "from Tim Ittermann",
+ privacy: "Imprint / Privacy",
myMealPlans: "My Meal Plans",
logout: "Logout",
profile: "Profile",
},
+ features: {
+ featureA: "Simple Meal Planning",
+ featureADescription: "Plan your meal plan super easily",
+ featureAImgAlt: "Image with a screenshot of the application",
+
+ featureB: "Always and everywhere",
+ featureBDescription1: "This website works on the computer and the phone.",
+ featureBDescription2: "So you always have your meal plan with you!",
+ featureBImgAlt: "Image of a phone and a computer",
+
+ featureC: "Plan together",
+ featureCDescription: "Invite your roommates or friends and plan together",
+ featureCImgAlt: "Image of people eating together",
+
+ featureD: "Free & Ad-Free",
+ featureDDescription:
+ "This application is free and ad-free, because no one likes ads.",
+
+ featureE: "Open-Source",
+ featureEDescription:
+ "The source code is available on GitHub. Everyone can help to improve this application",
+ },
mealPlan: {
defaultLabel: "My Meal Plan",
},