get last name and phone number from appScript and google sheet - javascript

I have these two functions that take data from a google sheet and then send a whatsapp message. I can't modify the first function (getSheetData_) to take only the data from the last row. I only have two columns (name and phone). I have a trigger that launches the script on every change.
source: https://www.labnol.org/whatsapp-api-google-sheets-220520
const getSheetData_ = () => {
const [header, ...rows] = SpreadsheetApp.getActiveSheet().getDataRange().getDisplayValues();
const data = [];
rows.forEach((row) => {
const recipient = { };
header.forEach((title, column) => {
recipient[title] = row[column];
});
data.push(recipient);
});
return data;
};
const main = () => {
const data = getSheetData_();
data.forEach((recipient) => {
const status = sendMessage_({
recipient_number: recipient["Phone Number"].replace(/[^\d]/g, ""),
customer_name: recipient["Customer Name"]
});
});
};

Solution:
const getSheetData_ = () => {
var sheet = SpreadsheetApp.getActiveSheet();
var lastRow = sheet.getRange(sheet.getLastRow(),1,1,2);
const header = ['Customer Name', 'Phone Number'];
const rows = lastRow.getDisplayValues();
const data = [];
rows.forEach((row) => {
const recipient = { };
header.forEach((title, column) => {
recipient[title] = row[column];
});
data.push(recipient);
});
return data;
};

Related

antd table search filtering

i'm new on react hooks(typescript), here i'm having trouble figuring out how to filter by two, at the moment search filtering is working with one filter and it is 'receiver?.name?'
i want to add one more filter and it is 'sending?name?'
here is working code:
const handleSearchh = (searchText: string) => {
const filteredEvents = orders?.filter(({ receiver }) => {
const name = receiver?.name?.toLowerCase() ?? "";
return name.includes(searchText);
});
setData(filteredEvents);
};
<Searchh onSearch={handleSearchh} />
<Table
dataSource={Data}
columns={columns}
/>
i want to add this to it
const filteredEvents = orders?.filter(({ sending }) => {
const name = sending?.name?.toLowerCase() ?? "";
return name.includes(searchText);
});
it is antd table i want it to filter the search by two parameters , receivers and senders name
You can try this solution.
const handleSearchh = (searchText: string) => {
filterData(searchText)
};
const filterData = (searchText) => {
const filteredEvents = orders?.filter(({ receiver, sending }) => {
const receiverName = receiver?.name?.toLowerCase() ?? "";
const sendingName = sending?.name?.toLowerCase() ?? "";
return receiverName.includes(searchText) && sendingName.includes(searchText);
});
setData(filteredEvents);
}
This isn't antd specific, but can't you just chain the filter functions?
// ...
const filterFuncOne = ({ receiver }) => {
const name = receiver?.name?.toLowerCase() ?? "";
return name.includes(searchText);
};
const filterFuncTwo = ({ sending }) => {
const name = sending?.name?.toLowerCase() ?? "";
return name.includes(searchText);
}
const filteredEvents = orders?
.filter.(filterFuncOne)
.filter(filterFuncTwo); // <-- chained
//...

Why I'm not getting back the new value of useState - React.JS?

In the line setVotedPosts([...previousVotedPosts, postId]);
I'm trying to get the previous value of votedPosts, but I'm getting back the newest value.
full code : https://github.com/silvertechguy/reddit-clone/blob/main/src/components/vote-buttons.js
App live : https://reddit-clone-official.vercel.app/
const VoteButtons = ({ post }) => {
const [isVoting, setVoting] = useState(false);
const [votedPosts, setVotedPosts] = useState([]);
useEffect(() => {
const votesFromLocalStorage =
JSON.parse(localStorage.getItem("votes")) || [];
setVotedPosts(votesFromLocalStorage);
}, []);
const handleDisablingOfVoting = (postId) => {
const previousVotedPosts = votedPosts;
setVotedPosts([...previousVotedPosts, postId]);
localStorage.setItem(
"votes",
JSON.stringify([...previousVotedPosts, postId])
);
};
const handleClick = async (type) => {
setVoting(true);
// Do calculation to save the vote.
let upVotesCount = post.upVotesCount;
let downVotesCount = post.downVotesCount;
const date = new Date();
if (type === "upvote") {
upVotesCount = upVotesCount + 1;
} else {
downVotesCount = downVotesCount + 1;
}
await db.collection("posts").doc(post.id).set({
title: post.title,
upVotesCount,
downVotesCount,
createdAt: post.createdAt,
updatedAt: date.toUTCString(),
});
// Disable the voting button once the voting is successful.
handleDisablingOfVoting(post.id);
setVoting(false);
};
const checkIfPostIsAlreadyVoted = () => votedPosts.includes(post.id);
Problem
const previousVotedPosts = votedPosts;
In JavaScript, arrays are reference types, so you can't just create a new copy of an array using =.
Try this solution
Clone array using spread syntax(...).
const handleDisablingOfVoting = (postId) => {
const previousVotedPosts = [...votedPosts];
setVotedPosts([...previousVotedPosts, postId]);
localStorage.setItem(
"votes",
JSON.stringify([...previousVotedPosts, postId])
);
};

trouble with displaying data from firestore

I'm having two issues this moment
I am having trouble displaying the data from my firestore document after the user signs in and they do have a document with data
I have a button that lets the user upload a document and I will parse it to extract all important data. That data is then put into the document with Ids, which is basically the userId + what number doc it is belonging to the user. When they upload the data it seems that all data extracted uses the same ID and only the last one extracted gets displayed.
this is my code:
const loansRef = firebase.firestore().collection('goals');
const [ courseGoals, setCourseGoals ] = useState([]);
const [goalCounter, setGoalCounter] = useState(0)
//let the user upload doc, what gets called when they press button
const pickDocument = async () => {
try {
let input = await DocumentPicker.getDocumentAsync({
type: "text/plain",
});
setUserOut(await FileSystem.readAsStringAsync(input.uri));
} catch (error) {
console.log(error);
}
createLoans();
};
//extracts important info and returns array with all info for one loan
const fileParser = () => {
const parsedLoans = [];
var newUserOut = userOut;
if (newUserOut.length == 0) {
return;
}
//remove the grants
var grantPos = newUserOut.search("Grant Type:");
var pos = newUserOut.search("Loan Type:");
//hopefully just the loans now
newUserOut = newUserOut.slice(pos, grantPos);
while (newUserOut.length > 0) {
var lastPos = newUserOut.lastIndexOf("Loan Type:");
parsedLoans.push(newUserOut.slice(lastPos, newUserOut.length));
newUserOut = newUserOut.slice(0, lastPos);
}
//console.log('parsed loans: ' + parsedLoans)
return parsedLoans;
};
//where we actually create loans and get the important data for each loan
const createLoans = () => {
const newLoans = fileParser();
const title= 'Loan Amount:$'
const interest = 'Loan Interest Rate:'
for(let i =0; i < newLoans.length; i++){
let loan = newLoans[i]
let goalTitle=loan.substring(loan.indexOf(title)+title.length,loan.indexOf('Loan Disbursed Amount:'))
//console.log("goalTitle: " + goalTitle)
let interestRate = loan.substring(loan.indexOf(interest)+interest.length,loan.indexOf('Loan Repayment Plan Type'))
//console.log("Interest rate: "+ interestRate)
let years = 0
let paidOff = 0
addGoalHandler(goalTitle,interestRate,years,paidOff)
}
return
};
useEffect(() => {
getPW()
let isMounted = true;
if (isMounted) {
loansRef.doc(userId).onSnapshot(
(docSnapshot) => {
if(!docSnapshot.exists){console.log('doc doesnt exist, start from scratch')}
else{
console.log('loaded successfully '+docSnapshot.data())
setGoalCounter(docSnapshot.data().loans.length)
console.log(goalCounter)
}
},
(error) => {
console.log(error);
}
);
}
return () => {
isMounted = false;
};
}, []);
const addGoalHandler = (goalTitle, interestRate, years, paidOff) => {
//setCourseGoals([...courseGoals, enteredGoal])
setGoalCounter(goalCounter+1)
setCourseGoals((prevGoals) => [
...courseGoals,
{
id:userId.toString() + goalCounter.toString(),
value: goalTitle,
interest: interestRate,
years: years,
paidOff: paidOff
}
]);
addToFB(goalTitle, interestRate,years,paidOff)
//var oldIDS = docIDS
//oldIDS.push(userId.toString() + goalCounter.toString())
//setDocIDS(oldIDS)
setIsAddMode(false);
};
const cancelGoalAdditionHandler = () => {
setIsAddMode(false);
};
const addToFB = async (goalTitle, interestRate, years, paidOff) => {
//adding data to firebase, takes into account if doc exists already
const loadDoc = await loansRef.doc(userId).get()
.then((docSnapshot)=> {
if(docSnapshot.exists){
loansRef.doc(userId).onSnapshot((docu)=>{
const updateLoansArr = loansRef.doc(userId).update({
loans: firebase.firestore.FieldValue.arrayUnion({
id: userId+goalCounter.toString(),
value: goalTitle,
interest: interestRate,
years: years,
paidOff: paidOff
})
})
//setGoalCounter(goalCounter+1)
})
}
else{
const addDoc = loansRef.doc(userId).set({
loans: firebase.firestore.FieldValue.arrayUnion({
id: userId+goalCounter.toString(),
value: goalTitle,
interest: interestRate,
years: years,
paidOff: paidOff
})
})
//setGoalCounter(goalCounter+1)
}})
//setGoalCounter(goalCounter+1)
}
const removeGoalHandler = async (goalId) => {
/*
setCourseGoals((currentGoals) => {
loansRef.doc(goalId).delete().then(console.log('removed correctly'))
return currentGoals.filter((goal) => goal.id !== goalId);
});
setGoalCounter(goalCounter-1)
*/
//need to use the value and not the goalId
const removeGoal = await loansRef.doc(goalId).update({
goals: firebase.firestore.FieldValue.arrayRemove(goalId)
})
setCourseGoals((currentGoals)=> {
return currentGoals.filer((goal)=> goal.id !== goalId)
})
};

getting index of list when click change location 🙏

how can I get the Index of city cliked and pass it to function Daily.js
exemple when user change location to differnt city I want to change the Url
in the list location 1 => url 1
location2 => url 2
...
function Daily({ locationProps = 1, root }) {
const context = useContext(ThemeContext);
const localization = useCallback(() => {
if (root && cookies.get("location") !== undefined) {
return cookies.get("location");
}
return locationProps;
}, [locationProps, root]);
const handleClick = event => {
window.focus();
notification.close(event.target.tag);
};
const openNav = () => {
document.getElementById("sidenav").style.width = "100%";
};
const closeNav = e => {
e.preventDefault();
document.getElementById("sidenav").style.width = "0";
};
const _data = useRef(new Data(INDEX)); // -----> How to pass INDEX here !
const getTimes = () => _data.current.getTimes();
const times = useState(()=>getTimes());
data.json
location [
liste of location
],
URL [
list of url
]

Receiving Promise {<pending>} back from .then()

I have an API call in api.js:
export const getGraphData = (domain, userId, testId) => {
return axios({
url: `${domain}/api/${c.embedConfig.apiVersion}/member/${userId}/utests/${testId}`,
method: 'get',
});
};
I have a React helper that takes that data and transforms it.
import { getGraphData } from './api';
const dataObj = (domain, userId, testId) => {
const steps = getGraphData(domain, userId, testId)
.then((result) => {
return result.attributes;
});
console.log(steps);
// const steps = test.get('steps');
const expr = /select/;
// build array of steps that we have results in
const resultsSteps = [];
steps.forEach((step) => {
// check for types that contain 'select', and add them to array
if (expr.test(step.get('type'))) {
resultsSteps.push(step);
}
});
const newResultsSteps = [];
resultsSteps.forEach((item, i) => {
const newMapStep = new Map();
const itemDescription = item.get('description');
const itemId = item.get('id');
const itemOptions = item.get('options');
const itemAnswers = item.get('userAnswers');
const newOptArray = [];
itemOptions.forEach((element) => {
const optionsMap = new Map();
let elemName = element.get('value');
if (!element.get('value')) { elemName = element.get('caption'); }
const elemPosition = element.get('position');
const elemCount = element.get('count');
optionsMap.name = elemName;
optionsMap.position = elemPosition;
optionsMap.value = elemCount;
newOptArray.push(optionsMap);
});
newMapStep.chartType = 'horizontalBar';
newMapStep.description = itemDescription;
newMapStep.featured = 'false';
newMapStep.detailUrl = '';
newMapStep.featuredStepIndex = i + 1;
newMapStep.id = itemId;
newMapStep.isValid = 'false';
newMapStep.type = 'results';
const listForNewOptArray = List(newOptArray);
newMapStep.data = listForNewOptArray;
newMapStep.userAnswers = itemAnswers;
newResultsSteps.push(newMapStep);
});
return newResultsSteps;
};
export default dataObj;
The issue is steps, when logged outside the .then() returns a Promise {<pending>}. If I log results.attributes inside the .then(), I see the data fully returned.
You need to wait until your async call is resolved. You can do this by chaining on another then:
getGraphData(domain, userId, testId)
.then((result) => {
return result.attributes;
})
.then(steps => {
// put the rest of your method here
});
You can also look at async/await if your platform supports it which would allow code closer to your original
const steps = await getGraphData(domain, userId, testId)
.then((result) => {
return result.attributes;
});
// can use steps here
You have 2 options to transform your fetched data :
1st option : create a async function that returns a promise with the modified data :
const dataObj = (domain, userId, testId) => {
return getGraphData(domain, userId, testId).then((result) => {
const steps = result.attributes;
const expr = /select/;
// build array of steps that we have results in
const resultsSteps = [];
steps.forEach((step) => {
// check for types that contain 'select', and add them to array
if (expr.test(step.get('type'))) {
resultsSteps.push(step);
}
});
const newResultsSteps = [];
resultsSteps.forEach((item, i) => {
const newMapStep = new Map();
const itemDescription = item.get('description');
const itemId = item.get('id');
const itemOptions = item.get('options');
const itemAnswers = item.get('userAnswers');
const newOptArray = [];
itemOptions.forEach((element) => {
const optionsMap = new Map();
let elemName = element.get('value');
if (!element.get('value')) {
elemName = element.get('caption');
}
const elemPosition = element.get('position');
const elemCount = element.get('count');
optionsMap.name = elemName;
optionsMap.position = elemPosition;
optionsMap.value = elemCount;
newOptArray.push(optionsMap);
});
newMapStep.chartType = 'horizontalBar';
newMapStep.description = itemDescription;
newMapStep.featured = 'false';
newMapStep.detailUrl = '';
newMapStep.featuredStepIndex = i + 1;
newMapStep.id = itemId;
newMapStep.isValid = 'false';
newMapStep.type = 'results';
const listForNewOptArray = List(newOptArray);
newMapStep.data = listForNewOptArray;
newMapStep.userAnswers = itemAnswers;
newResultsSteps.push(newMapStep);
});
return newResultsSteps;
});
};
With es7 async/await syntax it should be :
const dataObj = async (domain, userId, testId) => {
const result = await getGraphData(domain, userId, testId);
const steps = result.attributes;
... modify the data
}
Then keep in mind that this function returns a promise, you'll need to wait for it to get the result, example in a react component :
componentDidMount(){
dataObj('mydomain', 'myuserId', 'mytestId').then((res) => {
this.setState({ data: res });
}
}
The component will update when the promise is resolve, you can then use the data (you'll need to handle the undefined data state in render method)
2nd option : Create a sync function to modify the data :
const dataObj = (steps) => {
const expr = /select/;
const resultsSteps = [];
steps.forEach((step) => {
...
}
return newResultsSteps;
};
To have the same result as option 1 in our component we'll use it like this :
componentDidMount(){
getGraphData('mydomain', 'myuserId', 'mytestId').then((res) => {
const modifiedData = dataObj(res);
this.setState({ data: modifiedData });
}
}
That's how promises work. The data is not ready when you are trying to use it so you should move all your processing into the .then. The reason your variable is a Promise {<pending>} is because you can chain other things onto it.
Something like:
steps.then((steps) => {
...
});

Categories