Add 71 new skills: Spark Intelligence, SupaRalph, PayloadCMS, Frontend-Design, Ralph, and Vibeship ecosystem integration
This commit is contained in:
790
skills/payloadcms-cms/SKILL.md
Normal file
790
skills/payloadcms-cms/SKILL.md
Normal file
@@ -0,0 +1,790 @@
|
||||
# PayloadCMS Skill for QwenClaw
|
||||
|
||||
## Overview
|
||||
|
||||
This skill provides **PayloadCMS expertise** to QwenClaw, enabling it to build, configure, and extend Payload CMS projects. Payload is an open-source, fullstack Next.js framework that gives you instant backend superpowers.
|
||||
|
||||
**Source:** https://github.com/payloadcms/payload
|
||||
|
||||
---
|
||||
|
||||
## What is PayloadCMS?
|
||||
|
||||
**Payload** is a Next.js native CMS that can be installed directly in your existing `/app` folder. It provides:
|
||||
- Full TypeScript backend and admin panel instantly
|
||||
- Server components to extend Payload UI
|
||||
- Direct database queries in server components (no REST/GraphQL needed)
|
||||
- Automatic TypeScript types for your data
|
||||
|
||||
### Key Features
|
||||
- **Next.js Native** - Runs inside your `/app` folder
|
||||
- **TypeScript First** - Automatic type generation
|
||||
- **Authentication** - Built-in auth out of the box
|
||||
- **Versions & Drafts** - Content versioning support
|
||||
- **Localization** - Multi-language content
|
||||
- **Block-Based Layout** - Visual page builder
|
||||
- **Customizable Admin** - React-based admin panel
|
||||
- **Lexical Editor** - Modern rich text editor
|
||||
- **Access Control** - Granular permissions
|
||||
- **Hooks** - Document and field-level hooks
|
||||
- **High Performance** - Optimized API
|
||||
- **Security** - HTTP-only cookies, CSRF protection
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
### Create New Project
|
||||
|
||||
```bash
|
||||
# Basic installation
|
||||
pnpx create-payload-app@latest
|
||||
|
||||
# With website template (recommended)
|
||||
pnpx create-payload-app@latest -t website
|
||||
|
||||
# From example
|
||||
npx create-payload-app --example example_name
|
||||
```
|
||||
|
||||
### Project Structure
|
||||
|
||||
```
|
||||
my-payload-app/
|
||||
├── app/
|
||||
│ ├── (payload)/
|
||||
│ │ ├── admin/
|
||||
│ │ ├── api/
|
||||
│ │ └── layout.tsx
|
||||
│ └── (frontend)/
|
||||
│ ├── page.tsx
|
||||
│ └── layout.tsx
|
||||
├── collections/
|
||||
│ ├── Users.ts
|
||||
│ └── Posts.ts
|
||||
├── payload.config.ts
|
||||
├── payload-types.ts
|
||||
└── package.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Collections & Schemas
|
||||
|
||||
### Basic Collection
|
||||
|
||||
```typescript
|
||||
// collections/Posts.ts
|
||||
import type { CollectionConfig } from 'payload';
|
||||
|
||||
export const Posts: CollectionConfig = {
|
||||
slug: 'posts',
|
||||
admin: {
|
||||
useAsTitle: 'title',
|
||||
},
|
||||
access: {
|
||||
read: () => true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'content',
|
||||
type: 'richText',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'publishedAt',
|
||||
type: 'date',
|
||||
admin: {
|
||||
date: {
|
||||
pickerAppearance: 'dayAndTime',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'status',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'Draft', value: 'draft' },
|
||||
{ label: 'Published', value: 'published' },
|
||||
],
|
||||
defaultValue: 'draft',
|
||||
},
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
### Collection with Relationships
|
||||
|
||||
```typescript
|
||||
// collections/Authors.ts
|
||||
export const Authors: CollectionConfig = {
|
||||
slug: 'authors',
|
||||
admin: {
|
||||
useAsTitle: 'name',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'email',
|
||||
type: 'email',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'avatar',
|
||||
type: 'upload',
|
||||
relationTo: 'media',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// In Posts collection
|
||||
{
|
||||
name: 'author',
|
||||
type: 'relationship',
|
||||
relationTo: 'authors',
|
||||
}
|
||||
```
|
||||
|
||||
### Blocks Field (Page Builder)
|
||||
|
||||
```typescript
|
||||
{
|
||||
name: 'layout',
|
||||
type: 'blocks',
|
||||
blocks: [
|
||||
{
|
||||
slug: 'hero',
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'subtitle',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'backgroundImage',
|
||||
type: 'upload',
|
||||
relationTo: 'media',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'content',
|
||||
fields: [
|
||||
{
|
||||
name: 'content',
|
||||
type: 'richText',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'cta',
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'buttonText',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'buttonUrl',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Access Control
|
||||
|
||||
### Field-Level Access
|
||||
|
||||
```typescript
|
||||
{
|
||||
name: 'isFeatured',
|
||||
type: 'checkbox',
|
||||
access: {
|
||||
read: () => true,
|
||||
update: ({ req }) => req.user?.role === 'admin',
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### Collection Access
|
||||
|
||||
```typescript
|
||||
access: {
|
||||
read: () => true,
|
||||
create: ({ req }) => req.user?.role === 'admin',
|
||||
update: ({ req }) => req.user?.role === 'admin',
|
||||
delete: ({ req }) => req.user?.role === 'admin',
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Hooks
|
||||
|
||||
### Before Change Hook
|
||||
|
||||
```typescript
|
||||
{
|
||||
name: 'slug',
|
||||
type: 'text',
|
||||
hooks: {
|
||||
beforeChange: [
|
||||
({ value, data }) => {
|
||||
if (!value && data.title) {
|
||||
return data.title
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]+/g, '-')
|
||||
.replace(/(^-|-$)/g, '');
|
||||
}
|
||||
return value;
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### After Change Hook
|
||||
|
||||
```typescript
|
||||
hooks: {
|
||||
afterChange: [
|
||||
async ({ doc, req, operation }) => {
|
||||
if (operation === 'create') {
|
||||
// Send welcome email
|
||||
await sendWelcomeEmail(doc.email);
|
||||
}
|
||||
return doc;
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Localization
|
||||
|
||||
```typescript
|
||||
const config: Config = {
|
||||
localization: {
|
||||
locales: [
|
||||
{ code: 'en', label: 'English' },
|
||||
{ code: 'es', label: 'Spanish' },
|
||||
{ code: 'fr', label: 'French' },
|
||||
],
|
||||
defaultLocale: 'en',
|
||||
},
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Using Payload in Server Components
|
||||
|
||||
### Query Collection
|
||||
|
||||
```typescript
|
||||
import { getPayload } from 'payload';
|
||||
import config from '@payload-config';
|
||||
|
||||
export default async function PostsPage() {
|
||||
const payload = await getPayload({ config });
|
||||
|
||||
const posts = await payload.find({
|
||||
collection: 'posts',
|
||||
where: {
|
||||
status: { equals: 'published' },
|
||||
},
|
||||
sort: '-publishedAt',
|
||||
limit: 10,
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
{posts.docs.map(post => (
|
||||
<article key={post.id}>
|
||||
<h2>{post.title}</h2>
|
||||
<p>{post.content}</p>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Query by ID
|
||||
|
||||
```typescript
|
||||
const post = await payload.findByID({
|
||||
collection: 'posts',
|
||||
id: postId,
|
||||
depth: 2, // Populate relationships
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## VPS Landing Page with PayloadCMS
|
||||
|
||||
### Collections for VPS Site
|
||||
|
||||
```typescript
|
||||
// collections/VpsPlans.ts
|
||||
export const VpsPlans: CollectionConfig = {
|
||||
slug: 'vps-plans',
|
||||
admin: {
|
||||
useAsTitle: 'name',
|
||||
},
|
||||
access: {
|
||||
read: () => true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'price',
|
||||
type: 'number',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'billingPeriod',
|
||||
type: 'select',
|
||||
options: ['monthly', 'yearly'],
|
||||
defaultValue: 'monthly',
|
||||
},
|
||||
{
|
||||
name: 'vcpuCores',
|
||||
type: 'number',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'ram',
|
||||
type: 'number',
|
||||
label: 'RAM (GB)',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'storage',
|
||||
type: 'number',
|
||||
label: 'Storage (GB)',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'bandwidth',
|
||||
type: 'text',
|
||||
label: 'Bandwidth',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'features',
|
||||
type: 'array',
|
||||
fields: [
|
||||
{
|
||||
name: 'feature',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'isPopular',
|
||||
type: 'checkbox',
|
||||
defaultValue: false,
|
||||
},
|
||||
{
|
||||
name: 'ctaText',
|
||||
type: 'text',
|
||||
defaultValue: 'Get Started',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// collections/Features.ts
|
||||
export const Features: CollectionConfig = {
|
||||
slug: 'features',
|
||||
admin: {
|
||||
useAsTitle: 'title',
|
||||
},
|
||||
access: {
|
||||
read: () => true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
type: 'richText',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'icon',
|
||||
type: 'text',
|
||||
label: 'Icon Name (Bootstrap Icons)',
|
||||
},
|
||||
{
|
||||
name: 'order',
|
||||
type: 'number',
|
||||
admin: {
|
||||
position: 'sidebar',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// collections/Testimonials.ts
|
||||
export const Testimonials: CollectionConfig = {
|
||||
slug: 'testimonials',
|
||||
admin: {
|
||||
useAsTitle: 'authorName',
|
||||
},
|
||||
access: {
|
||||
read: () => true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'authorName',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'authorTitle',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'company',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'quote',
|
||||
type: 'richText',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'avatar',
|
||||
type: 'upload',
|
||||
relationTo: 'media',
|
||||
},
|
||||
{
|
||||
name: 'rating',
|
||||
type: 'number',
|
||||
min: 1,
|
||||
max: 5,
|
||||
},
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
### Frontend Page Component
|
||||
|
||||
```typescript
|
||||
// app/(frontend)/page.tsx
|
||||
import { getPayload } from 'payload';
|
||||
import config from '@payload-config';
|
||||
import VpsPricing from './components/VpsPricing';
|
||||
import FeaturesGrid from './components/FeaturesGrid';
|
||||
|
||||
export default async function HomePage() {
|
||||
const payload = await getPayload({ config });
|
||||
|
||||
const plans = await payload.find({
|
||||
collection: 'vps-plans',
|
||||
sort: 'price',
|
||||
});
|
||||
|
||||
const features = await payload.find({
|
||||
collection: 'features',
|
||||
sort: 'order',
|
||||
});
|
||||
|
||||
return (
|
||||
<main>
|
||||
<HeroSection />
|
||||
<FeaturesGrid features={features.docs} />
|
||||
<VpsPricing plans={plans.docs} />
|
||||
<CtaSection />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Pricing Component
|
||||
|
||||
```typescript
|
||||
// app/(frontend)/components/VpsPricing.tsx
|
||||
import type { VpsPlan } from '@payload-types';
|
||||
|
||||
interface VpsPricingProps {
|
||||
plans: VpsPlan[];
|
||||
}
|
||||
|
||||
export default function VpsPricing({ plans }: VpsPricingProps) {
|
||||
return (
|
||||
<section className="py-20 bg-dark">
|
||||
<div className="container">
|
||||
<div className="text-center mb-16">
|
||||
<h2 className="text-4xl font-bold mb-4">
|
||||
Simple, Transparent Pricing
|
||||
</h2>
|
||||
<p className="text-gray-400 max-w-2xl mx-auto">
|
||||
No hidden fees. Pay only for what you use.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-3 gap-8">
|
||||
{plans.map((plan) => (
|
||||
<div
|
||||
key={plan.id}
|
||||
className={`p-8 rounded-2xl ${
|
||||
plan.isPopular
|
||||
? 'bg-dark-light border-primary border-2'
|
||||
: 'bg-dark-light border border-gray-800'
|
||||
}`}
|
||||
>
|
||||
{plan.isPopular && (
|
||||
<span className="bg-primary text-white px-3 py-1 rounded-full text-sm font-medium">
|
||||
Most Popular
|
||||
</span>
|
||||
)}
|
||||
|
||||
<h3 className="text-xl font-semibold mt-4 mb-2">
|
||||
{plan.name}
|
||||
</h3>
|
||||
|
||||
<div className="text-4xl font-bold mb-2">
|
||||
${plan.price}
|
||||
<span className="text-sm text-gray-400 font-normal">
|
||||
/{plan.billingPeriod}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<ul className="space-y-3 my-6">
|
||||
<li className="flex items-center gap-2">
|
||||
<i className="bi bi-check-circle-fill text-primary" />
|
||||
{plan.vcpuCores} vCPU Cores
|
||||
</li>
|
||||
<li className="flex items-center gap-2">
|
||||
<i className="bi bi-check-circle-fill text-primary" />
|
||||
{plan.ram} GB RAM
|
||||
</li>
|
||||
<li className="flex items-center gap-2">
|
||||
<i className="bi bi-check-circle-fill text-primary" />
|
||||
{plan.storage} GB NVMe Storage
|
||||
</li>
|
||||
<li className="flex items-center gap-2">
|
||||
<i className="bi bi-check-circle-fill text-primary" />
|
||||
{plan.bandwidth} Bandwidth
|
||||
</li>
|
||||
{plan.features?.map((feature, idx) => (
|
||||
<li key={idx} className="flex items-center gap-2">
|
||||
<i className="bi bi-check-circle-fill text-primary" />
|
||||
{feature.feature}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<button
|
||||
className={`w-full py-3 rounded-lg font-semibold ${
|
||||
plan.isPopular
|
||||
? 'bg-primary text-white'
|
||||
: 'border-2 border-gray-600 text-white'
|
||||
}`}
|
||||
>
|
||||
{plan.ctaText}
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Payload Config Example
|
||||
|
||||
```typescript
|
||||
// payload.config.ts
|
||||
import { buildConfig } from 'payload';
|
||||
import { lexicalEditor } from '@payloadcms/richtext-lexical';
|
||||
import { mongooseAdapter } from '@payloadcms/db-mongodb';
|
||||
import { nodemailerAdapter } from '@payloadcms/email-nodemailer';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
import { Users } from './collections/Users';
|
||||
import { VpsPlans } from './collections/VpsPlans';
|
||||
import { Features } from './collections/Features';
|
||||
import { Media } from './collections/Media';
|
||||
|
||||
const filename = fileURLToPath(import.meta.url);
|
||||
const dirname = path.dirname(filename);
|
||||
|
||||
export default buildConfig({
|
||||
admin: {
|
||||
user: Users.slug,
|
||||
importMap: {
|
||||
baseDir: path.resolve(dirname),
|
||||
},
|
||||
},
|
||||
collections: [
|
||||
Users,
|
||||
VpsPlans,
|
||||
Features,
|
||||
Media,
|
||||
],
|
||||
editor: lexicalEditor(),
|
||||
db: mongooseAdapter({
|
||||
url: process.env.DATABASE_URI || '',
|
||||
}),
|
||||
email: nodemailerAdapter({
|
||||
defaultFromAddress: 'noreply@example.com',
|
||||
defaultFromName: 'CloudVPS',
|
||||
}),
|
||||
typescript: {
|
||||
outputFile: path.resolve(dirname, 'payload-types.ts'),
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Usage in QwenClaw
|
||||
|
||||
### Basic Payload Project Creation
|
||||
|
||||
```
|
||||
Use the payloadcms-cms skill to create a new PayloadCMS project for a VPS hosting landing page with:
|
||||
- VPS plans collection (name, price, specs, features)
|
||||
- Features collection (title, description, icon)
|
||||
- Testimonials collection
|
||||
- Media library for images
|
||||
```
|
||||
|
||||
### Query Payload Data
|
||||
|
||||
```
|
||||
Use payloadcms-cms to query all VPS plans sorted by price and display them in a pricing table
|
||||
```
|
||||
|
||||
### Create Collection
|
||||
|
||||
```
|
||||
Use payloadcms-cms skill to create a new collection for data centers with:
|
||||
- name (text)
|
||||
- location (text)
|
||||
- region (select: US, EU, Asia)
|
||||
- features (array: DDoS protection, NVMe, 10Gbps)
|
||||
- latitude/longitude for map
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Type Safety
|
||||
```typescript
|
||||
// Always import generated types
|
||||
import type { Post, User } from '@payload-types';
|
||||
```
|
||||
|
||||
### 2. Depth for Relationships
|
||||
```typescript
|
||||
// Use depth to populate relationships
|
||||
const post = await payload.findByID({
|
||||
collection: 'posts',
|
||||
id: postId,
|
||||
depth: 2,
|
||||
});
|
||||
```
|
||||
|
||||
### 3. Access Control
|
||||
```typescript
|
||||
// Always define access control
|
||||
access: {
|
||||
read: () => true,
|
||||
create: ({ req }) => !!req.user,
|
||||
update: ({ req }) => !!req.user,
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Validation
|
||||
```typescript
|
||||
// Add validation to fields
|
||||
{
|
||||
name: 'email',
|
||||
type: 'email',
|
||||
required: true,
|
||||
validate: (value) => {
|
||||
if (value && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
|
||||
return 'Invalid email format';
|
||||
}
|
||||
return true;
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Resources
|
||||
|
||||
### Official
|
||||
- **Website**: https://payloadcms.com/
|
||||
- **GitHub**: https://github.com/payloadcms/payload
|
||||
- **Documentation**: https://payloadcms.com/docs
|
||||
- **Examples**: https://github.com/payloadcms/payload/tree/main/examples
|
||||
|
||||
### Templates
|
||||
- **Website**: `pnpx create-payload-app@latest -t website`
|
||||
- **E-commerce**: `pnpx create-payload-app@latest -t ecommerce`
|
||||
- **Blank**: `pnpx create-payload-app@latest -t blank`
|
||||
|
||||
---
|
||||
|
||||
## Skill Metadata
|
||||
|
||||
```yaml
|
||||
name: payloadcms-cms
|
||||
version: 1.0.0
|
||||
category: development
|
||||
description: PayloadCMS project creation, configuration, and development
|
||||
author: PayloadCMS Team (https://github.com/payloadcms)
|
||||
license: MIT
|
||||
tags:
|
||||
- cms
|
||||
- nextjs
|
||||
- typescript
|
||||
- react
|
||||
- backend
|
||||
- admin-panel
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Skill ready for QwenClaw integration!** 🚀
|
||||
Reference in New Issue
Block a user