SoftwareBrothers / adminjs

AdminJS is an admin panel for apps written in node.js

Home Page:https://adminjs.co

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

How to update mongoDB with custom component in list

vadymfrlv opened this issue · comments

Contact Details

No response

What happened?

In the list I created a custom component (checkbox). When I checked it or unchecked must be a record to DB to change status like false or true.
Could not understand what is wrong while trying to update a param of an object in DB through custom component in adminjs.

Receiving
Failed to load resource: the server responded with a status of 500 (Internal Server Error)
`


TypeError: Cannot read properties of undefined (reading 'params')


   at handler (file:///Users/vadym.f/Documents/dev/admin-test/offerStatus.js:66:36)


   at file:///Users/vadym.f/Documents/dev/admin-test/node_modules/adminjs/lib/backend/decorators/action/action-decorator.js:98:90


   at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

`

Bug prevalence

Every time I try to make a check

AdminJS dependencies version

{
"name": "admin-test",
"version": "1.0.0",
"description": "Admin test",
"main": "index.js",
"type": "module",
"scripts": {
"start": "nodemon index.js"
},
"author": "frlv",
"license": "ISC",
"dependencies": {
"@adminjs/design-system": "^4.1.0",
"@adminjs/express": "^6.1.0",
"@adminjs/mongoose": "^4.1.0",
"@babel/register": "^7.23.7",
"adminjs": "^7.7.0",
"express": "^4.18.3",
"express-formidable": "^1.2.0",
"mongoose": "^8.2.1"
},
"devDependencies": {
"@babel/cli": "^7.23.9",
"@babel/core": "^7.24.0",
"@babel/preset-env": "^7.24.0",
"@babel/preset-react": "^7.23.3",
"nodemon": "^3.1.0"
}
}

What browsers do you see the problem on?

No response

Relevant log output

No response

Relevant code that's giving you issues

//
index.js
//
import AdminJS from "adminjs"
import { buildRouter as AdminJSExpress } from "@adminjs/express"
import express from "express"
import mongoose from "mongoose"
import * as AdminJSMongoose from "@adminjs/mongoose"

import { componentLoader } from "./componentLoader.js"
import { OfferStatus } from "./offerStatus.js"

const PORT = 3000
const DB =
  "mongodb+srv:/..."

AdminJS.registerAdapter({
  Resource: AdminJSMongoose.Resource,
  Database: AdminJSMongoose.Database,
})

const startAdminJS = async () => {
  try {
    await mongoose.connect(DB, {
      useNewUrlParser: true,
      useUnifiedTopology: true,
    })
    console.log("Connected to MongoDB")

    const app = express()

    const admin = new AdminJS({
      resources: [OfferStatus],
      componentLoader: componentLoader,
    })

    const adminRouter = AdminJSExpress(admin)

    app.use(admin.options.rootPath, adminRouter)
    admin.watch()

    app.listen(PORT, () => {
      console.log(
        `Listening on port ${PORT}, AdminJS server started on URL: http://localhost:${PORT}${admin.options.rootPath}`
      )
    })
  } catch (error) {
    console.error("Error starting AdminJS", error)
  }
}

startAdminJS()

//
order.entity.js
//
import mongoose from "mongoose"

const orderSchema = new mongoose.Schema({
  network_id: {
    type: Number,
    required: true,
  },
  offer_status: { type: Boolean, required: true },
})

export const Order = mongoose.model("Order", orderSchema)

//
offerStatus.js
//
import { Order } from "./order.entity.js"
import { Components } from "./componentLoader.js"

export const OfferStatus = {
  resource: Order,
  options: {
    properties: {
      _id: {
        type: "string",
        isVisible: {
          edit: false,
          show: false,
          list: false,
          filter: false,
        },
      },
      offer_status: {
        type: "boolean",
        components: {
          list: Components.CustomCheckbox,
        },
      },
    },
    actions: {
      toggleStatus: {
        actionType: "record",
        handler: async (request, context) => {
          const { record, resource } = context
          const newValue = !record.params.offer_status

          const updatedRecord = await resource.update(request.params.recordId, {
            offer_status: newValue,
          })

          if (!updatedRecord) {
            throw new Error("Record could not be updated")
          }

          return {
            record: updatedRecord.toJSON(),
            notice: {
              message: "Status successfully toggled",
              type: "success",
            },
          }
        },
      },
    },
  },
}

//
CustomCheckbox.jsx
//
import { useState } from "react"
import { CheckBox } from "@adminjs/design-system"

const CustomCheckbox = ({ record, property }) => {
  const [checked, setChecked] = useState(record.params[property.name])

  const handleChange = async () => {
    const newValue = !checked
    setChecked(newValue)

    try {
      await fetch(
        `/admin/api/resources/Order/records/${record.id}/toggleStatus`,
        {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({ offer_status: newValue }),
        }
      )
      console.log("1")
    } catch (error) {
      console.error("Error to change status", error)
      setChecked(!newValue)
    }
  }

  return <CheckBox checked={checked} onChange={handleChange} />
}

export default CustomCheckbox

I'd honestly suggest using ApiClient to send requests to the backend:

import { ApiClient } from 'adminjs'

const api = new ApiClient()

await api.recordAction({
  recordId: record.id,
  resourceId: 'Order',
  actionName: 'toggleStatus',
  method: 'post',
  data: {
    offer_status: newValue
  }
})

TypeError: Cannot read properties of undefined (reading 'params') doesn't really tell much except that it couldn't find a record with given id in the database. All record actions have a record set in context even before before hooks are run unless it cannot find that record.