Adminjs adding custom reactjs components to my resource on adminjs - node.js

I am using adminjs for the first time and I have written my own custom component, but adding it to the resource keeps producing an error that says "You have to implement action component for your Action" Here is my resource:
const {Patient} = require('./../models/Patient')
const Components = require('./../components/components')
const { Patient } = require('./../models/Patient')
const Components = require('./../components/components')
const PatientResource = {
resource: Patient,
options: {
actions: {
consultation: {
actionType: 'record',
component: Components.MyCustomAction,
handler: (request, response, context) => {
const { record, currentAdmin } = context
return {
record: record.toJSON(currentAdmin),
msg: 'Hello world',
}
},
},
isAccessible: ({ currentAdmin }) => {
action: if (currentAdmin.role_id === 5) {
return false
} else {
return true
}
},
},
navigation: 'Manage Patients',
listProperties: [
'id',
'last_name',
'first_name',
'sex',
'marital_status',
'blood_group',
'genotype',
'existing_health_condition',
],
},
}
module.exports = { PatientResource }
And this is my custom component:
import React from 'react'
import { Box, H3 } from '#adminjs/design-system'
import { ActionProps } from 'adminjs'
const MyCustomActionComponent = (props: ActionProps) => {
const { record } = props
return (
<Box flex>
<Box
variant='white'
width={1 / 2}
boxShadow='card'
mr='xxl'
flexShrink={0}
>
<H3>Example of a simple page</H3>
<p>Where you can put almost everything</p>
<p>like this: </p>
<p>
<img
src='https://i.redd.it/rd39yuiy9ns21.jpg'
alt='stupid cat'
width={300}
/>
</p>
</Box>
<p> Or (more likely), operate on a returned record:</p>
<Box overflowX='auto'> {JSON.stringify(record)}</Box>
</Box>
)
}
module.exports = { MyCustomActionComponent }
I tried to add the component to my custom defined action "Consultation", Hence I expected to see a custom page which i have designed using react as in "mycustomAction" above

Related

Use XState in a nuxt composable

I want to use an xstate state machine in Nuxt 3 which is used over multiple components.
I created a small example of how I want this to look like.
I also use the nuxt-xstate module.
State Machine:
export default createMachine(
{
id: 'toggle',
initial: 'switched_off',
states: {
switched_on: {
on: {
SWITCH: {
target: 'switched_off'
}
}
},
switched_off: {
on: {
SWITCH: {
target: 'switched_on'
},
}
},
},
}
)
Composable:
const toggle = useMachine(toggleMachine)
export function useToggleMachine(){
return { toggle }
}
app.vue:
<template>
<div>
State: {{toggle.state.value.value}}
</div>
<br />
<button
#click="toggle.send('SWITCH')"
>
Switch
</button>
</template>
<script>
import { useToggleMachine } from '~/composables/toggle_machine'
export default {
setup(){
const { toggle } = useToggleMachine()
return { toggle }
}
}
</script>
The problem is, that I can have a look at the state of the machine {{state.value.value}} gives me the expected 'turned_off'. But I cannot call the events to transition between states. When clicking on the button, nothing happens.
Here is the console.log for the passed 'toggle' object:
Does anyone know a way how to fix this, or how to use xstate state machines over multiple components.
I am aware that props work, but I don't really want to have an hierarchical approach like that.
In Nuxt3, it's very simple:
in composables/states.ts
import { createMachine, assign } from 'xstate';
import { useMachine } from '#xstate/vue';
const toggleMachine = createMachine({
predictableActionArguments: true,
id: 'toggle',
initial: 'inactive',
context: {
count: 0,
},
states: {
inactive: {
on: { TOGGLE: 'active' },
},
active: {
// eslint-disable-next-line #typescript-eslint/no-explicit-any
entry: assign({ count: (ctx: any) => ctx.count + 1 }),
on: { TOGGLE: 'inactive' },
},
},
});
export const useToggleMachine = () => useMachine(toggleMachine);
In pages/counter.vue
<script setup>
const { state, send } = useToggleMachine()
</script>
<template>
<div>
<button #click="send('TOGGLE')">
Click me ({{ state.matches("active") ? "✅" : "❌" }})
</button>
<code>
Toggled
<strong>{{ state.context.count }}</strong> times
</code>
</div>
</template>

Using CKEditor in a component with Laravel/Inertia

I'd like to use Ckeditor in my Laravel/Inertia project and i can't get it to work. I found a tutorial from LaraTips, but that was written for VueJS-2. I am working with the lastest version Inertia which uses VueJS-3.
I want to use Ckeditor in a separate component, and it (sort of) works, but i can't get the old data to show in the editor. I get an error "Uncaught (in promise) TypeError: Cannot read property 'setData' of undefined at Proxy.modelValue (app.js:29)"
What am i doing wrong?
This is my component:
<template>
<ckeditor :editor="editor" v-model="text" :config="editorConfig"></ckeditor>
</template>
<script>
import ClassicEditor from '#ckeditor/ckeditor5-build-classic';
export default {
data() {
return {
text: "",
editor: ClassicEditor,
editorConfig: {
// The configuration of the editor.
},
}
},
props: {
modelValue: {}
},
setup() {
},
watch: {
modelValue: {
immediate: true,
handler(modelValue) {
this.text = modelValue;
}
},
text(text) {
this.$emit('update:modelValue', text);
}
},
}
</script>
Any suggestions??
I am doing the same tutorial (i am using vueJS-3).
this may work for you:
in app.js include CKEditor:
createInertiaApp({
title: (title) => `${title} - ${appName}`,
resolve: (name) => require(`./Pages/${name}.vue`),
setup({ el, app, props, plugin }) {
return createApp({ render: () => h(app, props) })
.use(plugin)
.use( CKEditor)
.mixin({ methods: { route } })
.mount(el);
},
});
In Components/CkEditor.vue check what are you emitting
look for this this.$emit("input", text);
<template>
<ckeditor :editor="editor" v-model="text" :config="editorConfig"></ckeditor>
</template>
<script>
import ClasicEditor from "#ckeditor/ckeditor5-build-classic";
export default {
props: {
value: {},
},
data() {
return {
text: "",
editor: ClasicEditor,
editorConfig: {
// The configuration of the editor.
},
};
},
watch: {
value:{
inmediate: true,
handler(value){
this.text = value;
}
},
text(text) {
this.$emit("input", text);
},
},
};
</script>
let me know if that worked for you
I looked at the answer here, and below is what worked for me:
Hope this helps! :)
I am using laravel/inertia with vue 3.
app.js
import './bootstrap';
import '../css/app.css';
import { createApp, h } from 'vue';
import { createInertiaApp } from '#inertiajs/inertia-vue3';
import { InertiaProgress } from '#inertiajs/progress';
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
import { ZiggyVue } from '../../vendor/tightenco/ziggy/dist/vue.m';
import { createPinia } from 'pinia';
import { _t } from './Utilities/translations';
import CKEditor from '#ckeditor/ckeditor5-vue';
const appName =
window.document.getElementsByTagName('title')[0]?.innerText || 'Laravel';
createInertiaApp({
title: (title) => `${title} - ${appName}`,
resolve: (name) =>
resolvePageComponent(
`./Pages/${name}.vue`,
import.meta.glob('./Pages/**/*.vue')
),
setup({ el, app, props, plugin }) {
const vue_app = createApp({ render: () => h(app, props) });
vue_app.use(plugin);
vue_app.use(ZiggyVue, Ziggy);
vue_app.use(createPinia());
// Register all base components globally
const components = import.meta.globEager('./Components/Base/*.vue');
for (const path in components) {
let componentName;
if (path.split) {
const split_componentName = path.split('/').pop();
if (split_componentName) {
componentName = split_componentName.replace(/\.\w+$/, '');
vue_app.component(componentName, components[path].default);
}
}
}
vue_app.config.globalProperties.$_t = _t;
vue_app.use(CKEditor);
vue_app.mount(el);
return vue_app;
}
});
InertiaProgress.init({ color: '#4B5563' });
CKEditor Component:
<template>
<div id="app">
<ckeditor
v-model="editor_data"
:editor="editor"
:config="editor_config"
></ckeditor>
</div>
</template>
<script setup lang="ts">
import { reactive, ref } from '#vue/reactivity';
import * as editor from '#ckeditor/ckeditor5-build-classic';
const editor_data = ref('');
const editor_config = {};
</script>

GSAP timeline needed on every page in Gatsby

My Gatsby site use the same GSAP timeline on every page, so I want to stay DRY and my idea is to include my timeline in my Layout component in that order.
But I don't know how to pass refs that I need between children and layout using forwardRef.
In short, I don't know how to handle the sectionsRef part between pages and layout.
sectionsRef is dependant of the page content (children) but is needed in the timeline living in layout.
How can I share sectionsRef between these two (I tried many things but always leading to errors)?
Here's a codesandbox without the refs in the Layout:
https://codesandbox.io/s/jolly-almeida-njt2e?file=/src/pages/index.js
And the sandbox with the refs in the layout:
https://codesandbox.io/s/pensive-varahamihira-tc45m?file=/src/pages/index.js
Here's a simplified version of my files :
Layout.js
export default function Layout({ children }) {
const containerRef = useRef(null);
const sectionsRef = useRef([]);
sectionsRef.current = [];
useEffect(() => {
gsap.registerPlugin(ScrollTrigger);
const scrollTimeline = gsap.timeline();
scrollTimeline.to(sectionsRef.current, {
x: () =>
`${-(
containerRef.current.scrollWidth -
document.documentElement.clientWidth
)}px`,
ease: 'none',
scrollTrigger: {
trigger: containerRef.current,
invalidateOnRefresh: true,
scrub: 0.5,
pin: true,
start: () => `top top`,
end: () =>
`+=${
containerRef.current.scrollWidth -
document.documentElement.clientWidth
}`,
},
});
}, [containerRef, sectionsRef]);
return (
<div className="slides-container" ref={containerRef}>
{children}
</div>
);
}
index.js (page)
import { graphql } from 'gatsby';
import React, { forwardRef } from 'react';
import SectionImage from '../components/sections/SectionImage';
import SectionIntro from '../components/sections/SectionIntro';
import SectionColumns from '../components/sections/SectionColumns';
const HomePage = ({ data: { home } }, sectionsRef) => {
const { sections } = home;
const addToRefs = (el) => {
if (el && !sectionsRef.current.includes(el)) {
sectionsRef.current.push(el);
}
};
return (
<>
{sections.map((section) => {
if (section.__typename === 'SanitySectionIntro') {
return (
<SectionIntro key={section.id} section={section} ref={addToRefs} />
);
}
if (section.__typename === 'SanitySectionImage') {
return (
<SectionImage key={section.id} section={section} ref={addToRefs} />
);
}
if (section.__typename === 'SanitySectionColumns') {
return (
<SectionColumns
key={section.id}
section={section}
ref={addToRefs}
/>
);
}
return '';
})}
</>
);
};
export default forwardRef(HomePage);
export const query = graphql`
query HomeQuery {
// ...
}
`;
Any clue greatly appreciated :)

How to use react native navigation wix with context to share data from provider to all components?

I want to use react-native-navigation with context to send data from provider to all my components. I have done it as follow:
index.js
import {Navigation} from 'react-native-navigation';
import {App} from './App'
App();
Navigation.events().registerAppLaunchedListener(() => {
Navigation.setRoot({
root: {
stack:
{
id: 'FoodApp',
children: [
{
component: {
id: 'myLoginId',
name: 'myLogin',
options: {
topBar: {
visible: false,
height: 0,
},
}
}
},
],
},
},
});
});
App.js
export const App = () => {
Navigation.registerComponent(`myLogin`, () => (props) => (
<GlobalProvider>
<Login {...props} />
</GlobalProvider>
), () => Login);
Navigation.registerComponent(`myMain`, () => (props) => (
<GlobalProvider>
<Main {...props} />
</GlobalProvider>
), () => Main);
};
GlobalProvider and GlobalContext
const GlobalContext = React.createContext();
export const GlobalProvider = ({children}) => {
return <GlobalContext.Provider value={20}>
{children}
</GlobalContext.Provider>;
};
export default GlobalContext;
Main.js
const Main = () => {
const value= useContext(GlobalContext);
const options = (passProps) => {
return {
topBar: {
height: 0,
},
};
};
return (
<View style={styles.mainContainer}>
<Text>Main {value}</Text>
</View>
);
};
It does not give any errors but it does not show value. Please help me how to fix it. I have searched a lot but I cannot find any useful thing.
I have used:
"react": "16.11.0",
"react-native": "0.62.0",
"react-native-navigation": "^6.3.3",
it's weird that the value is not showing at all as your code snippet should display value. I've created an example below to demonstrate integrating React Context with react-native-navigation but unfortunately as react-native-navigation is not a single root application (each registered screen is a "root") the regular Context pattern would not work as expected.
// CounterContext.js
import React from 'react
const initialCounterState = {
count: 0
}
const counterContextWrapper = (component) => ({
...initialCounterState,
increment: () => {
initialCounterState.count += 1
component.setState({ context: contextWrapper(component) })
},
decrement: () => {
initialCounterState.count -= 1
component.setState({ context: contextWrapper(component) })
},
})
export const CounterContext = React.createContext({})
export class CounterContextProvider extends React.Component {
state = {
context: counterContextWrapper(this)
}
render() {
return (
<CounterContext.Provider value={this.state.context}>
{this.props.children}
</CounterContext.Provider>
)
}
}
// index.js
import { Navigation } from 'react-native-navigation
import { CounterContextProvider } from './CounterContext
import { Main } from './Main
Navigation.registerComponent(
'Main',
() => props => (
<CounterContextProvider>
<Main {...props} />
</CounterContextProvider>
),
() => CounterReactContextScreen
)
// Main.js
import React from 'react'
import { Button, Text, View } from 'react-native'
import { CounterContext } from './CounterContext'
export const Main = () => {
const { count, increment, decrement } = React.useContext(CounterContext)
return (
<View>
<Text>{`Clicked ${count} times!`}</Text>
<Button title="Increment" onPress={increment} />
<Button title="Decrement" onPress={decrement} />
</View>
)
}
This should all work, however the caveat is if you register 2 screens with the same Context Provider (for example, Main as your root and Pushed as a screen that gets pushed from Main) if you update the value on Pushed screen, it would not re-render Main screen to show the updated value.
I'd recommend to use MobX if you want Context like API. You could checkout my boilerplate project https://github.com/jinshin1013/rnn-boilerplate.

how can I pass data like the name of my user and put it in the post they created

so I am making an application for events and for some reason when a user creates an event the even info shows but the user info like their name and photo doesn't show up please help I've been having this problem for almost a week now.
THIS IS THE componentDidMount function
async componentDidMount() {
const { data } = await getCategories();
const categories = [{ _id: "", name: "All Categories" }, ...data];
const { data: events } = await getEvents();
this.setState({ events, categories });
console.log(events);
}
THIS IS THE STATE
class Events extends Component {
state = {
events: [],
user: getUser(),
users: getUsers(),
showDetails: false,
shownEventID: 0,
showUserProfile: false,
shownUserID: 0,
searchQuery: ""
};
THIS IS THE EVENTS FILE WHERE THE USER'S NAME AND PHOTO SHOULD BE DISPLAYED
<Link>
<img
className="profilePic mr-2"
src={"/images/" + event.hostPicture}
alt=""
onClick={() => this.handleShowUserProfile(event.userId)}
/>
</Link>
<Link style={{ textDecoration: "none", color: "black" }}>
<h4
onClick={() => this.handleShowUserProfile(event.userId)}
className="host-name"
>
{getUser(event.userId).name}
</h4>
</Link>
This is the userService file where the getUser function is
import http from "./httpService";
const apiEndPoint = "http://localhost:3100/api/users";
export function register(user) {
return http.post(apiEndPoint, {
email: user.email,
password: user.password,
name: user.name
});
}
export function getUsers() {
return http.get(apiEndPoint);
}
export async function getUser(userId) {
const result = await http.get(apiEndPoint + "/" + userId);
return result.data;
}
This is the eventService file where the event is
import http from "./httpService";
const apiEndPoint = "http://localhost:3100/api/events";
export function getEvents() {
return http.get(apiEndPoint);
}
export function getEvent(eventId) {
return http.get(apiEndPoint + "/" + eventId);
}
export function saveEvent(event) {
if(event._id){
const body = {...event}
delete body._id
return http.put(apiEndPoint + '/' + event._id, body)
}
return http.post(apiEndPoint, event);
}
export function deleteEvent(eventId) {
return http.delete(apiEndPoint + "/" + eventId);
}
First, you have some mistakes to use the class in <div> elements.
please use className instead class.
And then second I am not sure what it is.
class Events extends Component {
state = {
... ...
user: getUser(),
... ...
};
As you seen getUser() function requires one parameter userId.
But you did not send this.
So you met internal server error to do it.
Since I did not investigate all projects, I could not provide perfectly solution.
However, it is main reason, I think.
Please check it.

Resources