Created
August 5, 2024 20:38
-
-
Save mikecebul/f7c5c4328ada36168b7dcf241cda57ee to your computer and use it in GitHub Desktop.
Payload array field component for Link Cards. fetchRandomImage() hook to fetch an image from Unsplash to speed up editing. AddHTTPS hook to fix to add 'https://' to the url. And RowLabel() to use title as row label.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { FieldHook, FieldHookArgs } from "payload"; | |
export const addHTTPS: FieldHook = ({ operation, value }) => { | |
if (operation === "create" || operation === "update") { | |
if ( | |
typeof value === "string" && | |
!value.startsWith("https://") && | |
!value.startsWith("http://") | |
) | |
return `https://${value}`; | |
} | |
return value; | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { FieldHook } from "payload"; | |
import fs from "fs"; | |
import path, { extname } from "path"; | |
import { fileTypeFromBuffer } from "file-type"; | |
import { generateSlug } from "@/shared/utils"; | |
async function storeFileLocally( | |
url: string, | |
fileName: string | |
): Promise<string> { | |
const response = await fetch(url); | |
const buffer = await response.arrayBuffer(); | |
const data = Buffer.from(buffer); | |
const fileExtension = extname(url).slice(1); | |
const filePath = `/tmp/${fileName}.${fileExtension}`; | |
fs.writeFileSync(filePath, data); | |
return filePath; | |
} | |
export const fetchRandomImage = | |
(query: string[]): FieldHook => | |
async ({ value, req, siblingData }) => { | |
if (!value) { | |
const url = `https://api.unsplash.com/photos/random?client_id=${ | |
process.env.UNSPLASH_ACCESS_KEY | |
}&query=${query.join(",")}`; | |
try { | |
const response = await fetch(url); | |
const data = await response.json(); | |
const imageUrl = data.urls.regular; | |
const altDescription = data.alt_description; | |
const fileName = data.slug; | |
const localFilePath = await storeFileLocally(imageUrl, fileName); | |
// Read the file buffer | |
const fileBuffer = fs.readFileSync(localFilePath); | |
const fileType = await fileTypeFromBuffer(fileBuffer); | |
const mimeType = fileType?.mime ?? "image/jpeg"; | |
// Create the image file object | |
const imageFile = { | |
data: fileBuffer, | |
mimetype: mimeType, | |
name: generateSlug(siblingData.title), | |
size: fileBuffer.length, | |
}; | |
// Upload the image to the media collection | |
const image = await req.payload.create({ | |
collection: "media", | |
data: { | |
alt: altDescription, | |
}, | |
file: imageFile, | |
req, | |
}); | |
// Clean up the temporary file | |
fs.unlinkSync(localFilePath); | |
return image.id; | |
} catch (error) { | |
console.error("Error uploading image:", error); | |
} | |
} | |
return value; | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Field } from "payload"; | |
import { RowLabel } from "./RowLabel"; | |
import { addHTTPS } from "./addHTTPS"; | |
import { fetchRandomImage } from "./fetchRandomImage"; | |
export const linkCards: Field = { | |
name: "links", | |
type: "array", | |
interfaceName: "LinkCards", | |
admin: { | |
components: { | |
RowLabel, | |
}, | |
}, | |
fields: [ | |
{ | |
name: "title", | |
type: "text", | |
required: true, | |
}, | |
{ | |
name: "description", | |
type: "textarea", | |
required: true, | |
}, | |
{ | |
name: "imageUploadOption", | |
label: "Image Upload Option", | |
type: "radio", | |
defaultValue: "generate", | |
admin: { | |
condition: (_, siblingData) => !siblingData.image, | |
}, | |
options: [ | |
{ | |
label: "Generate image", | |
value: "generate", | |
}, | |
{ | |
label: "Upload image", | |
value: "manual", | |
}, | |
], | |
}, | |
{ | |
name: "image", | |
type: "upload", | |
relationTo: "media", | |
hooks: { | |
beforeChange: [fetchRandomImage(["child", "adhd"])], | |
}, | |
admin: { | |
condition: (_, siblingData) => { | |
return ( | |
!!siblingData.image || | |
(siblingData.imageUploadOption && | |
siblingData.imageUploadOption === "manual") | |
); | |
}, | |
}, | |
}, | |
{ | |
name: "href", | |
label: "Url", | |
type: "text", | |
required: true, | |
hooks: { | |
beforeValidate: [addHTTPS], | |
}, | |
}, | |
], | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use client"; | |
import type { RowLabelComponent } from "payload"; | |
import { useRowLabel } from "@payloadcms/ui"; | |
export const RowLabel: RowLabelComponent = () => { | |
const { data, rowNumber } = useRowLabel<{ title: string }>(); | |
return ( | |
<div className="text-orange-400 capitalize"> | |
{`${rowNumber} - ${data.title || "Untitled"}`} | |
</div> | |
); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment