custom button component in admin panel

default discord avatar
unonweblast year
1 2

Hi community,

apart from 'Save' I need another button in the right-side navigation which allows me to execute a server side script.
It would be great to use the same style as the default save button.
If it's not possible to add it at this place it could be any other easily accessible place.
As I'm not familiar with React adding a custom component with a new functionality is quite difficult for me.
Any help is appreciated.

  • Selected Answer
    default discord avatar
    matthijs166last year

    Hey there,

    So we have an implementation of preview buttons for our collections. I'm not really sure, but I think that you can use the Payload API in the React component and call a custom endpoint (https://payloadcms.com/docs/rest-api/overview#custom-endpoints)

    Field config

    import { Field } from "payload/types";
    import { PreviewButtonField } from "./components/PreviewButtonField";
    
    export const PreviewButtons: Field = {
        name: "previewButtons",
        label: "Voorbeeld",
        type: "ui",
        admin: {
            position: "sidebar",
            components: {
                Field: PreviewButtonField,
            }
        }
    }

    React component:

    import { Button } from "payload/components";
    import { Label, useFormFields } from "payload/components/forms";
    import React from "react";
    import "./index.scss";
    
    type Props = {
        label?: string;
    };
    
    export const PreviewButtonField: React.FC = ({ label }: Props) => {
        const url = useFormFields(([fields]) => fields.url);
    
        return (
            <>
                <Label label={label} />
                <div className="preview-buttons">
                    <a href={`${process.env.PAYLOAD_PUBLIC_FRONDEND_SERVER_URL ?? ''}${url.value}`} target="live-preview">
                        <Button
                            className="preview-button"
                            buttonStyle="primary"
                        >
                            Live
                        </Button>
                    </a>
                    <a href={`${process.env.PAYLOAD_PUBLIC_FRONDEND_SERVER_URL ?? ''}${url.value}?draft=true`} target="draft-preview">
                        <Button
                            className="preview-button"
                            buttonStyle="secondary"
                        >
                            Concept
                        </Button>
                    </a>
                </div>
            </>
        );
    };

    And the styling:

    .preview-buttons {
      display: flex;
      gap: 10px;
      margin-bottom: 10px;
    
      a,
      button {
        width: 100%;
        margin: 0;
      }
    }
    1 reply
  • default discord avatar
    unonweblast year

    Hi Matthijs, that's a great starting point! I appreciate that a lot!

  • default discord avatar
    unonweb7 months ago

    In order to contribute at least something, here's my implementation of a custom "publish" button component:

    // deploy-button/index.js
    export const deployButton = () => {
      let siteID
      useFormFields(([fields]) => {
      siteID = fields.site.value
      });
      
      return (
      <>
        <div className="deploy-button-wrapper">
          <a href={`/deploy-site?site=${siteID}`} target="_blank">
            <button
              className="deploy-button"
              buttonStyle="secondary">Publish
            </button>
          </a>
          <div className="field-description">Alle Änderungen hochladen. Das kann etwas
            dauern.</div>
          <ul className="field-description">
            <li>Erst speichern</li>
            <li>Erst am Ende der Sitzung ausführen</li>
          </ul>
        </div>
      </>
      );
    };

    Here the custom component is imported into a ui field:

    // deployButtonField.js
    import { deployButton } from '../components/deploy-button'
    
    export const deployButtonField = {
      type: 'ui',
      name: 'deployButton',
      label: {
        de: 'Veröffentlichen',
        en: 'Publish'
      },
      admin: {
        position: "sidebar",
        components: {
          Field: deployButton,
        }
      }
    }

    Here, in server.js a custom route is used to trigger upload to hoster:

    // server.js
    
    /* route: deploy-site */
    const routerDeploy = express.Router()
    routerDeploy.use(payload.authenticate)
    routerDeploy.use(async (req, res, next) => {
      try {
        if (req.user) {
          const siteID = req.query.site
          const site = await req.payload.findByID({ collection: 'sites', id: siteID, depth: 0 })
          const cmd = `/home/payload/cms/scripts/un-deploy-site.sh ${site.domainShort}`
          res.redirect(`https://${site.domain}`)
          exec(cmd, (error, stdout, stderr) => {
            if (error) {
              log(`exec error: ${error}`, req.user.shortName, __filename)
              return
            }
            if (stderr) {
              log(`exec stderr: ${stderr}`, req.user.shortName, __filename)
            }
            log(`exec stdout: ${stdout}`, req.user.shortName, __filename)
          })
        }
        else {
          next()
        }
      } catch (error) {
        log(error, '', __filename, 3)
        mailError(error)
      }
    
    })
    
    app.use('/deploy-site', routerDeploy)
Star on GitHub

Star

Chat on Discord

Discord

online

Can't find what you're looking for?

Get help straight from the Payload team with an Enterprise License.