From 37bca0e2d084cf8018c4e4252662f30ce2d0e0af Mon Sep 17 00:00:00 2001 From: Zhu Zhanyan Date: Tue, 23 Jan 2024 12:42:56 +0800 Subject: [PATCH] feat(cms): Add Orders Payload collection to CMS (#138) * ops(cms): docker-compose volume mount declared volume instead of bind mount * feat(cms): add Orders payload collection * feat(cms): only disable local storage of media assets when s3 is enabled * feat(cms): add orderItems to Orders collection * refactor(cms): change order status to string format Current int format is not suitable for use as api --- .../cms/docker/development/docker-compose.yml | 2 +- apps/cms/src/collections/Media.ts | 5 +- apps/cms/src/collections/Orders.ts | 116 ++++++++++++++++++ apps/cms/src/payload.config.ts | 51 ++++---- apps/cms/src/utilities/cloud.ts | 11 ++ packages/types/src/lib/cms.ts | 22 ++++ 6 files changed, 180 insertions(+), 27 deletions(-) create mode 100644 apps/cms/src/collections/Orders.ts create mode 100644 apps/cms/src/utilities/cloud.ts diff --git a/apps/cms/docker/development/docker-compose.yml b/apps/cms/docker/development/docker-compose.yml index c4fedba5..bb17b6b5 100644 --- a/apps/cms/docker/development/docker-compose.yml +++ b/apps/cms/docker/development/docker-compose.yml @@ -8,7 +8,7 @@ services: command: - --storageEngine=wiredTiger volumes: - - ./data:/data/db + - data:/data/db volumes: data: diff --git a/apps/cms/src/collections/Media.ts b/apps/cms/src/collections/Media.ts index 6336e4ac..2cafe804 100644 --- a/apps/cms/src/collections/Media.ts +++ b/apps/cms/src/collections/Media.ts @@ -1,4 +1,5 @@ import { CollectionConfig } from "payload/types"; +import { isUsingCloudStore } from "../utilities/cloud"; const Media: CollectionConfig = { slug: "media", @@ -18,7 +19,9 @@ const Media: CollectionConfig = { staticURL: "/media", staticDir: "media", mimeTypes: ["image/*"], - disableLocalStorage: true, + // disable local storage of media assets if using cloud storage + // otherwise allow local storage for local development + disableLocalStorage: isUsingCloudStore(), }, }; export default Media; diff --git a/apps/cms/src/collections/Orders.ts b/apps/cms/src/collections/Orders.ts new file mode 100644 index 00000000..8eb5e123 --- /dev/null +++ b/apps/cms/src/collections/Orders.ts @@ -0,0 +1,116 @@ +import { CollectionConfig } from "payload/types"; +import Media from "./Media"; + +/** Orders collection stores merch orders from users. */ +const Orders: CollectionConfig = { + slug: "orders", + admin: { + defaultColumns: [ + "id", + "orderItems", + "orderDateTime", + "status", + "updatedAt", + ], + description: "Merchandise orders from users.", + }, + fields: [ + // by default, payload generates an 'id' field each order automatically + { + name: "paymentGateway", + type: "text", + required: true, + }, + { + name: "status", + label: "Order Status", + type: "select", + options: [ + { + value: "pending", + label: "Pending Payment", + }, + { + value: "paid", + label: "Payment Completed", + }, + { + value: "delivered", + label: "Order Completed", + }, + ], + required: true, + }, + { + name: "customerEmail", + type: "email", + required: true, + }, + { + name: "transactionID", + label: "Transaction ID", + admin: { + description: "Transaction ID provided by Payment Gateway", + }, + type: "text", + required: true, + }, + { + name: "orderDateTime", + label: "Ordered On", + type: "date", + admin: { + date: { + pickerAppearance: "dayAndTime", + }, + }, + required: true, + }, + // ordered items for this order + { + name: "orderItems", + type: "array", + fields: [ + { + name: "image", + type: "upload", + relationTo: Media.slug, + // validation: only allow image filetypes + filterOptions: { + mimeType: { contains: "image" }, + }, + }, + { + name: "quantity", + type: "number", + required: true, + }, + { + name: "size", + type: "text", + required: true, + }, + { + name: "price", + type: "number", + required: true, + }, + { + name: "name", + type: "text", + required: true, + }, + { + name: "colorway", + type: "text", + required: true, + }, + ], + // direct paylaod to generate a OrderItem type + interfaceName: "OrderItem", + // validate: orders should not be empty + minRows: 1, + }, + ], +}; +export default Orders; diff --git a/apps/cms/src/payload.config.ts b/apps/cms/src/payload.config.ts index 48487e19..fd3c8fd5 100644 --- a/apps/cms/src/payload.config.ts +++ b/apps/cms/src/payload.config.ts @@ -1,12 +1,12 @@ -import { buildConfig } from 'payload/config'; -import { cloudStorage } from '@payloadcms/plugin-cloud-storage'; -import { s3Adapter as createS3Adapter } from '@payloadcms/plugin-cloud-storage/s3'; -import path from 'path'; +import { buildConfig } from "payload/config"; +import { cloudStorage } from "@payloadcms/plugin-cloud-storage"; +import { s3Adapter as createS3Adapter } from "@payloadcms/plugin-cloud-storage/s3"; +import path from "path"; -import Categories from './collections/Categories'; -import Posts from './collections/Posts'; -import Tags from './collections/Tags'; -import Users from './collections/Users'; +import Categories from "./collections/Categories"; +import Posts from "./collections/Posts"; +import Tags from "./collections/Tags"; +import Users from "./collections/Users"; import Media from "./collections/Media"; import AfterNavLinks from "./admin/components/AfterNavLinks"; @@ -16,6 +16,8 @@ import MerchOverview from "./admin/views/MerchOverview"; import MerchProducts from "./admin/views/MerchProducts"; import { SCSEIcon, SCSELogo } from "./admin/graphics/Logos"; import BeforeNavLinks from "./admin/components/BeforeNavLinks"; +import Order from "./collections/Orders"; +import { isUsingCloudStore } from "./utilities/cloud"; const adapter = createS3Adapter({ config: { @@ -56,20 +58,17 @@ export default buildConfig({ user: Users.slug, css: path.resolve(__dirname, "admin", "styles.scss"), }, - collections: [ - Categories, - Posts, - Tags, - Users, - Media, - ], + collections: [Categories, Posts, Tags, Users, Media, Order], csrf: [ // whitelist of domains to allow cookie auth from process.env.PAYLOAD_PUBLIC_SERVER_URL, ], typescript: { // outputFile: path.resolve(__dirname, "payload-types.ts"), - outputFile: path.resolve(__dirname, "../../../packages/types/src/lib/cms.ts"), // overridden by PAYLOAD_TS_OUTPUT_PATH env var + outputFile: path.resolve( + __dirname, + "../../../packages/types/src/lib/cms.ts" + ), // overridden by PAYLOAD_TS_OUTPUT_PATH env var }, graphQL: { schemaOutputFile: path.resolve( @@ -77,13 +76,15 @@ export default buildConfig({ "../../../packages/schemas/lib/cms.graphql" ), }, - plugins: [ - cloudStorage({ - collections: { - media: { - adapter: adapter, - } - }, - }), - ], + plugins: isUsingCloudStore() + ? [ + cloudStorage({ + collections: { + media: { + adapter: adapter, + }, + }, + }), + ] + : [], }); diff --git a/apps/cms/src/utilities/cloud.ts b/apps/cms/src/utilities/cloud.ts new file mode 100644 index 00000000..9621992b --- /dev/null +++ b/apps/cms/src/utilities/cloud.ts @@ -0,0 +1,11 @@ +/** + * Determine if we are using cloud storage (AWS S3) by detecting environment vars. + * + * @returns true if using cloud storage, false otherwise. + */ +export function isUsingCloudStore(): boolean { + return ( + process.env?.S3_ACCESS_KEY_ID != null && + process.env.S3_ACCESS_KEY_ID.length !== 0 + ); +} diff --git a/packages/types/src/lib/cms.ts b/packages/types/src/lib/cms.ts index 4e2f6a32..97f8aa8b 100644 --- a/packages/types/src/lib/cms.ts +++ b/packages/types/src/lib/cms.ts @@ -5,6 +5,16 @@ * and re-run `payload generate:types` to regenerate this file. */ +export type OrderItem = { + image?: string | Media; + quantity: number; + size: string; + price: number; + name: string; + colorway: string; + id?: string; +}[]; + export interface Config { collections: { categories: Category; @@ -12,6 +22,7 @@ export interface Config { tags: Tag; users: User; media: Media; + orders: Order; }; globals: {}; } @@ -87,3 +98,14 @@ export interface User { lockUntil?: string; password?: string; } +export interface Order { + id: string; + paymentGateway: string; + status: 'pending' | 'paid' | 'delivered'; + customerEmail: string; + transactionID: string; + orderDateTime: string; + orderItems?: OrderItem; + updatedAt: string; + createdAt: string; +}