Repository: UoaWDCC/aucc
Pull request #40: Creating Trip Report Collection #15
Author: login: neryadom
Created: 2025-04-16T06:31:35Z | Merged: 2025-04-19T07:40:34Z

closes #15 

## 🌟 Context

This collection (Trip Report) was made for the app's Trip Reports's content management.

---

## 📝 Description 

The Trip Report collection assumes a variety of parameters for each Trip Report like date, author, content, etc..
Added the Trip Report collection to the payload config file.



---

## 📋 Notes 

The Trip Report collection depends on the River and Event collections - As of right now, only the River collection has been created.

For this reason, in the Trip report collection, the attribute (relatedEvent) that depends on the not-yet-created Event collection has been commented out. 

Once the Event collection is defined, the commented-out "relatedEvent" attribute should be uncommented,  on lines 43-50 in tripReports.ts.

Linked issue #15: [Backend] Set up Payload collection for Trip Reports
## 🌟 User Story

As a AUCC exec,
I want payload configurations defined,
so that I can add / remove / edit trip reports via the CMS.

---

### 🎯 **Acceptance Criteria**

<!-- Define the conditions that must be met for this story to be complete: -->

- [x] Each trip report has the following fields:
  - [x] title: (Text, required).
  - [x] status: (Select, required, default: 'draft') - Options: draft, published.
  - [x] author: (Relationship, required, hasMany: false, relationTo: 'users').
  - [x] tripDate: (DateTime) - When the trip occurred.
  - [x] relatedEvent: Link to the event this report is about (optional).
  - [x] relatedRiver: Link to the river this report is on (optional).
  - [x] content: (RichText - Lexical Editor) - The main body of the report.
  - [x] gallery: (Relationship, hasMany: true, relationTo: 'media') - Photos/videos from the trip (optional).
  - [x] slug: (Text, unique, indexed) - Auto-generate from title using hook. (this will be the trip identifier in the url)
---
### 📊 **Definition of Done (DoD)**

<!-- Checklist of requirements that must be fulfilled before the issue can be considered complete -->

- [ ] Acceptance criteria met
- [ ] Agreed tests written
- [ ] Agreed documentation in GitHub wiki
- [ ] PR reviewed

---

### 🔗 **Dependencies**
N/A

---

### 📚 **Additional Context**
**This will be a very barebones implementation of the collection for now as we do not have other collections set up to establish relations, but you may choose to pick multiple tickets up (or make your own issue!) if you want to challenge yourself with doing this. Also think about if there are any information missing not stated by the Acceptance Criteria, and potentially note them down on this ticket as a comment :eyes:**

What trip reports look like

https://www.aucc.org.nz/single-post/fulljames-2025


Refer to [the payload collections documentation](https://payloadcms.com/docs/configuration/collections) to get started

Here are also some other resources you can refer to:
* [short intro to payload](https://youtu.be/ftohATkHBi0?si=wa4-RzM2MJMtbvpM&t=227)
* [more in depth payload crash course](https://www.youtube.com/watch?v=0Pz9g654OIc)
* [example payload repositories] https://payloadcms.com/docs/examples/overview

<!-- Include screenshots, wireframes, or references that provide more context. -->


Diff:
diff --git a/package.json b/package.json
index c3b801d7..7ef897da 100644
--- a/package.json
+++ b/package.json
@@ -35,6 +35,7 @@
     "react": "19.0.0",
     "react-dom": "19.0.0",
     "sharp": "0.32.6",
+    "slugify": "^1.6.6",
     "tailwind-merge": "^3.0.2",
     "tailwindcss": "^4.0.9",
     "tailwindcss-animate": "^1.0.7",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index a416904c..7f04b696 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -65,6 +65,9 @@ importers:
       sharp:
         specifier: 0.32.6
         version: 0.32.6
+      slugify:
+        specifier: ^1.6.6
+        version: 1.6.6
       tailwind-merge:
         specifier: ^3.0.2
         version: 3.0.2
@@ -3932,6 +3935,10 @@ packages:
     resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==}
     engines: {node: '>=18'}
 
+  slugify@1.6.6:
+    resolution: {integrity: sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==}
+    engines: {node: '>=8.0.0'}
+
   sonic-boom@4.2.0:
     resolution: {integrity: sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==}
 
@@ -8918,6 +8925,8 @@ snapshots:
       ansi-styles: 6.2.1
       is-fullwidth-code-point: 5.0.0
 
+  slugify@1.6.6: {}
+
   sonic-boom@4.2.0:
     dependencies:
       atomic-sleep: 1.0.0
diff --git a/src/collections/tripReports.ts b/src/collections/tripReports.ts
new file mode 100644
index 00000000..5a087629
--- /dev/null
+++ b/src/collections/tripReports.ts
@@ -0,0 +1,102 @@
+import type { CollectionConfig } from 'payload'
+import slugify from 'slugify'
+
+import { anyone } from '@/access/anyone'
+import { authenticated } from '@/access/authenticated'
+
+export const TripReports: CollectionConfig = {
+  slug: 'trip-reports',
+  access: {
+    create: authenticated,
+    read: anyone,
+    update: authenticated,
+    delete: authenticated,
+  },
+  hooks: {
+    beforeChange: [
+      ({ data, originalDoc }) => {
+        if (data.title && data.title !== originalDoc?.title) {
+          data.slug = slugify(data.title, {
+            lower: true,
+            strict: true,
+          })
+        }
+        return data
+      },
+    ],
+  },
+  fields: [
+    {
+      name: 'title',
+      type: 'text',
+      required: true,
+      label: 'Trip Name',
+    },
+    {
+      name: 'status',
+      type: 'select',
+      required: true,
+      label: 'Trip Status',
+      options: [
+        { label: 'Draft', value: 'draft' },
+        { label: 'Published', value: 'published' },
+      ],
+    },
+    // TODO(@Troyabbs): uncomment this to switch this on when the exec collection is ready #issue 16
+    // {
+    //   name: 'author',
+    //   type: 'relationship',
+    //   required: true,
+    //   hasMany: true,
+    //   relationTo: 'execs',
+    // },
+    {
+      name: 'tripDate',
+      type: 'date',
+      label: 'Trip Date',
+    },
+    {
+      name: 'location',
+      type: 'text',
+      label: 'Trip Location',
+    },
+    {
+      name: 'relatedEvent',
+      type: 'relationship',
+      relationTo: 'events',
+      required: false,
+      label: 'Related Event',
+    },
+    {
+      name: 'relatedRiver',
+      type: 'relationship',
+      required: false,
+      relationTo: 'rivers',
+      label: 'Related River',
+    },
+    {
+      name: 'gallery',
+      type: 'upload',
+      hasMany: true,
+      relationTo: 'media',
+      label: 'Trip Gallery',
+    },
+    {
+      name: 'slug',
+      type: 'text',
+      unique: true,
+      index: true,
+      label: 'Slug',
+      admin: {
+        readOnly: true,
+        description: 'Automatically generated from title',
+        hidden: true,
+      },
+    },
+    {
+      name: 'content',
+      type: 'richText',
+      label: 'Content',
+    },
+  ],
+}
diff --git a/src/payload-types.ts b/src/payload-types.ts
index 92d206e8..399ad2c7 100644
--- a/src/payload-types.ts
+++ b/src/payload-types.ts
@@ -71,6 +71,7 @@ export interface Config {
     media: Media;
     rivers: River;
     events: Event;
+    'trip-reports': TripReport;
     'payload-locked-documents': PayloadLockedDocument;
     'payload-preferences': PayloadPreference;
     'payload-migrations': PayloadMigration;
@@ -81,6 +82,7 @@ export interface Config {
     media: MediaSelect<false> | MediaSelect<true>;
     rivers: RiversSelect<false> | RiversSelect<true>;
     events: EventsSelect<false> | EventsSelect<true>;
+    'trip-reports': TripReportsSelect<false> | TripReportsSelect<true>;
     'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
     'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
     'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
@@ -209,7 +211,7 @@ export interface River {
 export interface Event {
   id: number;
   title: string;
-  status: 'draft' | 'published' | 'archived';
+  status: 'Draft' | 'Published' | 'Archived';
   startTime: string;
   endTime?: string | null;
   location?: string | null;
@@ -232,6 +234,41 @@ export interface Event {
   updatedAt: string;
   createdAt: string;
 }
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "trip-reports".
+ */
+export interface TripReport {
+  id: number;
+  title: string;
+  status: 'draft' | 'published';
+  tripDate?: string | null;
+  location?: string | null;
+  relatedEvent?: (number | null) | Event;
+  relatedRiver?: (number | null) | River;
+  gallery?: (number | Media)[] | null;
+  /**
+   * Automatically generated from title
+   */
+  slug?: string | null;
+  content?: {
+    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;
+  updatedAt: string;
+  createdAt: string;
+}
 /**
  * This interface was referenced by `Config`'s JSON-Schema
  * via the `definition` "payload-locked-documents".
@@ -254,6 +291,10 @@ export interface PayloadLockedDocument {
     | ({
         relationTo: 'events';
         value: number | Event;
+      } | null)
+    | ({
+        relationTo: 'trip-reports';
+        value: number | TripReport;
       } | null);
   globalSlug?: string | null;
   user: {
@@ -403,6 +444,23 @@ export interface EventsSelect<T extends boolean = true> {
   updatedAt?: T;
   createdAt?: T;
 }
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "trip-reports_select".
+ */
+export interface TripReportsSelect<T extends boolean = true> {
+  title?: T;
+  status?: T;
+  tripDate?: T;
+  location?: T;
+  relatedEvent?: T;
+  relatedRiver?: T;
+  gallery?: T;
+  slug?: T;
+  content?: T;
+  updatedAt?: T;
+  createdAt?: T;
+}
 /**
  * This interface was referenced by `Config`'s JSON-Schema
  * via the `definition` "payload-locked-documents_select".
diff --git a/src/payload.config.ts b/src/payload.config.ts
index 0d8e5d52..a907a00d 100644
--- a/src/payload.config.ts
+++ b/src/payload.config.ts
@@ -11,6 +11,7 @@ import { Users } from '@/collections/users'
 import { env } from '@/lib/env'
 import { Events } from './collections/events'
 import { Rivers } from './collections/rivers'
+import { TripReports } from './collections/tripReports'
 
 const filename = fileURLToPath(import.meta.url)
 const dirname = path.dirname(filename)
@@ -22,7 +23,7 @@ export default buildConfig({
       baseDir: path.resolve(dirname),
     },
   },
-  collections: [Users, Media, Rivers, Events],
+  collections: [Users, Media, Rivers, Events, TripReports],
   editor: lexicalEditor({}),
   serverURL: env.SERVER_URL,
   secret: env.PAYLOAD_SECRET,


Reviews:
user: login: dyzhuu
body: nice! few small things to fix
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-16T06:59:50Z
user: login: neryadom
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-16T08:11:53Z
user: login: neryadom
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-16T08:16:08Z
user: login: neryadom
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-16T08:17:12Z
user: login: neryadom
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-16T08:17:30Z
user: login: dyzhuu
body: also, next time you can re-request a review when ready

<img width="323" alt="image" src="https://github.com/user-attachments/assets/2a409d50-e512-482c-b295-d7b5ea6ef204" />

state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-17T09:02:38Z
user: login: neryadom
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T23:54:22Z
user: login: neryadom
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T23:54:55Z
user: login: neryadom
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T23:55:31Z
user: login: neryadom
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-19T00:30:09Z
user: login: dyzhuu
body: LGTM with some changes
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-19T06:29:10Z
user: login: BlackPri0r
body: LGTM! 🚀 
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-19T06:48:27Z
user: login: neryadom
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-19T07:24:57Z
user: login: dyzhuu
body: LGTM! ✅ 
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-19T07:40:33Z

Review comments:
diff_hunk: @@ -38,6 +44,7 @@ assignees: ''
 ### 🔗 **Dependencies**
 
 <!-- List any related issues or blockers, for example:
+>>>>>>> 5ee75839ca6e37c50903890794212ecb264db70e
path: .github/ISSUE_TEMPLATE/issue-template.md
user: login: dyzhuu
body: bad merge commit? 

looks like you made the branch off of a old version of main -- in the future make sure to 

```sh
git switch main
git pull # fetch latest changes
git checkout -b your-branch
```
created_at: 2025-04-16T06:45:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,83 @@
+import type { CollectionConfig } from 'payload'
+
+import { anyone } from '@/access/anyone'
+import { authenticated } from '@/access/authenticated'
+
+export const TripReports: CollectionConfig = {
+  slug: 'trip-reports',
+  access: {
+    create: authenticated,
+    read: anyone,
+    update: authenticated,
+    delete: authenticated,
+  },
+  fields: [
+    {
+      name: 'title',
+      type: 'text',
+      required: true,
+      label: 'Trip Name',
+    },
+    {
+      name: 'status',
+      type: 'select',
+      required: true,
+      label: 'Trip Status',
+      options: [
+        { label: 'Draft', value: 'draft' },
+        { label: 'Published', value: 'published' },
+      ],
+    },
+    {
+      name: 'author',
+      type: 'relationship',
+      required: true,
+      hasMany: false,
+      relationTo: 'users',
+    },
path: src/collections/tripReports.ts
user: login: dyzhuu
body: oops! I think I may have made a mistake in the ticket, this relation should be to the `execs` collection (add a TODO here for when #16 is created), and also, trip reports may have _multiple authors_ as well!
created_at: 2025-04-16T06:50:41Z
author_association: CONTRIBUTOR
path: package-lock.json
user: login: dyzhuu
body: looks like you may have `npm i`'ed which generated this file, you can delete it since pnpm uses a different lock file
created_at: 2025-04-16T06:54:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,83 @@
+import type { CollectionConfig } from 'payload'
+
+import { anyone } from '@/access/anyone'
+import { authenticated } from '@/access/authenticated'
+
+export const TripReports: CollectionConfig = {
+  slug: 'trip-reports',
+  access: {
+    create: authenticated,
+    read: anyone,
+    update: authenticated,
+    delete: authenticated,
+  },
+  fields: [
+    {
+      name: 'title',
+      type: 'text',
+      required: true,
+      label: 'Trip Name',
+    },
+    {
+      name: 'status',
+      type: 'select',
+      required: true,
+      label: 'Trip Status',
+      options: [
+        { label: 'Draft', value: 'draft' },
+        { label: 'Published', value: 'published' },
+      ],
+    },
+    {
+      name: 'author',
+      type: 'relationship',
+      required: true,
+      hasMany: false,
+      relationTo: 'users',
+    },
+    {
+      name: 'tripDate',
+      type: 'date',
+      label: 'Trip Date',
+    },
+    // Just uncomment this to switch this on when the event collection is ready @Franck #issue 34
path: src/collections/tripReports.ts
user: login: dyzhuu
body: I like this comment, for todos could we structure it like this?

```ts
// TODO(@f13322): uncomment this to switch this on when the event collection is ready #issue 34
```
created_at: 2025-04-16T06:59:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,83 @@
+import type { CollectionConfig } from 'payload'
+
+import { anyone } from '@/access/anyone'
+import { authenticated } from '@/access/authenticated'
+
+export const TripReports: CollectionConfig = {
+  slug: 'trip-reports',
+  access: {
+    create: authenticated,
+    read: anyone,
+    update: authenticated,
+    delete: authenticated,
+  },
+  fields: [
+    {
+      name: 'title',
+      type: 'text',
+      required: true,
+      label: 'Trip Name',
+    },
+    {
+      name: 'status',
+      type: 'select',
+      required: true,
+      label: 'Trip Status',
+      options: [
+        { label: 'Draft', value: 'draft' },
+        { label: 'Published', value: 'published' },
+      ],
+    },
+    {
+      name: 'author',
+      type: 'relationship',
+      required: true,
+      hasMany: false,
+      relationTo: 'users',
+    },
+    {
+      name: 'tripDate',
+      type: 'date',
+      label: 'Trip Date',
+    },
+    // Just uncomment this to switch this on when the event collection is ready @Franck #issue 34
path: src/collections/tripReports.ts
user: login: neryadom
body: Awesome, noted!
created_at: 2025-04-16T08:11:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,83 @@
+import type { CollectionConfig } from 'payload'
+
+import { anyone } from '@/access/anyone'
+import { authenticated } from '@/access/authenticated'
+
+export const TripReports: CollectionConfig = {
+  slug: 'trip-reports',
+  access: {
+    create: authenticated,
+    read: anyone,
+    update: authenticated,
+    delete: authenticated,
+  },
+  fields: [
+    {
+      name: 'title',
+      type: 'text',
+      required: true,
+      label: 'Trip Name',
+    },
+    {
+      name: 'status',
+      type: 'select',
+      required: true,
+      label: 'Trip Status',
+      options: [
+        { label: 'Draft', value: 'draft' },
+        { label: 'Published', value: 'published' },
+      ],
+    },
+    {
+      name: 'author',
+      type: 'relationship',
+      required: true,
+      hasMany: false,
+      relationTo: 'users',
+    },
path: src/collections/tripReports.ts
user: login: neryadom
body: Changed

created_at: 2025-04-16T08:16:07Z
author_association: CONTRIBUTOR
path: package-lock.json
user: login: neryadom
body: Yes, I may have done that ^^' deleted the file.
created_at: 2025-04-16T08:17:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -38,6 +44,7 @@ assignees: ''
 ### 🔗 **Dependencies**
 
 <!-- List any related issues or blockers, for example:
+>>>>>>> 5ee75839ca6e37c50903890794212ecb264db70e
path: .github/ISSUE_TEMPLATE/issue-template.md
user: login: neryadom
body: Forgot to clean that line, should be removed now. Just pulled today after a few pull requests were approved and had to resolve some conflicts, think this is a relic of that?
created_at: 2025-04-16T08:17:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,84 @@
+import type { CollectionConfig } from 'payload'
+
+import { anyone } from '@/access/anyone'
+import { authenticated } from '@/access/authenticated'
+
+export const TripReports: CollectionConfig = {
+  slug: 'trip-reports',
+  access: {
+    create: authenticated,
+    read: anyone,
+    update: authenticated,
+    delete: authenticated,
+  },
+  fields: [
+    {
+      name: 'title',
+      type: 'text',
+      required: true,
+      label: 'Trip Name',
+    },
+    {
+      name: 'status',
+      type: 'select',
+      required: true,
+      label: 'Trip Status',
+      options: [
+        { label: 'Draft', value: 'draft' },
+        { label: 'Published', value: 'published' },
+      ],
+    },
+    // TODO(@Troyabbs): uncomment this to switch this on when the exec collection is ready #issue 16
+    // {
+    //   name: 'author',
+    //   type: 'relationship',
+    //   required: true,
+    //   hasMany: true,
+    //   relationTo: 'execs',
+    // },
+    {
+      name: 'tripDate',
+      type: 'date',
+      label: 'Trip Date',
+    },
+    // TODO(@f13322): uncomment this to switch this on when the event collection is ready #issue 34
+    // {
+    //   name: 'relatedEvent',
+    //   type: 'relationship',
+    //   relationTo: 'events',
+    //   required: false,
+    //   label: 'Related Event',
+    // },
+    {
+      name: 'relatedRiver',
+      type: 'relationship',
+      required: false,
+      relationTo: 'rivers',
+      label: 'Related River',
+    },
+    {
+      name: 'content',
+      type: 'richText',
+      label: 'Trip Description',
+    },
+    {
+      name: 'location',
+      type: 'text',
+      label: 'Trip Location',
+    },
+    {
+      name: 'gallery',
+      type: 'relationship',
+      hasMany: true,
+      relationTo: 'media',
+      label: 'Trip Gallery',
+    },
+    {
+      name: 'slug',
+      type: 'text',
+      unique: true,
+      index: true,
+      label: 'Slug',
+    },
path: src/collections/tripReports.ts
user: login: dyzhuu
body: i want to clarify a bit on the purpose of this field, i was thinking this can be the identifier in the URL for that trip report. For example, if the title of the report was 
"President's Report 2023", the corresponding slug could be "presidents-report-2023". Take a look at https://slugify.online/ to get a feel and ideas on how you should generate this.

This will be a hidden field (not visible in the admin editor), but rather dynamically generated when the title change via [payload hooks](https://payloadcms.com/docs/hooks/overview)
created_at: 2025-04-17T08:05:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,84 @@
+import type { CollectionConfig } from 'payload'
+
+import { anyone } from '@/access/anyone'
+import { authenticated } from '@/access/authenticated'
+
+export const TripReports: CollectionConfig = {
+  slug: 'trip-reports',
+  access: {
+    create: authenticated,
+    read: anyone,
+    update: authenticated,
+    delete: authenticated,
+  },
+  fields: [
+    {
+      name: 'title',
+      type: 'text',
+      required: true,
+      label: 'Trip Name',
+    },
+    {
+      name: 'status',
+      type: 'select',
+      required: true,
+      label: 'Trip Status',
+      options: [
+        { label: 'Draft', value: 'draft' },
+        { label: 'Published', value: 'published' },
+      ],
+    },
+    // TODO(@Troyabbs): uncomment this to switch this on when the exec collection is ready #issue 16
+    // {
+    //   name: 'author',
+    //   type: 'relationship',
+    //   required: true,
+    //   hasMany: true,
+    //   relationTo: 'execs',
+    // },
+    {
+      name: 'tripDate',
+      type: 'date',
+      label: 'Trip Date',
+    },
+    // TODO(@f13322): uncomment this to switch this on when the event collection is ready #issue 34
+    // {
+    //   name: 'relatedEvent',
+    //   type: 'relationship',
+    //   relationTo: 'events',
+    //   required: false,
+    //   label: 'Related Event',
+    // },
+    {
+      name: 'relatedRiver',
+      type: 'relationship',
+      required: false,
+      relationTo: 'rivers',
+      label: 'Related River',
+    },
+    {
+      name: 'content',
+      type: 'richText',
+      label: 'Trip Description',
path: src/collections/tripReports.ts
user: login: dyzhuu
body: maybe we could rename this label as just "Content" as well
created_at: 2025-04-17T08:06:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,84 @@
+import type { CollectionConfig } from 'payload'
+
+import { anyone } from '@/access/anyone'
+import { authenticated } from '@/access/authenticated'
+
+export const TripReports: CollectionConfig = {
+  slug: 'trip-reports',
+  access: {
+    create: authenticated,
+    read: anyone,
+    update: authenticated,
+    delete: authenticated,
+  },
+  fields: [
path: src/collections/tripReports.ts
user: login: dyzhuu
body: can we also think about the ideal order to present these fields in? Personally I think it makes the most sense to have the rich text editor at the very end, i'll leave the hierarchy of the other fields up to you
created_at: 2025-04-17T08:09:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,84 @@
+import type { CollectionConfig } from 'payload'
+
+import { anyone } from '@/access/anyone'
+import { authenticated } from '@/access/authenticated'
+
+export const TripReports: CollectionConfig = {
+  slug: 'trip-reports',
+  access: {
+    create: authenticated,
+    read: anyone,
+    update: authenticated,
+    delete: authenticated,
+  },
+  fields: [
+    {
+      name: 'title',
+      type: 'text',
+      required: true,
+      label: 'Trip Name',
+    },
+    {
+      name: 'status',
+      type: 'select',
+      required: true,
+      label: 'Trip Status',
+      options: [
+        { label: 'Draft', value: 'draft' },
+        { label: 'Published', value: 'published' },
+      ],
+    },
+    // TODO(@Troyabbs): uncomment this to switch this on when the exec collection is ready #issue 16
+    // {
+    //   name: 'author',
+    //   type: 'relationship',
+    //   required: true,
+    //   hasMany: true,
+    //   relationTo: 'execs',
+    // },
+    {
+      name: 'tripDate',
+      type: 'date',
+      label: 'Trip Date',
+    },
+    // TODO(@f13322): uncomment this to switch this on when the event collection is ready #issue 34
+    // {
+    //   name: 'relatedEvent',
+    //   type: 'relationship',
+    //   relationTo: 'events',
+    //   required: false,
+    //   label: 'Related Event',
+    // },
+    {
+      name: 'relatedRiver',
+      type: 'relationship',
+      required: false,
+      relationTo: 'rivers',
+      label: 'Related River',
+    },
+    {
+      name: 'content',
+      type: 'richText',
+      label: 'Trip Description',
+    },
+    {
+      name: 'location',
+      type: 'text',
+      label: 'Trip Location',
+    },
+    {
+      name: 'gallery',
+      type: 'relationship',
+      hasMany: true,
+      relationTo: 'media',
+      label: 'Trip Gallery',
+    },
path: src/collections/tripReports.ts
user: login: dyzhuu
body: minor thing, but would be nice to be able to preview the images instead of showing just the name -- do you think this is possible?
<img width="871" alt="image" src="https://github.com/user-attachments/assets/aee4b547-92dd-489a-a11b-f4b98b50b524" />

created_at: 2025-04-17T09:01:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,84 @@
+import type { CollectionConfig } from 'payload'
+
+import { anyone } from '@/access/anyone'
+import { authenticated } from '@/access/authenticated'
+
+export const TripReports: CollectionConfig = {
+  slug: 'trip-reports',
+  access: {
+    create: authenticated,
+    read: anyone,
+    update: authenticated,
+    delete: authenticated,
+  },
+  fields: [
+    {
+      name: 'title',
+      type: 'text',
+      required: true,
+      label: 'Trip Name',
+    },
+    {
+      name: 'status',
+      type: 'select',
+      required: true,
+      label: 'Trip Status',
+      options: [
+        { label: 'Draft', value: 'draft' },
+        { label: 'Published', value: 'published' },
+      ],
+    },
+    // TODO(@Troyabbs): uncomment this to switch this on when the exec collection is ready #issue 16
+    // {
+    //   name: 'author',
+    //   type: 'relationship',
+    //   required: true,
+    //   hasMany: true,
+    //   relationTo: 'execs',
+    // },
+    {
+      name: 'tripDate',
+      type: 'date',
+      label: 'Trip Date',
+    },
+    // TODO(@f13322): uncomment this to switch this on when the event collection is ready #issue 34
+    // {
+    //   name: 'relatedEvent',
+    //   type: 'relationship',
+    //   relationTo: 'events',
+    //   required: false,
+    //   label: 'Related Event',
+    // },
+    {
+      name: 'relatedRiver',
+      type: 'relationship',
+      required: false,
+      relationTo: 'rivers',
+      label: 'Related River',
+    },
+    {
+      name: 'content',
+      type: 'richText',
+      label: 'Trip Description',
+    },
+    {
+      name: 'location',
+      type: 'text',
+      label: 'Trip Location',
+    },
+    {
+      name: 'gallery',
+      type: 'relationship',
+      hasMany: true,
+      relationTo: 'media',
+      label: 'Trip Gallery',
+    },
path: src/collections/tripReports.ts
user: login: neryadom
body: Changing the type from relationship to upload as above should enable the thumbnail preview.
created_at: 2025-04-18T23:54:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,84 @@
+import type { CollectionConfig } from 'payload'
+
+import { anyone } from '@/access/anyone'
+import { authenticated } from '@/access/authenticated'
+
+export const TripReports: CollectionConfig = {
+  slug: 'trip-reports',
+  access: {
+    create: authenticated,
+    read: anyone,
+    update: authenticated,
+    delete: authenticated,
+  },
+  fields: [
+    {
+      name: 'title',
+      type: 'text',
+      required: true,
+      label: 'Trip Name',
+    },
+    {
+      name: 'status',
+      type: 'select',
+      required: true,
+      label: 'Trip Status',
+      options: [
+        { label: 'Draft', value: 'draft' },
+        { label: 'Published', value: 'published' },
+      ],
+    },
+    // TODO(@Troyabbs): uncomment this to switch this on when the exec collection is ready #issue 16
+    // {
+    //   name: 'author',
+    //   type: 'relationship',
+    //   required: true,
+    //   hasMany: true,
+    //   relationTo: 'execs',
+    // },
+    {
+      name: 'tripDate',
+      type: 'date',
+      label: 'Trip Date',
+    },
+    // TODO(@f13322): uncomment this to switch this on when the event collection is ready #issue 34
+    // {
+    //   name: 'relatedEvent',
+    //   type: 'relationship',
+    //   relationTo: 'events',
+    //   required: false,
+    //   label: 'Related Event',
+    // },
+    {
+      name: 'relatedRiver',
+      type: 'relationship',
+      required: false,
+      relationTo: 'rivers',
+      label: 'Related River',
+    },
+    {
+      name: 'content',
+      type: 'richText',
+      label: 'Trip Description',
path: src/collections/tripReports.ts
user: login: neryadom
body: Renamed.
created_at: 2025-04-18T23:54:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,84 @@
+import type { CollectionConfig } from 'payload'
+
+import { anyone } from '@/access/anyone'
+import { authenticated } from '@/access/authenticated'
+
+export const TripReports: CollectionConfig = {
+  slug: 'trip-reports',
+  access: {
+    create: authenticated,
+    read: anyone,
+    update: authenticated,
+    delete: authenticated,
+  },
+  fields: [
path: src/collections/tripReports.ts
user: login: neryadom
body: Sent the content field to the end. Other fields seem to make sense in the order they are in at the moment
created_at: 2025-04-18T23:55:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,84 @@
+import type { CollectionConfig } from 'payload'
+
+import { anyone } from '@/access/anyone'
+import { authenticated } from '@/access/authenticated'
+
+export const TripReports: CollectionConfig = {
+  slug: 'trip-reports',
+  access: {
+    create: authenticated,
+    read: anyone,
+    update: authenticated,
+    delete: authenticated,
+  },
+  fields: [
+    {
+      name: 'title',
+      type: 'text',
+      required: true,
+      label: 'Trip Name',
+    },
+    {
+      name: 'status',
+      type: 'select',
+      required: true,
+      label: 'Trip Status',
+      options: [
+        { label: 'Draft', value: 'draft' },
+        { label: 'Published', value: 'published' },
+      ],
+    },
+    // TODO(@Troyabbs): uncomment this to switch this on when the exec collection is ready #issue 16
+    // {
+    //   name: 'author',
+    //   type: 'relationship',
+    //   required: true,
+    //   hasMany: true,
+    //   relationTo: 'execs',
+    // },
+    {
+      name: 'tripDate',
+      type: 'date',
+      label: 'Trip Date',
+    },
+    // TODO(@f13322): uncomment this to switch this on when the event collection is ready #issue 34
+    // {
+    //   name: 'relatedEvent',
+    //   type: 'relationship',
+    //   relationTo: 'events',
+    //   required: false,
+    //   label: 'Related Event',
+    // },
+    {
+      name: 'relatedRiver',
+      type: 'relationship',
+      required: false,
+      relationTo: 'rivers',
+      label: 'Related River',
+    },
+    {
+      name: 'content',
+      type: 'richText',
+      label: 'Trip Description',
+    },
+    {
+      name: 'location',
+      type: 'text',
+      label: 'Trip Location',
+    },
+    {
+      name: 'gallery',
+      type: 'relationship',
+      hasMany: true,
+      relationTo: 'media',
+      label: 'Trip Gallery',
+    },
+    {
+      name: 'slug',
+      type: 'text',
+      unique: true,
+      index: true,
+      label: 'Slug',
+    },
path: src/collections/tripReports.ts
user: login: neryadom
body: This should do it. 
created_at: 2025-04-19T00:30:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,102 @@
+import type { CollectionConfig } from 'payload'
+import slugify from 'slugify'
+
+import { anyone } from '@/access/anyone'
+import { authenticated } from '@/access/authenticated'
+
+export const TripReports: CollectionConfig = {
+  slug: 'trip-reports',
+  access: {
+    create: authenticated,
+    read: anyone,
+    update: authenticated,
+    delete: authenticated,
+  },
+  hooks: {
+    beforeChange: [
+      ({ data, originalDoc }) => {
+        if (data.title && data.title !== originalDoc?.title) {
+          data.slug = slugify(data.title, {
+            lower: true,
+            strict: true,
+          })
+        }
+        return data
+      },
+    ],
+  },
+  fields: [
+    {
+      name: 'title',
+      type: 'text',
+      required: true,
+      label: 'Trip Name',
+    },
+    {
+      name: 'status',
+      type: 'select',
+      required: true,
+      label: 'Trip Status',
+      options: [
+        { label: 'Draft', value: 'draft' },
+        { label: 'Published', value: 'published' },
+      ],
+    },
+    // TODO(@Troyabbs): uncomment this to switch this on when the exec collection is ready #issue 16
+    // {
+    //   name: 'author',
+    //   type: 'relationship',
+    //   required: true,
+    //   hasMany: true,
+    //   relationTo: 'execs',
+    // },
+    {
+      name: 'tripDate',
+      type: 'date',
+      label: 'Trip Date',
+    },
+    {
+      name: 'location',
+      type: 'text',
+      label: 'Trip Location',
+    },
+    {
+      name: 'relatedEvent',
+      type: 'relationship',
+      relationTo: 'events',
+      required: false,
+      label: 'Related Event',
+    },
+    {
+      name: 'relatedRiver',
+      type: 'relationship',
+      required: false,
+      relationTo: 'rivers',
+      label: 'Related River',
+    },
+    {
+      name: 'gallery',
+      type: 'upload',
+      hasMany: true,
+      relationTo: 'media',
+      label: 'Trip Gallery',
+    },
+    {
+      name: 'slug',
+      type: 'text',
+      unique: true,
+      index: true,
+      label: 'Slug',
+      admin: {
+        readOnly: true,
+        description: 'Automatically generated from title',
+        hidden: true,
+      },
+    },
+    {
+      name: 'content',
+      type: 'richText',
+      label: 'content',
path: src/collections/tripReports.ts
user: login: dyzhuu
body: Capitalize
created_at: 2025-04-19T06:24:04Z
author_association: CONTRIBUTOR
path: package-lock.json
user: login: dyzhuu
body: delete this file
created_at: 2025-04-19T06:28:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,102 @@
+import type { CollectionConfig } from 'payload'
+import slugify from 'slugify'
+
+import { anyone } from '@/access/anyone'
+import { authenticated } from '@/access/authenticated'
+
+export const TripReports: CollectionConfig = {
+  slug: 'trip-reports',
+  access: {
+    create: authenticated,
+    read: anyone,
+    update: authenticated,
+    delete: authenticated,
+  },
+  hooks: {
+    beforeChange: [
+      ({ data, originalDoc }) => {
+        if (data.title && data.title !== originalDoc?.title) {
+          data.slug = slugify(data.title, {
+            lower: true,
+            strict: true,
+          })
+        }
+        return data
+      },
+    ],
+  },
path: src/collections/tripReports.ts
user: login: dyzhuu
body: nice!
created_at: 2025-04-19T06:28:47Z
author_association: CONTRIBUTOR
path: package-lock.json
user: login: neryadom
body: Not sure why it keeps being made will be on the lookout for that in the future!
created_at: 2025-04-19T07:24:57Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-04-16T06:37:27Z
body: 🚀 Preview deployment is live: [Preview Link](https://aucc-preview-pr-40.fly.dev)
author_association: NONE
user: login: neryadom
created_at: 2025-04-19T01:03:09Z
body: Also, I am getting an enum error when launching the CMS panel; could it be due to case sensitivity in the events.ts collection? I am not sure if this just my computer or everyone's. Refreshing page then removes error message.
![image](https://github.com/user-attachments/assets/dccaf263-8cf4-4123-9753-ddaa400b1908)

author_association: CONTRIBUTOR
user: login: dyzhuu
created_at: 2025-04-19T06:30:33Z
body: > Also, I am getting an enum error when launching the CMS panel; could it be due to case sensitivity in the events.ts collection? I am not sure if this just my computer or everyone's. Refreshing page then removes error message. ![image](https://private-user-images.githubusercontent.com/167057409/435342862-dccaf263-8cf4-4123-9753-ddaa400b1908.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDUwNDQ0NTEsIm5iZiI6MTc0NTA0NDE1MSwicGF0aCI6Ii8xNjcwNTc0MDkvNDM1MzQyODYyLWRjY2FmMjYzLThjZjQtNDEyMy05NzUzLWRkYWE0MDBiMTkwOC5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjUwNDE5JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI1MDQxOVQwNjI5MTFaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT02MTZhNjZjZWVlYWQ5NmZmMTgzYzkxY2UyMmQ0NmQyNmRmMTNlOTJiMmY2YTVkMWI5YTA2ZTFkNmIzZTZmMjkzJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.AWn5PD4-IboN6JcA-ETMRjEm5eWt2XtBcMAOfLhNB2Q)

@neryadom you're right, seems to be a casing issue. I've pushed a fix up at https://github.com/UoaWDCC/aucc/pull/42, you could approve this and then rebase from main :)

EDIT: looks like its been merged
author_association: CONTRIBUTOR