Sorting columns that are aggregating a reference ( e.g. sum) - react-admin

In react-admin, for one record in a data grid, I want to get the total number of a relation to that record, and then be able to sort it.
For example, let's say I have a join table for authors and followers. For the author list I want a column that tells me the total number of followers for this author. Then I want to sort that column so that I can see which authors have the most/least followers.
To get the "number of followers"-column I created a custom Component that can return the follower-count for each author:
const ReferenceCount = ({ reference, target }) => {
const record = useRecordContext();
const { total, isLoading, error } = useGetManyReference(reference, {
target: target,
id: record.id
//sort: { field: "?", order: "DESC" }
});
if (isLoading) {
return <p>loading...</p>;
}
if (error) {
return <p>ERROR</p>;
}
return total;
};
and used it within a datagrid (source is authors):
<List>
<Datagrid>
<TextField source="name" />
<ReferenceCount
reference="authorFollower"
target="authorId"
label="Number of Followers"
/>
</Datagrid>
</List>
It works! But the column is not sortable as the values are not related to any field. So I cannot add the sort property to the custom component, or use the sortBy-tag. It seems that the custom sort button also depends on a field.
Is there a any way to achieve this?

Related

Nested data in RTKQuery hook getting join data

I am implementing the "favorites" logic in a project where there are some cards showing some info about songs, and a heart icon shows red if that song is in the user favorites or empty if it is not.
I am using Supabase as a backend (SQL) and I already have a "profiles" table, a "songs" table and a "favorites" table with two foreign keys: profile_id and song_id.
In the project I am using RTKQuery and I have already configured the endpoint to obtain the data for the favorites which is like:
endpoints: (builder) => ({
getFavorites: builder.query({
queryFn: async (id) => {
const {data, error} = await supabase
.from('favorites')
.select(`users_id,songs_id (*)`)
.eq('users_id',id)
return { data, error }
},
providesTags: ["Favorites"]
}),
Then I get the data with:
const { data, isSuccess } = useGetFavoritesQuery( id );
I am calling this hook from the component mad for each card, which already have the props. So I can check if the prop "song.id" is in some of the ids inside the "favorites" object.
But the problem I am facing is because the data that I really need is the nested object inside data, and each one of the nested objects have the id that I would use to check if it is === to song.id of the component I am calling from.
I have tried several things but I don't know if I am doing fine. This is one solution that I have tried. First I iterate over data to obtain the object "favorites":
let favorites;
if (isSuccess){
data.forEach(element => {
favorites = element.songs_id;
})
}
And then the boolean for the heart red or empty:
const favoriteIconColor = useMemo(() => {
if (Object.values(favorites).some(item=> item.id === song.id)){
return true
}
} , [favorites, song]);
But that is not working. I am thinking about creating just an array with all of the ids in the first iteration, but I am not sure if I am overcomplicating the issue.
I have also considered that maybe there is a way to obtain the data as I need with the query itself, but not sure about it. Any advice?

Creating a checkbox group with React Native

Good Morning! I am wanting to create a selection box where the user has several options of items to choose from and when clicking on a button, it triggers a function that shows all the values that the user chose in the form of an array, json or even arrays ( hard task).
In the React Native documentation, only simple examples of checkboxes using the component are provided and I wanted to go much further than the documentation provides me. What are the possible solutions to this problem? (from a simpler example to an advanced one) and what (s) ways can I explore this problem in order to solve it in the most practical and uncomplicated way?
Definitions and examples of official documentation:
https://reactnative.dev/docs/checkbox/ (CheckBox)
https://reactnative.dev/docs/button/ (Button)
With this problem, another one came up: build an application where the user selects shopping options (items) and a subtotal is displayed in the lower corner of the application as he selects or deselects the items he is going to buy, and there is also an option to reset the subtotal by returning it to the zero value.
From the problem mentioned at the beginning, what are the possible solutions to create this application previously mentioned in a practical and simple way?
Multi Checkbox example ( Updated with Hook )
export const Example = () => {
const [checkboxes, setCheckboxes] = useState([{
id: 1,
title: 'one',
checked: false,
}, {
id: 2,
title: 'two',
checked: false,
}]);
const onButtonPress = () => {
const selectedCheckBoxes = checkboxes.find((cb) => cb.checked === true);
// selectedCheckBoxes will have checboxes which are selected
}
const toggleCheckbox = (id, index) => {
const checkboxData = [...checkboxes];
checkboxData[index].checked = !checkboxData[index].checked;
setCheckboxes(checkboxData);
}
render(){
const checBoxesView = checkboxes.map((cb, index) => {
return (
<View style={{flexDirection:"row"}}>
<Checkbox
key={cb.id}
checked={cb.checked}
onPress={() => toggleCheckbox(cb.id, index)} />
<Text>{cb.title}</Text>
</View>
);
});
return (
<View>
{ checBoxesView }
<Button onPress={onButtonPress} title="Click" />
</View>
);
}
}

React Native: Checkbox List Structure

A user object has an array prop schools that references one or more school objects. I would like to use a <List> with <CheckBox> to mutate the schools array.
I load the user object into the view, and I load the listOfSchools (from the application state) to generate the checkbox list:
<List data={listOfSchools} keyExtractor={ item=> item._id } renderItem={({item})=>renderItem(item)} />
The renderItem function:
const renderItem = (school) => {
return <ListItem
title={school.name}
accessory={()=>renderAccessory(school)}
/>
};
The renderAccessory function:
const renderAccessory = (school) => {
return <CheckBox checked={() => checkSchool(school._id)} onChange={()=>changeSchool(school._id)} />
}
The checkSchool function returns boolean on if the school._id is referenced in the user.schools array. The changeSchool function adds or removes the school._id from the users.schools array.
The changeSchool function:
const changeSchool = (schoolId) => {
let checked = checkSchool(schoolId);
if (!checked) {
// add schoolId to user.schools
} else {
// remove schoolId from user.schools
}
}
This drastically does not work. It appears that no matter what I use to mutate the state, the checkboxes never update, nor does the user.schools array mutate.
What is the proper way to structure such a design goal?
Assuming that you use UI Kitten, I can see that you got the checked prop value wrong for the CheckBox component.
UI Kitten CheckBox reference
The checked prop needs to be a boolean not a Callable as you have it there
I would try to change the code like this:
const renderAccessory = (school) => {
const isChecked = checkSchool(school._id);
return <CheckBox checked={isChecked} onChange={()=>changeSchool(school._id)} />
}
Let me know if that helped.
While trying various solutions i can conclude few things here:
With the solution given by #Cornel Raiu the checked and unchecked flags are getting correctly calculated however, the display was not correct with the state of checked/unchecked
I replaced Checkbox with Toggle, just to be sure that it works with iOS too
PROBLEM that i faced still is that, even the State of item getting toggled is correctly populating it was getting reset
The outside container of Toggles is List and ListItem,
OBSERVATION is that the Press event on List was actually getting the Checkbox/Toggle into correct Display State...
SOLUTION:
After longer time of research and experiments I got my thing working with following approach -
I maintained separate collection of Checked Items
There is already a state of Collection of master items, as input to List
Every time the Checkbox/Toggle is clicked, the master list of Data is cloned and copied back to its state
This was triggering the slight re-render of component and thing is working as expected.
const [cashTransactions, setCashTransactions] = useState([]); // master data
const [selectedTransactions, setSelectedTransactions] = useState([]); // selected data
const renderItem = ({ item, index }) => (
<ListItem
title={'('+item.id + ') ' + item.firstName + ' ' + item.lastName}
description={Moment(item.createdOn).format('yyyy-MM-DD hh:mm:ss')}
accessoryLeft={selectedTransactions.includes(item.id) ? RadioOnIcon : RadioOffIcon}
accessoryRight={() => checkBoxSpace(item)}
/>
);
const checkBoxSpace = (item) => {
let itemChecked = selectedTransactions.includes(item.id);
return (
<View style={styles.actionContainer}>
<Button size='tiny' status='basic' accessoryLeft={rupeeSymbol}>{item.amount}</Button>
<Toggle checked={itemChecked} status='primary' size='small' onChange={checked => checkboxChecked(item, checked)}></Toggle>
</View>
)
}
const checkboxChecked = (item, checked) => {
console.log('Item -' + item.id + ' ' + checked);
if (checked) {
if (!selectedTransactions.includes(item.id)) {
selectedTransactions.push(item.id);
}
} else {
if (selectedTransactions.includes(item.id)) {
selectedTransactions.pop(item.id);
}
}
console.log('selectedTransactions ' + JSON.stringify(selectedTransactions));
// This is the thing i applied to get it done.
const cloned = [...cashTransactions];
setCashTransactions(cloned);
}
// View
<List
style={styles.container}
data={cashTransactions}
renderItem={renderItem}
/>

How to create a multi-select with Vuetify

I am creating a multiple-v-select for users, so they can select multiple options. A computed property filters this selection. I cannot get this to work with multiple options.
Removing the multiple setting from v-select works, but that means that users can only select one option, while they should be able to select more than one.
<template>
<v-select
v-model="selectofficeperks"
:items="officeperks"
multiple />
</template>
<script>
export default {
data () {
return {
officeperks: ['Tafeltennis','Tafelvoetbal', 'Pooltafel']
selectofficeperks:[],
jobs:[],
jobofficeperks:'',
}
},
computed: {
filtered() {
return this.jobs.filter(result => {
let officeperks = this.selectofficeperks;
if (officeperks != '') {
return (officeperks.includes(result.jobofficeperks))
}
}
}
}
}
</script>
When selecting multiple options it does not give a result from the computed filter. The jobs array gets filled by data from Firestore, the values are exactly the same.
If I remove the multiple setting, it does return a value, but I'm guessing that's because it only tries to find one value, which is not what I want. Users have to be able to select multiple values.

Add search to react-native firebase list

I have a react-native list populated from firebase. I want to add a search bar on top of the app. Once I start typing, I want that list to only show results related to the search term. Its similar to this: https://www.freecodecamp.org/news/how-to-build-a-react-native-flatlist-with-realtime-searching-ability-81ad100f6699/
Only problem is the way my data is typed after its retrieved from firebase, its impossible to use the tutorial above to retrieve that data.
My html code is:
<Toolbar title="Hello World"/>
<Button
title="Add New"
type = "outline"
onPress={this.addItem.bind(this)}
/>
<ListView
dataSource={this.state.itemDataSource}
renderRow={this.renderRow}
/>
My Firebase retrieval code is as follows:
getItems(itemsRef) {
itemsRef.on('value', (snap) => {
let items = [];
snap.forEach((child) => {
items.push({
title: child.val().DESCRIPTION,
text: child.val().BASE,
_key: child.key
});
});
this.setState({
itemDataSource: this.state.itemDataSource.cloneWithRows(items)
});
});
}
I just don't know how to add a search bar that searches the retrieved list and displays only relevant stuff on the list as someone types a word in.
Pease help. I'm guessing the type of the data is what is causing the problem.
Results should look like this App. I built this on AppSheet:
https://www.appsheet.com/start/ab7f8d5d-1adf-4107-bdd8-f7022a1a81f8