From b51a35ab158cab0544b8e03376aeb88326764324 Mon Sep 17 00:00:00 2001 From: Yehoshua Sandler Date: Sun, 18 May 2025 15:27:06 -0500 Subject: [PATCH] feat: habbits and schedules --- src/collections/Exercises/index.ts | 4 + src/collections/Habbits/HabbitCategories.ts | 20 +++ src/collections/Habbits/HabbitEntry.ts | 25 +++ src/collections/Habbits/Habbits.ts | 42 +++++ src/collections/Meals/Ingredients.ts | 1 + src/collections/Meals/MealItems.ts | 1 + src/collections/Meals/Meals.ts | 1 + src/collections/Schedules/index.ts | 37 +++++ src/collections/Users/index.ts | 1 + src/collections/Workouts/index.ts | 2 + src/payload-types.ts | 164 ++++++++++++++++++++ src/payload.config.ts | 6 +- src/plugins/index.ts | 64 ++------ 13 files changed, 314 insertions(+), 54 deletions(-) create mode 100644 src/collections/Habbits/HabbitCategories.ts create mode 100644 src/collections/Habbits/HabbitEntry.ts create mode 100644 src/collections/Habbits/Habbits.ts create mode 100644 src/collections/Schedules/index.ts diff --git a/src/collections/Exercises/index.ts b/src/collections/Exercises/index.ts index 0900db8..5bfa234 100644 --- a/src/collections/Exercises/index.ts +++ b/src/collections/Exercises/index.ts @@ -4,6 +4,7 @@ export const ExerciseTypes: CollectionConfig = { slug: 'exerciseTypes', admin: { useAsTitle: 'name', + group: 'Exercise', }, fields: [ { @@ -25,6 +26,7 @@ export const MuscleGroups: CollectionConfig = { slug: 'muscleGroups', admin: { useAsTitle: 'name', + group: 'Exercise', }, fields: [ { @@ -46,6 +48,7 @@ export const Equipments: CollectionConfig = { slug: 'equipments', admin: { useAsTitle: 'name', + group: 'Exercise', }, fields: [ { @@ -68,6 +71,7 @@ export const Exercises: CollectionConfig = { slug: 'exercises', admin: { useAsTitle: 'name', + group: 'Exercise', }, fields: [ { diff --git a/src/collections/Habbits/HabbitCategories.ts b/src/collections/Habbits/HabbitCategories.ts new file mode 100644 index 0000000..774f020 --- /dev/null +++ b/src/collections/Habbits/HabbitCategories.ts @@ -0,0 +1,20 @@ +import { CollectionConfig } from "payload"; + +export const HabbitCategories: CollectionConfig = { + slug: 'habbitCategories', + admin: { + useAsTitle: 'name', + group: 'Habbits', + }, + fields: [ + { + name: 'name', + type: 'text', + unique: true, + }, + { + name: 'description', + type: 'textarea', + }, + ], +} diff --git a/src/collections/Habbits/HabbitEntry.ts b/src/collections/Habbits/HabbitEntry.ts new file mode 100644 index 0000000..f3da574 --- /dev/null +++ b/src/collections/Habbits/HabbitEntry.ts @@ -0,0 +1,25 @@ +import { CollectionConfig } from "payload"; + +export const HabbitEntries: CollectionConfig = { + slug: 'habbitEntries', + admin: { + useAsTitle: 'completedDate', + group: 'User Data', + }, + fields: [ + { + name: 'habbit', + type: 'relationship', + relationTo: 'habbits' + }, + { + name: 'completedDate', + type: 'date', + admin: { + date: { + pickerAppearance: 'dayAndTime' + } + } + }, + ], +} diff --git a/src/collections/Habbits/Habbits.ts b/src/collections/Habbits/Habbits.ts new file mode 100644 index 0000000..73c473e --- /dev/null +++ b/src/collections/Habbits/Habbits.ts @@ -0,0 +1,42 @@ +import { CollectionConfig } from "payload"; + +export const Habbits: CollectionConfig = { + slug: 'habbits', + admin: { + useAsTitle: 'name', + group: 'User Data', + }, + fields: [ + { + name: 'name', + type: 'text', + }, + { + name: 'description', + type: 'textarea', + }, + { + name: 'category', + type: 'relationship', + relationTo: 'habbitCategories', + }, + { + name: 'instructions', + type: 'richText', + }, + { + name: 'unitValue', + type: 'number', + }, + { + name: 'unitName', + type: 'text', + }, + { + name: 'user', + type: 'relationship', + relationTo: 'users', + hasMany: false, + }, + ], +} diff --git a/src/collections/Meals/Ingredients.ts b/src/collections/Meals/Ingredients.ts index d76d75d..ccc4eb0 100644 --- a/src/collections/Meals/Ingredients.ts +++ b/src/collections/Meals/Ingredients.ts @@ -4,6 +4,7 @@ export const Ingredients: CollectionConfig = { slug: 'ingredients', admin: { useAsTitle: 'name', + group: 'Nutrition', }, fields: [ { diff --git a/src/collections/Meals/MealItems.ts b/src/collections/Meals/MealItems.ts index 0fe1fc1..01c7f8b 100644 --- a/src/collections/Meals/MealItems.ts +++ b/src/collections/Meals/MealItems.ts @@ -7,6 +7,7 @@ export const MealItems: CollectionConfig = { admin: { description: 'Items that make up a meal, such as mashed potatoes, salad, grilled chicken, etc.', useAsTitle: 'name', + group: 'Nutrition', }, fields: [ { diff --git a/src/collections/Meals/Meals.ts b/src/collections/Meals/Meals.ts index 882267b..75f5953 100644 --- a/src/collections/Meals/Meals.ts +++ b/src/collections/Meals/Meals.ts @@ -5,6 +5,7 @@ export const Meals: CollectionConfig = { slug: 'meals', admin: { useAsTitle: 'name', + group: 'Nutrition', }, fields: [ { diff --git a/src/collections/Schedules/index.ts b/src/collections/Schedules/index.ts new file mode 100644 index 0000000..c6298fc --- /dev/null +++ b/src/collections/Schedules/index.ts @@ -0,0 +1,37 @@ +import { CollectionConfig } from "payload"; + +export const Schedules: CollectionConfig = { + slug: 'schedules', + admin: { + group: 'User Data', + }, + fields: [ + { + name: 'user', + type: 'relationship', + relationTo: 'users', + hasMany: false, + }, + { + name: 'scheduledItems', + type: 'array', + fields: [ + { + name: 'item', + type: 'relationship', + relationTo: ['workouts', 'meals', 'habbits'], + hasMany: false, + }, + { + name: 'dateTime', + type: 'date', + admin: { + date: { + pickerAppearance: 'dayAndTime' + } + } + }, + ], + }, + ], +} diff --git a/src/collections/Users/index.ts b/src/collections/Users/index.ts index 2e1bed3..ef1e11c 100644 --- a/src/collections/Users/index.ts +++ b/src/collections/Users/index.ts @@ -48,6 +48,7 @@ export const Users: CollectionConfig = { admin: { defaultColumns: ['name', 'email'], useAsTitle: 'username', + group: 'User Data', }, auth: true, endpoints: [externalUsersLogin], diff --git a/src/collections/Workouts/index.ts b/src/collections/Workouts/index.ts index 4e4a1b4..2ba5494 100644 --- a/src/collections/Workouts/index.ts +++ b/src/collections/Workouts/index.ts @@ -46,6 +46,7 @@ export const WorkoutTypes: CollectionConfig = { slug: 'workoutTypes', admin: { useAsTitle: 'name', + group: 'Exercise', }, fields: [ { @@ -67,6 +68,7 @@ export const Workouts: CollectionConfig = { slug: 'workouts', admin: { useAsTitle: 'name', + group: 'Exercise', }, fields: [ { diff --git a/src/payload-types.ts b/src/payload-types.ts index 9269a09..bb17c39 100644 --- a/src/payload-types.ts +++ b/src/payload-types.ts @@ -82,6 +82,10 @@ export interface Config { ingredients: Ingredient; mealItems: MealItem; meals: Meal; + habbitCategories: HabbitCategory; + habbits: Habbit; + habbitEntries: HabbitEntry; + schedules: Schedule; redirects: Redirect; forms: Form; 'form-submissions': FormSubmission; @@ -108,6 +112,10 @@ export interface Config { ingredients: IngredientsSelect | IngredientsSelect; mealItems: MealItemsSelect | MealItemsSelect; meals: MealsSelect | MealsSelect; + habbitCategories: HabbitCategoriesSelect | HabbitCategoriesSelect; + habbits: HabbitsSelect | HabbitsSelect; + habbitEntries: HabbitEntriesSelect | HabbitEntriesSelect; + schedules: SchedulesSelect | SchedulesSelect; redirects: RedirectsSelect | RedirectsSelect; forms: FormsSelect | FormsSelect; 'form-submissions': FormSubmissionsSelect | FormSubmissionsSelect; @@ -1093,6 +1101,91 @@ export interface Meal { updatedAt: string; createdAt: string; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "habbitCategories". + */ +export interface HabbitCategory { + id: number; + tenant?: (number | null) | Tenant; + name?: string | null; + description?: string | null; + updatedAt: string; + createdAt: string; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "habbits". + */ +export interface Habbit { + id: number; + tenant?: (number | null) | Tenant; + name?: string | null; + description?: string | null; + category?: (number | null) | HabbitCategory; + instructions?: { + root: { + type: string; + children: { + type: string; + version: number; + [k: string]: unknown; + }[]; + direction: ('ltr' | 'rtl') | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + [k: string]: unknown; + } | null; + unitValue?: number | null; + unitName?: string | null; + user?: (number | null) | User; + updatedAt: string; + createdAt: string; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "habbitEntries". + */ +export interface HabbitEntry { + id: number; + tenant?: (number | null) | Tenant; + habbit?: (number | null) | Habbit; + completedDate?: string | null; + updatedAt: string; + createdAt: string; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "schedules". + */ +export interface Schedule { + id: number; + tenant?: (number | null) | Tenant; + user?: (number | null) | User; + scheduledItems?: + | { + item?: + | ({ + relationTo: 'workouts'; + value: number | Workout; + } | null) + | ({ + relationTo: 'meals'; + value: number | Meal; + } | null) + | ({ + relationTo: 'habbits'; + value: number | Habbit; + } | null); + dateTime?: string | null; + id?: string | null; + }[] + | null; + updatedAt: string; + createdAt: string; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "redirects". @@ -1325,6 +1418,22 @@ export interface PayloadLockedDocument { relationTo: 'meals'; value: number | Meal; } | null) + | ({ + relationTo: 'habbitCategories'; + value: number | HabbitCategory; + } | null) + | ({ + relationTo: 'habbits'; + value: number | Habbit; + } | null) + | ({ + relationTo: 'habbitEntries'; + value: number | HabbitEntry; + } | null) + | ({ + relationTo: 'schedules'; + value: number | Schedule; + } | null) | ({ relationTo: 'redirects'; value: number | Redirect; @@ -1866,6 +1975,61 @@ export interface MealsSelect { updatedAt?: T; createdAt?: T; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "habbitCategories_select". + */ +export interface HabbitCategoriesSelect { + tenant?: T; + name?: T; + description?: T; + updatedAt?: T; + createdAt?: T; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "habbits_select". + */ +export interface HabbitsSelect { + tenant?: T; + name?: T; + description?: T; + category?: T; + instructions?: T; + unitValue?: T; + unitName?: T; + user?: T; + updatedAt?: T; + createdAt?: T; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "habbitEntries_select". + */ +export interface HabbitEntriesSelect { + tenant?: T; + habbit?: T; + completedDate?: T; + updatedAt?: T; + createdAt?: T; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "schedules_select". + */ +export interface SchedulesSelect { + tenant?: T; + user?: T; + scheduledItems?: + | T + | { + item?: T; + dateTime?: T; + id?: T; + }; + updatedAt?: T; + createdAt?: T; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "redirects_select". diff --git a/src/payload.config.ts b/src/payload.config.ts index 33daec6..5e8b204 100644 --- a/src/payload.config.ts +++ b/src/payload.config.ts @@ -20,6 +20,10 @@ import { Tenants } from './collections/Tenants' import { Equipments, Exercises, ExerciseTypes, MuscleGroups } from './collections/Exercises' import { Workouts, WorkoutTypes } from './collections/Workouts' import { Ingredients, MealItems, Meals } from './collections/Meals' +import { HabbitCategories } from './collections/Habbits/HabbitCategories' +import { Habbits } from './collections/Habbits/Habbits' +import { HabbitEntries } from './collections/Habbits/HabbitEntry' +import { Schedules } from './collections/Schedules' const filename = fileURLToPath(import.meta.url) const dirname = path.dirname(filename) @@ -68,7 +72,7 @@ export default buildConfig({ connectionString: process.env.DATABASE_URI || '', }, }), - collections: [Pages, Posts, Media, Categories, Users, Tenants, Exercises, ExerciseTypes, MuscleGroups, Equipments, Workouts, WorkoutTypes, Ingredients, MealItems, Meals], + collections: [Pages, Posts, Media, Categories, Users, Tenants, Exercises, ExerciseTypes, MuscleGroups, Equipments, Workouts, WorkoutTypes, Ingredients, MealItems, Meals, HabbitCategories, Habbits, HabbitEntries, Schedules], cors: [getServerSideURL()].filter(Boolean), globals: [Header, Footer], plugins: [ diff --git a/src/plugins/index.ts b/src/plugins/index.ts index c58ad53..39b3b68 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -15,7 +15,8 @@ import { beforeSyncWithSearch } from '@/search/beforeSync' import { Config, Page, Post, User } from '@/payload-types' import { getServerSideURL } from '@/utilities/getURL' import { UserAccessLevel } from '@/collections/Users' -import { isSuperAdmin } from '@/access/admin' +import { isSuperAdmin } from '@/access/isSuperAdmin' +import { getUserTenantIDs } from '@/utilities/getUserTenantIds' const generateTitle: GenerateTitle = ({ doc }) => { return doc?.title ? `${doc.title} | Payload Website Template` : 'Payload Website Template' @@ -108,6 +109,10 @@ export const plugins: Plugin[] = [ ingredients: {}, mealItems: {}, meals: {}, + habbitCategories: {}, + habbits: {}, + habbitEntries: {}, + schedules: {}, }, tenantsArrayField: { includeDefaultField: false, @@ -116,18 +121,11 @@ export const plugins: Plugin[] = [ tenantField: { access: { read: () => true, - update: (data) => { - return true - // const { req, doc } = data - // - // if (isSuperAdmin({ req })) return true - // - // if (!req.user) return false - // - // const userTentants = req.user.tenants || [] - // if (userTentants.includes(doc.tenant)) return true - // - // return false + update: ({ req }) => { + if (isSuperAdmin(req.user)) { + return true + } + return getUserTenantIDs(req.user).length > 0 }, create: () => true, }, @@ -135,43 +133,3 @@ export const plugins: Plugin[] = [ }), ] - -//mport type { CollectionConfig } from 'payload' -// -//mport { authenticated } from '../../access/authenticated' -// -//xport const Exercises: CollectionConfig = { -// slug: 'exercises', -// access: { -// create: authenticated, -// delete: authenticated, -// read: authenticated, -// update: authenticated, -// }, -// admin: { -// useAsTitle: 'name', -// }, -// fields: [ -// { -// name: 'name', -// type: 'text', -// required: true, -// unique: true, -// }, -// { -// name: 'instuctions', -// type: 'text', -// }, -// -// { -// name: 'images', -// type: 'relationship', -// relationTo: 'media', -// hasMany: true, -// }, -// // add these relationships -// // categories - strength, cardio etc -// // muscle group -// // exercise Type - Bodyweight, dumbells etc -// ], -//