Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 129 additions & 0 deletions src/app/(private)/data-sources/new/fields/PayloadCMSFields.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import FormFieldWrapper from "@/components/forms/FormFieldWrapper";
import { DataSourceType } from "@/server/models/DataSource";
import { Badge } from "@/shadcn/ui/badge";
import { Input } from "@/shadcn/ui/input";
import type { PayloadCMSConfig } from "@/server/models/DataSource";

export default function PayloadCMSFields({
config,
onChange,
}: {
config: Partial<PayloadCMSConfig>;
onChange: (
config: Partial<
Pick<PayloadCMSConfig, "apiBaseUrl" | "apiKey" | "collectionName">
>,
) => void;
}) {
if (config.type !== DataSourceType.PayloadCMS) return;

return (
<>
<FormFieldWrapper
label="API Base URL"
id="apiBaseUrl"
helpText={ApiBaseUrlHelpText}
hint="The base URL of your PayloadCMS instance (e.g., https://your-site.com)"
>
<Input
type="url"
required
className="w-full"
id="apiBaseUrl"
placeholder="https://your-site.com"
value={config.apiBaseUrl || ""}
onChange={(e) => onChange({ apiBaseUrl: e.target.value })}
/>
</FormFieldWrapper>
<FormFieldWrapper
label="Collection Name"
id="collectionName"
helpText={CollectionNameHelpText}
hint="The slug of the collection you want to import (e.g., posts, pages)"
>
<Input
type="text"
required
id="collectionName"
className="w-full"
placeholder="posts"
value={config.collectionName || ""}
onChange={(e) => onChange({ collectionName: e.target.value })}
/>
</FormFieldWrapper>
<FormFieldWrapper
label="API Key"
id="apiKey"
helpText={ApiKeyHelpText}
hint="Your PayloadCMS API key for authentication"
>
<Input
type="password"
required
className="w-full"
id="apiKey"
placeholder="your-api-key"
value={config.apiKey || ""}
onChange={(e) => onChange({ apiKey: e.target.value })}
/>
</FormFieldWrapper>
</>
);
}

const ApiBaseUrlHelpText = (
<>
<h3>API Base URL</h3>
<p>
This is the base URL of your PayloadCMS instance. It should include the
protocol (https://) but not the <Badge variant="secondary">/api</Badge>{" "}
path.
</p>
<p>Example: https://your-site.com</p>
</>
);

const CollectionNameHelpText = (
<>
<h3>Collection Name</h3>
<p>
The collection slug as defined in your PayloadCMS configuration. This is
the name used in the API endpoints.
</p>
<p>
For example, if your collection is accessible at{" "}
<Badge variant="secondary">/api/posts</Badge>, the collection name is{" "}
<Badge variant="secondary">posts</Badge>.
</p>
</>
);

const ApiKeyHelpText = (
<>
<h3>How to generate an API Key in PayloadCMS</h3>
<ol>
<li>Log in to your PayloadCMS admin panel.</li>
<li>
Navigate to the Users collection (or wherever API keys are configured
in your instance).
</li>
<li>
Create a new API key with permissions to read and write data in your
collection.
</li>
<li>Copy the API key and paste it here.</li>
</ol>
<p>
For more information, see the{" "}
<a
href="https://payloadcms.com/docs/authentication/api-keys#api-key-only-auth"
target="_blank"
rel="noopener noreferrer"
className="underline"
>
PayloadCMS API Keys documentation
</a>
.
</p>
</>
);
3 changes: 3 additions & 0 deletions src/app/(private)/data-sources/new/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import AirtableFields from "./fields/AirtableFields";
import CSVFields from "./fields/CSVFields";
import GoogleSheetsFields from "./fields/GoogleSheetsFields";
import MailchimpFields from "./fields/MailchimpFields";
import PayloadCMSFields from "./fields/PayloadCMSFields";

import { type NewDataSourceConfig, defaultStateSchema } from "./schema";
import type {
Expand Down Expand Up @@ -252,6 +253,8 @@ function ConfigFields({
);
case DataSourceType.Mailchimp:
return <MailchimpFields config={config} onChange={onChange} />;
case DataSourceType.PayloadCMS:
return <PayloadCMSFields config={config} onChange={onChange} />;
default:
return null;
}
Expand Down
2 changes: 2 additions & 0 deletions src/app/(private)/data-sources/new/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
csvConfigSchema,
googleSheetsConfigSchema,
mailchimpConfigSchema,
payloadCMSConfigSchema,
} from "@/server/models/DataSource";

export const newCSVConfigSchema = csvConfigSchema.extend({
Expand All @@ -22,6 +23,7 @@ export const newDataSourceConfigSchema = z.discriminatedUnion("type", [
mailchimpConfigSchema,
googleSheetsConfigSchema,
newCSVConfigSchema,
payloadCMSConfigSchema,
]);

export type NewDataSourceConfig = z.infer<typeof newDataSourceConfigSchema>;
Expand Down
28 changes: 28 additions & 0 deletions src/components/DataSourceIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -293,12 +293,40 @@ const ActionNetworkSVG = () => (
</svg>
);

const PayloadCMSSVG = () => (
<svg
width="20"
height="20"
viewBox="0 0 256 256"
xmlns="http://www.w3.org/2000/svg"
>
<rect width="256" height="256" fill="none" />
<path
d="M128 32L32 96v128l96 64 96-64V96L128 32z"
fill="#000000"
stroke="#000000"
strokeWidth="8"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M128 96v128M32 96l96 64 96-64"
stroke="#FFFFFF"
strokeWidth="8"
strokeLinecap="round"
strokeLinejoin="round"
fill="none"
/>
</svg>
);

const dataSourceIcons: Record<DataSourceType, React.ReactNode> = {
[DataSourceType.ActionNetwork]: <ActionNetworkSVG />,
[DataSourceType.Airtable]: <AirtableIconSVG />,
[DataSourceType.CSV]: <File size={16} />,
[DataSourceType.GoogleSheets]: <GoogleSheetsIconSVG />,
[DataSourceType.Mailchimp]: <MailchimpIconSVG />,
[DataSourceType.PayloadCMS]: <PayloadCMSSVG />,
};

const DataSourceIcon = ({
Expand Down
5 changes: 5 additions & 0 deletions src/features.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,9 @@ export const DataSourceFeatures: Record<
autoImport: true,
enrichment: false,
},
[DataSourceType.PayloadCMS]: {
autoEnrich: true,
autoImport: true,
enrichment: false,
},
};
7 changes: 6 additions & 1 deletion src/labels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type {
csvConfigSchema,
googleSheetsConfigSchema,
mailchimpConfigSchema,
payloadCMSConfigSchema,
} from "./server/models/DataSource";
import type { DataSourceType } from "@/server/models/DataSource";
import type z from "zod";
Expand All @@ -31,11 +32,14 @@ type DataSourceConfigKey =
| keyof z.infer<typeof airtableConfigSchema>
| keyof z.infer<typeof googleSheetsConfigSchema>
| keyof z.infer<typeof mailchimpConfigSchema>
| keyof z.infer<typeof csvConfigSchema>;
| keyof z.infer<typeof csvConfigSchema>
| keyof z.infer<typeof payloadCMSConfigSchema>;

export const DataSourceConfigLabels: Record<DataSourceConfigKey, string> = {
apiKey: "API Key",
apiBaseUrl: "API Base URL",
baseId: "Base ID",
collectionName: "Collection Name",
listId: "List ID",
oAuthCredentials: "OAuth Credentials",
sheetName: "Sheet Name",
Expand All @@ -51,6 +55,7 @@ export const DataSourceTypeLabels: Record<DataSourceType, string> = {
csv: "CSV",
googlesheets: "Google Sheets",
mailchimp: "Mailchimp",
payloadcms: "PayloadCMS",
};

export const EnrichmentSourceTypeLabels: Record<EnrichmentSourceType, string> =
Expand Down
7 changes: 7 additions & 0 deletions src/server/adaptors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { AirtableAdaptor } from "./airtable";
import { CSVAdaptor } from "./csv";
import { GoogleSheetsAdaptor } from "./googlesheets";
import { MailchimpAdaptor } from "./mailchimp";
import { PayloadCMSAdaptor } from "./payloadcms";
import type { DataSourceConfig } from "../models/DataSource";

export const getDataSourceAdaptor = (dataSource: {
Expand Down Expand Up @@ -35,6 +36,12 @@ export const getDataSourceAdaptor = (dataSource: {
);
case DataSourceType.Mailchimp:
return new MailchimpAdaptor(id, config.apiKey, config.listId);
case DataSourceType.PayloadCMS:
return new PayloadCMSAdaptor(
config.apiBaseUrl,
config.apiKey,
config.collectionName,
);
default:
logger.error(`Unimplemented data source type: ${dataSourceType}`);
return null;
Expand Down
Loading
Loading