From 353feec90132911805cd6d13ff876452dcb40627 Mon Sep 17 00:00:00 2001 From: vikyw89 Date: Wed, 15 Mar 2023 18:40:42 +0800 Subject: [PATCH 01/81] checkpoint: progress on listContactsComponent --- client/src/common/contactCard/index.js | 17 +++ client/src/components/contactList/index.js | 66 +++++++++++ client/src/pages/contactList/index.js | 63 +++++++++++ client/src/pages/test.js | 121 +++++++++++++++++++++ client/src/styles/globals.css | 7 +- 5 files changed, 273 insertions(+), 1 deletion(-) create mode 100644 client/src/common/contactCard/index.js create mode 100644 client/src/components/contactList/index.js create mode 100644 client/src/pages/contactList/index.js create mode 100644 client/src/pages/test.js diff --git a/client/src/common/contactCard/index.js b/client/src/common/contactCard/index.js new file mode 100644 index 0000000..3d8dd1a --- /dev/null +++ b/client/src/common/contactCard/index.js @@ -0,0 +1,17 @@ +import { Avatar, Paper, Typography } from "@mui/material" +import { useState } from "react" + + +export const ContactCard = ({ contact }) => { + return ( + + + {contact.first_name} + + ) +} \ No newline at end of file diff --git a/client/src/components/contactList/index.js b/client/src/components/contactList/index.js new file mode 100644 index 0000000..c978cf0 --- /dev/null +++ b/client/src/components/contactList/index.js @@ -0,0 +1,66 @@ +import PropTypes from 'prop-types' + +// common +import Page from '@/common/layout/page' + +// lib +import Typography from '@mui/material/Typography' +import Button from '@mui/material/Button' +import Link from 'next/link' +import CheckIcon from '@mui/icons-material/Check' +import Paper from '@mui/material/Paper' +import SimpleSnackbar from '@/common/snackbars/simpleSnackbar' +import TransparentTextfield from '@/common/ui/transparentfield' +import { useTheme } from '@emotion/react' +import { ContactCard } from '@/common/contactCard' +import { Box } from '@mui/material' + +function ContactListComponent({ state, eventsHandler }) { + const theme = useTheme() + // divide state.contacts into each container + const sortedContacts = [...state.contacts].sort((a, b) => a.first_name < b.first_name ? -1 : 1) + const groupedSortedContacts = [...sortedContacts].reduce((acc, curr) => { + const capitalizedFirstNameFirstLetterChar = curr.first_name.match(new RegExp( + String.raw`(?^[a-z])|`, 'i'))[0] + if (!capitalizedFirstNameFirstLetterChar) { + acc.misc.push(curr) + } else { + acc[capitalizedFirstNameFirstLetterChar] + } + console.log(firstNameFirstLetterChar) + + }) + + return ( + + + + Search Bar + + + {state.contacts.map((el, index) => { + return ( + + ) + })} + + + + ) +} +ContactListComponent.propTypes = { + state: PropTypes.object, + eventsHandler: PropTypes.func +} + +export default ContactListComponent diff --git a/client/src/pages/contactList/index.js b/client/src/pages/contactList/index.js new file mode 100644 index 0000000..3e46907 --- /dev/null +++ b/client/src/pages/contactList/index.js @@ -0,0 +1,63 @@ +import ContactListComponent from '@/components/contactList' +import { useEffect, useState } from 'react' + + +const defaultState = { + contacts:[ + { + doc_id:'somerandomid', + first_name:'zirst', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'scond', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'%ds', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'#dsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + } + ] +} + +function ContactList () { + const [state, setState] = useState(defaultState) + const eventsHandler = () => { + + } + return ( + + ) +} + +export default ContactList diff --git a/client/src/pages/test.js b/client/src/pages/test.js new file mode 100644 index 0000000..5aa8178 --- /dev/null +++ b/client/src/pages/test.js @@ -0,0 +1,121 @@ +import { useState, useEffect } from 'react' +import { db } from '@/utils/firebase/config' +import { collection, doc, getDocs, getDoc, setDoc, query, where, orderBy, limit, deleteDoc } from 'firebase/firestore' +import { useAuth } from '@/lib/hooks/useAuth' + +function TestPage () { + const [contacts, setContacts] = useState([]) + const { authUser, authLoading } = useAuth() + + useEffect(() => { + if (!authLoading && authUser) { + listDocuments() + } + }, [authLoading, authUser]) + + // Generate a firestore document ID + const generateFirestoreID = (collectionName) => doc(collection(db, collectionName)) + + // Create a document under a sub collection + const createDocument = async (e) => { + if (!authUser && !authLoading) { + console.log('no user') + return + } + + e.preventDefault() + const { first_name, middle_name, last_name, phone_no, photo_url, collection_name } = e.target + + const subcollectionName = `${collection_name.value}/${authUser.uid}/list` + const docId = generateFirestoreID(subcollectionName) + + const contactObj = { + id: docId.id, + first_name: first_name.value, + middle_name: middle_name.value, + last_name: last_name.value, + phone_no: phone_no.value, + photo_url: photo_url.value + } + + await setDoc(doc(db, subcollectionName, docId.id), contactObj) + alert(`New document created!\n${subcollectionName}\n${docId.id}`) + listDocuments(subcollectionName) + } + + // Fetch all documents in a subcollection + const listDocuments = async (collection_name) => { + const subcollectionName = collection_name || `users/${authUser.uid}/list` + const querySnapshot = await getDocs(collection(db, subcollectionName)) + + const data = querySnapshot.docs.reduce((list, item) => { + list.push(item.data()) + return list + }, []) + + setContacts(data) + } + + // Delete a document by document docID + const deleteDocument = async (docId) => { + const docPath = `users/${authUser.uid}/list/${docId}` + await deleteDoc(doc(db, docPath)) + alert(`Document ${docId}, deleted`) + listDocuments() + } + + // Get a single document by docID + const getDocument = async (docId) => { + const docPath = `users/${authUser.uid}/list/${docId}` + const docRef = doc(db, docPath) + const docSnap = await getDoc(docRef) + + if (docSnap.exists()) { + const info = JSON.stringify(docSnap.data()) + alert(`Document ${docId}]\n${info}`) + } else { + alert(`Document ${docId} does not exist`) + } + } + + return ( +
+

Test Page

+ +
+
+
+
+
+
+
+ +
+ +
+ + {authLoading + ?

Loading...

+ : (authUser) + ?

Contacts list of User ID: {authUser.uid}

+ :

No User

+ } + +
    + {contacts.map((item, id) => ( +
  • + {item.id}: {item.first_name} {item.middle_name}. {item.last_name} + +   +   + +
  • + ))} +
+
+ ) +} + +export default TestPage diff --git a/client/src/styles/globals.css b/client/src/styles/globals.css index a5fa11c..13b9fd9 100644 --- a/client/src/styles/globals.css +++ b/client/src/styles/globals.css @@ -1,9 +1,14 @@ /* for debug */ * { - /* outline: 1px solid gray; */ + outline: 1px solid gray; } html, body { width:100%; height:100% +} + +#__next { + width:100%; + min-height:100% } \ No newline at end of file From 9c316721c57b1ba3d1b16e478a5872998e058454 Mon Sep 17 00:00:00 2001 From: vikyw89 Date: Thu, 16 Mar 2023 16:00:35 +0800 Subject: [PATCH 02/81] progress: checkpoint adjusting header footer sx --- client/src/common/contactCard/index.js | 17 - .../contactCard/index.js | 58 ++++ .../src/common/contactsCardContainer/index.js | 26 ++ client/src/components/contactList/index.js | 54 +-- client/src/pages/contactList/index.js | 323 +++++++++++++++++- client/src/styles/globals.css | 1 + 6 files changed, 439 insertions(+), 40 deletions(-) delete mode 100644 client/src/common/contactCard/index.js create mode 100644 client/src/common/contactsCardContainer/contactCard/index.js create mode 100644 client/src/common/contactsCardContainer/index.js diff --git a/client/src/common/contactCard/index.js b/client/src/common/contactCard/index.js deleted file mode 100644 index 3d8dd1a..0000000 --- a/client/src/common/contactCard/index.js +++ /dev/null @@ -1,17 +0,0 @@ -import { Avatar, Paper, Typography } from "@mui/material" -import { useState } from "react" - - -export const ContactCard = ({ contact }) => { - return ( - - - {contact.first_name} - - ) -} \ No newline at end of file diff --git a/client/src/common/contactsCardContainer/contactCard/index.js b/client/src/common/contactsCardContainer/contactCard/index.js new file mode 100644 index 0000000..a0ac088 --- /dev/null +++ b/client/src/common/contactsCardContainer/contactCard/index.js @@ -0,0 +1,58 @@ +import { Avatar, Paper, Typography, useTheme } from "@mui/material" +import { useState } from "react" + +export const ContactCard = ({ contact }) => { + const theme = useTheme() + + const backgroundColorGenerator = (name) => { + let value = 0 + for (let i = 0; i < name.length; i++) { + value = value + name.charCodeAt(i) + } + value = value % 255 + return value + } + + const getInitial = (first, second, last) => { + const firstInitial = (first.match(new RegExp( + String.raw`(?^[a-z])`, 'i')) ?? [''])[0] + const middleInitial = (second.match(new RegExp( + String.raw`(?^[a-z])`, 'i')) ?? [''])[0] + const lastInitial = (second.match(new RegExp( + String.raw`(?^[a-z])`, 'i')) ?? [''])[0] + return `${firstInitial}${middleInitial}${lastInitial}` + } + + const initial = getInitial(contact.first_name, contact.middle_name, contact.last_name).toUpperCase() + + const backgroundColor = backgroundColorGenerator(initial) + return ( + + + {initial} + + + + {contact.first_name} {contact.middle_name} {contact.last_name} + + + ) +} \ No newline at end of file diff --git a/client/src/common/contactsCardContainer/index.js b/client/src/common/contactsCardContainer/index.js new file mode 100644 index 0000000..fa84e36 --- /dev/null +++ b/client/src/common/contactsCardContainer/index.js @@ -0,0 +1,26 @@ +import { Avatar, Box, Paper, Typography } from "@mui/material" +import { useState } from "react" +import { ContactCard } from "./contactCard" + + +export const ContactCardsContainer = ({ content }) => { + const { group, contacts } = content + return ( + + {group} + + {contacts.map((el, index) => { + return ( + + ) + })} + + + ) +} \ No newline at end of file diff --git a/client/src/components/contactList/index.js b/client/src/components/contactList/index.js index c978cf0..c79ee72 100644 --- a/client/src/components/contactList/index.js +++ b/client/src/components/contactList/index.js @@ -12,47 +12,57 @@ import Paper from '@mui/material/Paper' import SimpleSnackbar from '@/common/snackbars/simpleSnackbar' import TransparentTextfield from '@/common/ui/transparentfield' import { useTheme } from '@emotion/react' -import { ContactCard } from '@/common/contactCard' +import { ContactCard } from '@/common/contactsCardContainer/contactCard' import { Box } from '@mui/material' +import { ContactCardsContainer } from '@/common/contactsCardContainer' function ContactListComponent({ state, eventsHandler }) { const theme = useTheme() // divide state.contacts into each container const sortedContacts = [...state.contacts].sort((a, b) => a.first_name < b.first_name ? -1 : 1) + const groupedSortedContacts = [...sortedContacts].reduce((acc, curr) => { const capitalizedFirstNameFirstLetterChar = curr.first_name.match(new RegExp( String.raw`(?^[a-z])|`, 'i'))[0] if (!capitalizedFirstNameFirstLetterChar) { - acc.misc.push(curr) + if (!acc.misc) acc.misc = [] + acc.misc = [...acc.misc, curr] } else { - acc[capitalizedFirstNameFirstLetterChar] + if (!acc[capitalizedFirstNameFirstLetterChar]) { + acc[capitalizedFirstNameFirstLetterChar.toUpperCase()] = [] + } + acc[capitalizedFirstNameFirstLetterChar.toUpperCase()] = [ + ...acc[capitalizedFirstNameFirstLetterChar.toUpperCase()], + curr + ] } - console.log(firstNameFirstLetterChar) - - }) + return acc + }, {}) + const groupedSortedContactsArr = Object.entries(groupedSortedContacts) return ( - - Search Bar + + Left Container - - {state.contacts.map((el, index) => { - return ( - - ) - })} + + + + {groupedSortedContactsArr.map((el, index) => { + return ( + + ) + })} + + diff --git a/client/src/pages/contactList/index.js b/client/src/pages/contactList/index.js index 3e46907..b7ac70c 100644 --- a/client/src/pages/contactList/index.js +++ b/client/src/pages/contactList/index.js @@ -43,7 +43,328 @@ const defaultState = { date_updated:new Date(), contact_no:'somerandomnumber', contact_email:'somerandomemail' - } + }, + { + doc_id:'somerandomid', + first_name:'zirst', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'scond', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'%ds', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'#dsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'zirst', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'scond', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'%ds', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'#dsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'zirst', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'scond', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'%ds', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'#dsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'zirst', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'scond', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'%ds', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'#dsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'zirst', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'scond', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'%ds', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'#dsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'zirst', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'scond', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'%ds', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'#dsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'zirst', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'scond', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'%ds', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'#dsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'zirst', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'scond', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'%ds', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'#dsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + ] } diff --git a/client/src/styles/globals.css b/client/src/styles/globals.css index 13b9fd9..6357b20 100644 --- a/client/src/styles/globals.css +++ b/client/src/styles/globals.css @@ -1,6 +1,7 @@ /* for debug */ * { outline: 1px solid gray; + overflow: unset; } html, body { From ce7493bce3d46edfbb6652db9a1661ce670a0794 Mon Sep 17 00:00:00 2001 From: vikyw89 Date: Thu, 16 Mar 2023 16:31:41 +0800 Subject: [PATCH 03/81] checkpoint: progress on header footer overflow --- client/src/common/layout/header/index.js | 3 +- client/src/common/layout/page/index.js | 13 +- client/src/common/layout/section/styles.js | 3 +- client/src/components/contactList/index.js | 24 +- client/src/styles/Home.module.css | 278 --------------------- client/src/styles/globals.css | 2 +- 6 files changed, 25 insertions(+), 298 deletions(-) delete mode 100644 client/src/styles/Home.module.css diff --git a/client/src/common/layout/header/index.js b/client/src/common/layout/header/index.js index 6736cc9..2ac54e1 100644 --- a/client/src/common/layout/header/index.js +++ b/client/src/common/layout/header/index.js @@ -83,8 +83,7 @@ function Header() { return ( { position: 'fixed', zIndex: '-1', top: 0, - width: '100vw', - height: '100vh', - width: '100vmax', - height: '100vmax', + width: '100%', + height: '100%', background: `hsla(${bgColor},40%,80%,${activeTheme === 'dark' ? '10%' : '80%'})`, animation: 'animate 90s linear infinite', }, @@ -80,12 +78,11 @@ function Page ({ children }) { <>
diff --git a/client/src/common/layout/section/styles.js b/client/src/common/layout/section/styles.js index 91173a1..93aa0d7 100644 --- a/client/src/common/layout/section/styles.js +++ b/client/src/common/layout/section/styles.js @@ -1,9 +1,10 @@ const styles = { container: { - minHeight: '100%', + height: '100%', flex: 1, display: 'flex', flexDirection: 'column', + overflow: 'auto' } } diff --git a/client/src/components/contactList/index.js b/client/src/components/contactList/index.js index c79ee72..2e5a876 100644 --- a/client/src/components/contactList/index.js +++ b/client/src/components/contactList/index.js @@ -13,7 +13,7 @@ import SimpleSnackbar from '@/common/snackbars/simpleSnackbar' import TransparentTextfield from '@/common/ui/transparentfield' import { useTheme } from '@emotion/react' import { ContactCard } from '@/common/contactsCardContainer/contactCard' -import { Box } from '@mui/material' +import { Box, TextField } from '@mui/material' import { ContactCardsContainer } from '@/common/contactsCardContainer' function ContactListComponent({ state, eventsHandler }) { @@ -46,16 +46,24 @@ function ContactListComponent({ state, eventsHandler }) { flex: 1, display: 'flex', flexWrap: 'wrap', - gridTemplateColumns: '2fr 5fr', gap: '10px', - position: 'relative' + overflow:'hidden' }}> - - Left Container + + + Search bar + + + {/* this will show a profile if either only 1 result appear after search, or if user click a profile on the right */} + View Profile + + + {/* related setting to print or export pdf */} + Settings + - - - + + {groupedSortedContactsArr.map((el, index) => { return ( diff --git a/client/src/styles/Home.module.css b/client/src/styles/Home.module.css deleted file mode 100644 index 27dfff5..0000000 --- a/client/src/styles/Home.module.css +++ /dev/null @@ -1,278 +0,0 @@ -.main { - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: center; - padding: 6rem; - min-height: 100vh; -} - -.description { - display: inherit; - justify-content: inherit; - align-items: inherit; - font-size: 0.85rem; - max-width: var(--max-width); - width: 100%; - z-index: 2; - font-family: var(--font-mono); -} - -.description a { - display: flex; - justify-content: center; - align-items: center; - gap: 0.5rem; -} - -.description p { - position: relative; - margin: 0; - padding: 1rem; - background-color: rgba(var(--callout-rgb), 0.5); - border: 1px solid rgba(var(--callout-border-rgb), 0.3); - border-radius: var(--border-radius); -} - -.code { - font-weight: 700; - font-family: var(--font-mono); -} - -.grid { - display: grid; - grid-template-columns: repeat(4, minmax(25%, auto)); - width: var(--max-width); - max-width: 100%; -} - -.card { - padding: 1rem 1.2rem; - border-radius: var(--border-radius); - background: rgba(var(--card-rgb), 0); - border: 1px solid rgba(var(--card-border-rgb), 0); - transition: background 200ms, border 200ms; -} - -.card span { - display: inline-block; - transition: transform 200ms; -} - -.card h2 { - font-weight: 600; - margin-bottom: 0.7rem; -} - -.card p { - margin: 0; - opacity: 0.6; - font-size: 0.9rem; - line-height: 1.5; - max-width: 30ch; -} - -.center { - display: flex; - justify-content: center; - align-items: center; - position: relative; - padding: 4rem 0; -} - -.center::before { - background: var(--secondary-glow); - border-radius: 50%; - width: 480px; - height: 360px; - margin-left: -400px; -} - -.center::after { - background: var(--primary-glow); - width: 240px; - height: 180px; - z-index: -1; -} - -.center::before, -.center::after { - content: ''; - left: 50%; - position: absolute; - filter: blur(45px); - transform: translateZ(0); -} - -.logo, -.thirteen { - position: relative; -} - -.thirteen { - display: flex; - justify-content: center; - align-items: center; - width: 75px; - height: 75px; - padding: 25px 10px; - margin-left: 16px; - transform: translateZ(0); - border-radius: var(--border-radius); - overflow: hidden; - box-shadow: 0px 2px 8px -1px #0000001a; -} - -.thirteen::before, -.thirteen::after { - content: ''; - position: absolute; - z-index: -1; -} - -/* Conic Gradient Animation */ -.thirteen::before { - animation: 6s rotate linear infinite; - width: 200%; - height: 200%; - background: var(--tile-border); -} - -/* Inner Square */ -.thirteen::after { - inset: 0; - padding: 1px; - border-radius: var(--border-radius); - background: linear-gradient( - to bottom right, - rgba(var(--tile-start-rgb), 1), - rgba(var(--tile-end-rgb), 1) - ); - background-clip: content-box; -} - -/* Enable hover only on non-touch devices */ -@media (hover: hover) and (pointer: fine) { - .card:hover { - background: rgba(var(--card-rgb), 0.1); - border: 1px solid rgba(var(--card-border-rgb), 0.15); - } - - .card:hover span { - transform: translateX(4px); - } -} - -@media (prefers-reduced-motion) { - .thirteen::before { - animation: none; - } - - .card:hover span { - transform: none; - } -} - -/* Mobile */ -@media (max-width: 700px) { - .content { - padding: 4rem; - } - - .grid { - grid-template-columns: 1fr; - margin-bottom: 120px; - max-width: 320px; - text-align: center; - } - - .card { - padding: 1rem 2.5rem; - } - - .card h2 { - margin-bottom: 0.5rem; - } - - .center { - padding: 8rem 0 6rem; - } - - .center::before { - transform: none; - height: 300px; - } - - .description { - font-size: 0.8rem; - } - - .description a { - padding: 1rem; - } - - .description p, - .description div { - display: flex; - justify-content: center; - position: fixed; - width: 100%; - } - - .description p { - align-items: center; - inset: 0 0 auto; - padding: 2rem 1rem 1.4rem; - border-radius: 0; - border: none; - border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25); - background: linear-gradient( - to bottom, - rgba(var(--background-start-rgb), 1), - rgba(var(--callout-rgb), 0.5) - ); - background-clip: padding-box; - backdrop-filter: blur(24px); - } - - .description div { - align-items: flex-end; - pointer-events: none; - inset: auto 0 0; - padding: 2rem; - height: 200px; - background: linear-gradient( - to bottom, - transparent 0%, - rgb(var(--background-end-rgb)) 40% - ); - z-index: 1; - } -} - -/* Tablet and Smaller Desktop */ -@media (min-width: 701px) and (max-width: 1120px) { - .grid { - grid-template-columns: repeat(2, 50%); - } -} - -@media (prefers-color-scheme: dark) { - .vercelLogo { - filter: invert(1); - } - - .logo, - .thirteen img { - filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70); - } -} - -@keyframes rotate { - from { - transform: rotate(360deg); - } - to { - transform: rotate(0deg); - } -} diff --git a/client/src/styles/globals.css b/client/src/styles/globals.css index 6357b20..d03c734 100644 --- a/client/src/styles/globals.css +++ b/client/src/styles/globals.css @@ -11,5 +11,5 @@ html, body { #__next { width:100%; - min-height:100% + height:100% } \ No newline at end of file From d0d5ef5322ee0e9d51e8cae365cbf8e1e6ca27e9 Mon Sep 17 00:00:00 2001 From: vikyw89 Date: Thu, 16 Mar 2023 17:06:22 +0800 Subject: [PATCH 04/81] chore: contactList container progress --- client/src/common/layout/page/index.js | 1 - client/src/components/contactList/index.js | 38 +++++++++++++++++----- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/client/src/common/layout/page/index.js b/client/src/common/layout/page/index.js index a400726..05fd475 100644 --- a/client/src/common/layout/page/index.js +++ b/client/src/common/layout/page/index.js @@ -82,7 +82,6 @@ function Page ({ children }) { height: '100%', display:'grid', gridTemplateRows:'auto 1fr auto', - border:'5px solid black', }}>
diff --git a/client/src/components/contactList/index.js b/client/src/components/contactList/index.js index 2e5a876..cf64264 100644 --- a/client/src/components/contactList/index.js +++ b/client/src/components/contactList/index.js @@ -15,6 +15,7 @@ import { useTheme } from '@emotion/react' import { ContactCard } from '@/common/contactsCardContainer/contactCard' import { Box, TextField } from '@mui/material' import { ContactCardsContainer } from '@/common/contactsCardContainer' +import { TextFields } from '@mui/icons-material' function ContactListComponent({ state, eventsHandler }) { const theme = useTheme() @@ -46,23 +47,44 @@ function ContactListComponent({ state, eventsHandler }) { flex: 1, display: 'flex', flexWrap: 'wrap', - gap: '10px', - overflow:'hidden' + overflow: 'hidden', }}> - + - Search bar + {/* this will show a profile if either only 1 result appear after search, or if user click a profile on the right */} - View Profile + + View Profile + {/* related setting to print or export pdf */} - Settings + + Settings + - - + + {groupedSortedContactsArr.map((el, index) => { return ( From 3db22d365bcdf5cc920bd669644f3e19b38594ce Mon Sep 17 00:00:00 2001 From: vikyw89 Date: Thu, 16 Mar 2023 17:46:16 +0800 Subject: [PATCH 05/81] chore: hide scrollBar on contactListContainer --- .../contactCard/index.js | 9 ++++---- .../src/common/contactsCardContainer/index.js | 2 +- client/src/common/layout/header/index.js | 2 +- client/src/common/layout/section/index.js | 1 + client/src/components/contactList/index.js | 22 ++++++++++++++----- 5 files changed, 24 insertions(+), 12 deletions(-) diff --git a/client/src/common/contactsCardContainer/contactCard/index.js b/client/src/common/contactsCardContainer/contactCard/index.js index a0ac088..46db747 100644 --- a/client/src/common/contactsCardContainer/contactCard/index.js +++ b/client/src/common/contactsCardContainer/contactCard/index.js @@ -34,12 +34,12 @@ export const ContactCard = ({ contact }) => { padding: '15px', gap: '15px', alignItems: 'center', - borderRadius: '15px' - // border:'2px solid red' + borderRadius: '10px', + width:'auto', }}> {initial} @@ -49,7 +49,8 @@ export const ContactCard = ({ contact }) => { variant="h6" sx={{ flex: '3', - color: theme.palette.primary + color: theme.palette.primary, + }}> {contact.first_name} {contact.middle_name} {contact.last_name} diff --git a/client/src/common/contactsCardContainer/index.js b/client/src/common/contactsCardContainer/index.js index fa84e36..fe57384 100644 --- a/client/src/common/contactsCardContainer/index.js +++ b/client/src/common/contactsCardContainer/index.js @@ -7,7 +7,7 @@ export const ContactCardsContainer = ({ content }) => { const { group, contacts } = content return ( - {group} + {group === 'misc' ? '' : group} - + diff --git a/client/src/components/contactList/index.js b/client/src/components/contactList/index.js index cf64264..2dd8853 100644 --- a/client/src/components/contactList/index.js +++ b/client/src/components/contactList/index.js @@ -56,8 +56,10 @@ function ContactListComponent({ state, eventsHandler }) { minWidth: '200px', zIndex: '50', backgroundColor: 'inherit', - padding: '20px' - // backdropFilter:'contrast(80%)' + padding: '20px', + overflow: 'hidden', + width: '100%', + height: '100%' }} > @@ -82,16 +84,24 @@ function ContactListComponent({ state, eventsHandler }) { minWidth: '500px', border: '2px solid black', maxHeight: '100%', - overflowY: 'auto', - padding:'20px', + width: '100%', + height: '100%', + padding: '20px', + overflowY:'scroll', + userSelect:'none', + '&::-webkit-scrollbar': { + display:'none', + }, + }}> + - {groupedSortedContactsArr.map((el, index) => { return ( ) })} - From 8c9abe798c204acf0468f1af42d458b3dc588e02 Mon Sep 17 00:00:00 2001 From: vikyw89 Date: Thu, 16 Mar 2023 18:12:59 +0800 Subject: [PATCH 06/81] chore: moved container hash, and adjust the stack --- .../contactCard/index.js | 3 + .../src/common/contactsCardContainer/index.js | 25 +++- client/src/components/contactList/index.js | 5 +- client/src/pages/contactList/index.js | 120 ++++++++++++++++-- 4 files changed, 136 insertions(+), 17 deletions(-) diff --git a/client/src/common/contactsCardContainer/contactCard/index.js b/client/src/common/contactsCardContainer/contactCard/index.js index 46db747..3a74aea 100644 --- a/client/src/common/contactsCardContainer/contactCard/index.js +++ b/client/src/common/contactsCardContainer/contactCard/index.js @@ -36,6 +36,9 @@ export const ContactCard = ({ contact }) => { alignItems: 'center', borderRadius: '10px', width:'auto', + '&:hover' :{ + backdropFilter: 'contrast(120%)' + } }}> { const { group, contacts } = content return ( - - {group === 'misc' ? '' : group} + + + {group === 'misc' ? '' : group} + {contacts.map((el, index) => { return ( diff --git a/client/src/components/contactList/index.js b/client/src/components/contactList/index.js index 2dd8853..37a1282 100644 --- a/client/src/components/contactList/index.js +++ b/client/src/components/contactList/index.js @@ -29,7 +29,7 @@ function ContactListComponent({ state, eventsHandler }) { if (!acc.misc) acc.misc = [] acc.misc = [...acc.misc, curr] } else { - if (!acc[capitalizedFirstNameFirstLetterChar]) { + if (!acc[capitalizedFirstNameFirstLetterChar.toUpperCase()]) { acc[capitalizedFirstNameFirstLetterChar.toUpperCase()] = [] } acc[capitalizedFirstNameFirstLetterChar.toUpperCase()] = [ @@ -40,7 +40,7 @@ function ContactListComponent({ state, eventsHandler }) { return acc }, {}) const groupedSortedContactsArr = Object.entries(groupedSortedContacts) - + console.log(groupedSortedContacts) return ( diff --git a/client/src/pages/contactList/index.js b/client/src/pages/contactList/index.js index b7ac70c..60ff35d 100644 --- a/client/src/pages/contactList/index.js +++ b/client/src/pages/contactList/index.js @@ -26,7 +26,7 @@ const defaultState = { }, { doc_id:'somerandomid', - first_name:'%ds', + first_name:'ads', last_name:'third', middle_name:'second', date_created:new Date(), @@ -36,7 +36,7 @@ const defaultState = { }, { doc_id:'somerandomid', - first_name:'#dsad', + first_name:'adsad', last_name:'third', middle_name:'second', date_created:new Date(), @@ -66,7 +66,7 @@ const defaultState = { }, { doc_id:'somerandomid', - first_name:'%ds', + first_name:'bds', last_name:'third', middle_name:'second', date_created:new Date(), @@ -76,7 +76,7 @@ const defaultState = { }, { doc_id:'somerandomid', - first_name:'#dsad', + first_name:'cdsad', last_name:'third', middle_name:'second', date_created:new Date(), @@ -96,7 +96,7 @@ const defaultState = { }, { doc_id:'somerandomid', - first_name:'scond', + first_name:'dcond', last_name:'third', middle_name:'second', date_created:new Date(), @@ -106,7 +106,7 @@ const defaultState = { }, { doc_id:'somerandomid', - first_name:'%ds', + first_name:'eds', last_name:'third', middle_name:'second', date_created:new Date(), @@ -116,7 +116,7 @@ const defaultState = { }, { doc_id:'somerandomid', - first_name:'#dsad', + first_name:'edsad', last_name:'third', middle_name:'second', date_created:new Date(), @@ -136,7 +136,7 @@ const defaultState = { }, { doc_id:'somerandomid', - first_name:'scond', + first_name:'fcond', last_name:'third', middle_name:'second', date_created:new Date(), @@ -146,7 +146,7 @@ const defaultState = { }, { doc_id:'somerandomid', - first_name:'%ds', + first_name:'gds', last_name:'third', middle_name:'second', date_created:new Date(), @@ -156,7 +156,107 @@ const defaultState = { }, { doc_id:'somerandomid', - first_name:'#dsad', + first_name:'adsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'adsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'adsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'adsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'adsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'adsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'adsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'adsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'adsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'adsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'adsad', last_name:'third', middle_name:'second', date_created:new Date(), From e676312c79e252e5e1fefba68731ce71bf81b579 Mon Sep 17 00:00:00 2001 From: vikyw89 Date: Thu, 16 Mar 2023 18:43:55 +0800 Subject: [PATCH 07/81] fix: uniformize the contact card size --- .../contactsCardContainer/contactCard/index.js | 16 ++++++++++------ client/src/common/contactsCardContainer/index.js | 3 ++- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/client/src/common/contactsCardContainer/contactCard/index.js b/client/src/common/contactsCardContainer/contactCard/index.js index 3a74aea..735271b 100644 --- a/client/src/common/contactsCardContainer/contactCard/index.js +++ b/client/src/common/contactsCardContainer/contactCard/index.js @@ -30,13 +30,15 @@ export const ContactCard = ({ contact }) => { @@ -51,10 +53,12 @@ export const ContactCard = ({ contact }) => { + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap' + }} + > {contact.first_name} {contact.middle_name} {contact.last_name} diff --git a/client/src/common/contactsCardContainer/index.js b/client/src/common/contactsCardContainer/index.js index b2e7def..5f65bae 100644 --- a/client/src/common/contactsCardContainer/index.js +++ b/client/src/common/contactsCardContainer/index.js @@ -30,7 +30,8 @@ export const ContactCardsContainer = ({ content }) => { gridTemplateColumns: 'repeat(auto-fill, minmax(250px, 1fr))', alignItems: 'start', gap: '10px', - flex: '1' + flex: '1', + borderBottom:'1px dashed grey', }}> {contacts.map((el, index) => { return ( From e8b2c8cde1af732a8f03349db7602383fbb02ba0 Mon Sep 17 00:00:00 2001 From: vikyw89 Date: Thu, 16 Mar 2023 22:58:01 +0800 Subject: [PATCH 08/81] progress: added searchContactContainer --- .../common/contactsCardContainer/contactCard/index.js | 4 +++- client/src/common/contactsCardContainer/index.js | 4 ++-- client/src/common/searchContactContainer/index.js | 9 +++++++++ client/src/styles/globals.css | 2 +- 4 files changed, 15 insertions(+), 4 deletions(-) create mode 100644 client/src/common/searchContactContainer/index.js diff --git a/client/src/common/contactsCardContainer/contactCard/index.js b/client/src/common/contactsCardContainer/contactCard/index.js index 735271b..5652f58 100644 --- a/client/src/common/contactsCardContainer/contactCard/index.js +++ b/client/src/common/contactsCardContainer/contactCard/index.js @@ -27,7 +27,7 @@ export const ContactCard = ({ contact }) => { const backgroundColor = backgroundColorGenerator(initial) return ( - { alignItems: 'center', borderRadius: '10px', width: 'auto', + border: '1px solid grey', '&:hover': { backdropFilter: 'contrast(120%)' } @@ -45,6 +46,7 @@ export const ContactCard = ({ contact }) => { {initial} diff --git a/client/src/common/contactsCardContainer/index.js b/client/src/common/contactsCardContainer/index.js index 5f65bae..b110b47 100644 --- a/client/src/common/contactsCardContainer/index.js +++ b/client/src/common/contactsCardContainer/index.js @@ -10,7 +10,8 @@ export const ContactCardsContainer = ({ content }) => { sx={{ display: 'grid', gridTemplateColumns: '50px 1fr', - padding: '20px' + padding: '20px', + borderBottom:'1px dashed grey' }} > { alignItems: 'start', gap: '10px', flex: '1', - borderBottom:'1px dashed grey', }}> {contacts.map((el, index) => { return ( diff --git a/client/src/common/searchContactContainer/index.js b/client/src/common/searchContactContainer/index.js new file mode 100644 index 0000000..ff187b1 --- /dev/null +++ b/client/src/common/searchContactContainer/index.js @@ -0,0 +1,9 @@ +import { Box } from "@mui/material" + +export const SearchContactContainer = () => { + return ( + + + + ) +} \ No newline at end of file diff --git a/client/src/styles/globals.css b/client/src/styles/globals.css index d03c734..5a14717 100644 --- a/client/src/styles/globals.css +++ b/client/src/styles/globals.css @@ -1,6 +1,6 @@ /* for debug */ * { - outline: 1px solid gray; + /* outline: 1px solid gray; */ overflow: unset; } From 8f339bddf96302a22ad432cc8e48f7107fe93f8b Mon Sep 17 00:00:00 2001 From: vikyw89 Date: Thu, 16 Mar 2023 23:10:32 +0800 Subject: [PATCH 09/81] chore: added syncGlobalVariable --- .../contactCard/index.js | 118 +-- .../src/common/contactsCardContainer/index.js | 80 +- .../common/searchContactContainer/index.js | 10 +- client/src/components/contactList/index.js | 174 ++-- client/src/lib/hooks/useSync.js | 41 + client/src/pages/contactList/index.js | 926 +++++++++--------- client/src/pages/{test.js => test.js.example} | 0 7 files changed, 695 insertions(+), 654 deletions(-) rename client/src/pages/{test.js => test.js.example} (100%) diff --git a/client/src/common/contactsCardContainer/contactCard/index.js b/client/src/common/contactsCardContainer/contactCard/index.js index 5652f58..07d6de2 100644 --- a/client/src/common/contactsCardContainer/contactCard/index.js +++ b/client/src/common/contactsCardContainer/contactCard/index.js @@ -1,68 +1,68 @@ -import { Avatar, Paper, Typography, useTheme } from "@mui/material" -import { useState } from "react" +import { Avatar, Paper, Typography, useTheme } from '@mui/material' +import { useState } from 'react' export const ContactCard = ({ contact }) => { - const theme = useTheme() + const theme = useTheme() - const backgroundColorGenerator = (name) => { - let value = 0 - for (let i = 0; i < name.length; i++) { - value = value + name.charCodeAt(i) - } - value = value % 255 - return value + const backgroundColorGenerator = (name) => { + let value = 0 + for (let i = 0; i < name.length; i++) { + value = value + name.charCodeAt(i) } + value = value % 255 + return value + } - const getInitial = (first, second, last) => { - const firstInitial = (first.match(new RegExp( - String.raw`(?^[a-z])`, 'i')) ?? [''])[0] - const middleInitial = (second.match(new RegExp( - String.raw`(?^[a-z])`, 'i')) ?? [''])[0] - const lastInitial = (second.match(new RegExp( - String.raw`(?^[a-z])`, 'i')) ?? [''])[0] - return `${firstInitial}${middleInitial}${lastInitial}` - } + const getInitial = (first, second, last) => { + const firstInitial = (first.match(new RegExp( + String.raw`(?^[a-z])`, 'i')) ?? [''])[0] + const middleInitial = (second.match(new RegExp( + String.raw`(?^[a-z])`, 'i')) ?? [''])[0] + const lastInitial = (second.match(new RegExp( + String.raw`(?^[a-z])`, 'i')) ?? [''])[0] + return `${firstInitial}${middleInitial}${lastInitial}` + } - const initial = getInitial(contact.first_name, contact.middle_name, contact.last_name).toUpperCase() + const initial = getInitial(contact.first_name, contact.middle_name, contact.last_name).toUpperCase() - const backgroundColor = backgroundColorGenerator(initial) - return ( - - - {initial} - + const backgroundColor = backgroundColorGenerator(initial) + return ( + + + {initial} + - - {contact.first_name} {contact.middle_name} {contact.last_name} - - - ) + + {contact.first_name} {contact.middle_name} {contact.last_name} + + + ) } \ No newline at end of file diff --git a/client/src/common/contactsCardContainer/index.js b/client/src/common/contactsCardContainer/index.js index b110b47..2ff6131 100644 --- a/client/src/common/contactsCardContainer/index.js +++ b/client/src/common/contactsCardContainer/index.js @@ -1,44 +1,44 @@ -import { Avatar, Box, Paper, Typography } from "@mui/material" -import { useState } from "react" -import { ContactCard } from "./contactCard" +import { Avatar, Box, Paper, Typography } from '@mui/material' +import { useState } from 'react' +import { ContactCard } from './contactCard' export const ContactCardsContainer = ({ content }) => { - const { group, contacts } = content - return ( - - - {group === 'misc' ? '' : group} - - - {contacts.map((el, index) => { - return ( - - ) - })} - - - ) + const { group, contacts } = content + return ( + + + {group === 'misc' ? '' : group} + + + {contacts.map((el, index) => { + return ( + + ) + })} + + + ) } \ No newline at end of file diff --git a/client/src/common/searchContactContainer/index.js b/client/src/common/searchContactContainer/index.js index ff187b1..a60fc7a 100644 --- a/client/src/common/searchContactContainer/index.js +++ b/client/src/common/searchContactContainer/index.js @@ -1,9 +1,9 @@ -import { Box } from "@mui/material" +import { Box } from '@mui/material' export const SearchContactContainer = () => { - return ( - + return ( + - - ) + + ) } \ No newline at end of file diff --git a/client/src/components/contactList/index.js b/client/src/components/contactList/index.js index 37a1282..4473862 100644 --- a/client/src/components/contactList/index.js +++ b/client/src/components/contactList/index.js @@ -18,98 +18,98 @@ import { ContactCardsContainer } from '@/common/contactsCardContainer' import { TextFields } from '@mui/icons-material' function ContactListComponent({ state, eventsHandler }) { - const theme = useTheme() - // divide state.contacts into each container - const sortedContacts = [...state.contacts].sort((a, b) => a.first_name < b.first_name ? -1 : 1) + const theme = useTheme() + // divide state.contacts into each container + const sortedContacts = [...state.contacts].sort((a, b) => a.first_name < b.first_name ? -1 : 1) - const groupedSortedContacts = [...sortedContacts].reduce((acc, curr) => { - const capitalizedFirstNameFirstLetterChar = curr.first_name.match(new RegExp( - String.raw`(?^[a-z])|`, 'i'))[0] - if (!capitalizedFirstNameFirstLetterChar) { - if (!acc.misc) acc.misc = [] - acc.misc = [...acc.misc, curr] - } else { - if (!acc[capitalizedFirstNameFirstLetterChar.toUpperCase()]) { - acc[capitalizedFirstNameFirstLetterChar.toUpperCase()] = [] - } - acc[capitalizedFirstNameFirstLetterChar.toUpperCase()] = [ - ...acc[capitalizedFirstNameFirstLetterChar.toUpperCase()], - curr - ] - } - return acc - }, {}) - const groupedSortedContactsArr = Object.entries(groupedSortedContacts) - console.log(groupedSortedContacts) - return ( - - - - - - - - {/* this will show a profile if either only 1 result appear after search, or if user click a profile on the right */} - + const groupedSortedContacts = [...sortedContacts].reduce((acc, curr) => { + const capitalizedFirstNameFirstLetterChar = curr.first_name.match(new RegExp( + String.raw`(?^[a-z])|`, 'i'))[0] + if (!capitalizedFirstNameFirstLetterChar) { + if (!acc.misc) acc.misc = [] + acc.misc = [...acc.misc, curr] + } else { + if (!acc[capitalizedFirstNameFirstLetterChar.toUpperCase()]) { + acc[capitalizedFirstNameFirstLetterChar.toUpperCase()] = [] + } + acc[capitalizedFirstNameFirstLetterChar.toUpperCase()] = [ + ...acc[capitalizedFirstNameFirstLetterChar.toUpperCase()], + curr + ] + } + return acc + }, {}) + const groupedSortedContactsArr = Object.entries(groupedSortedContacts) + console.log(groupedSortedContacts) + return ( + + + + + + + + {/* this will show a profile if either only 1 result appear after search, or if user click a profile on the right */} + View Profile - - - - {/* related setting to print or export pdf */} - + + + + {/* related setting to print or export pdf */} + Settings - - - - - - {groupedSortedContactsArr.map((el, index) => { - return ( - - ) - })} - - - - - ) + + + + + + {groupedSortedContactsArr.map((el, index) => { + return ( + + ) + })} + + + + + ) } ContactListComponent.propTypes = { - state: PropTypes.object, - eventsHandler: PropTypes.func + state: PropTypes.object, + eventsHandler: PropTypes.func } export default ContactListComponent diff --git a/client/src/lib/hooks/useSync.js b/client/src/lib/hooks/useSync.js index b839454..f42776b 100644 --- a/client/src/lib/hooks/useSync.js +++ b/client/src/lib/hooks/useSync.js @@ -2,6 +2,9 @@ import { useSyncExternalStore } from 'react' const useSyncLocalStorageSubscribers = {} const useSyncSessionStorageSubscribers = {} +const useSyncGlobalVariableSubscribers = {} + +const globalVariable = {} export const useSyncLocalStorage = (saveDirectory = 'global') => { if (!useSyncLocalStorageSubscribers[saveDirectory]) { @@ -76,5 +79,43 @@ export const useSyncSessionStorage = (saveDirectory = 'global') => { const state = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) + return [state ? JSON.parse(state) : undefined, setState] +} + +export const useSyncGlobalVariable = (saveDirectory = 'global') => { + if (!useSyncGlobalVariableSubscribers[saveDirectory]) { + useSyncGlobalVariableSubscribers[saveDirectory] = [] + } + + const subscribe = (callback) => { + useSyncGlobalVariableSubscribers[saveDirectory] = [...useSyncGlobalVariableSubscribers[saveDirectory], callback] + return () => { + useSyncGlobalVariableSubscribers[saveDirectory] = useSyncGlobalVariableSubscribers[saveDirectory].filter( + (el) => el !== callback + ) + } + } + + const getSnapshot = () => { + return JSON.stringify(globalVariable[saveDirectory]) + } + + const getServerSnapshot = () => { + return true + } + + const emitChange = () => { + for (let subscriber of useSyncGlobalVariableSubscribers[saveDirectory]) { + subscriber() + } + } + + const setState = (newState) => { + globalVariable[saveDirectory] = newState + emitChange() + } + + const state = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) + return [state ? JSON.parse(state) : undefined, setState] } \ No newline at end of file diff --git a/client/src/pages/contactList/index.js b/client/src/pages/contactList/index.js index 60ff35d..53e55f3 100644 --- a/client/src/pages/contactList/index.js +++ b/client/src/pages/contactList/index.js @@ -3,469 +3,469 @@ import { useEffect, useState } from 'react' const defaultState = { - contacts:[ - { - doc_id:'somerandomid', - first_name:'zirst', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'scond', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'ads', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'adsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'zirst', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'scond', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'bds', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'cdsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'zirst', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'dcond', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'eds', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'edsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'zirst', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'fcond', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'gds', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'adsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'adsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'adsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'adsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'adsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'adsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'adsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'adsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'adsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'adsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'adsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'zirst', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'scond', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'%ds', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'#dsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'zirst', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'scond', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'%ds', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'#dsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'zirst', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'scond', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'%ds', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'#dsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'zirst', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'scond', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'%ds', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'#dsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'zirst', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'scond', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'%ds', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'#dsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - - ] + contacts:[ + { + doc_id:'somerandomid', + first_name:'zirst', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'scond', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'ads', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'adsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'zirst', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'scond', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'bds', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'cdsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'zirst', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'dcond', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'eds', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'edsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'zirst', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'fcond', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'gds', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'adsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'adsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'adsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'adsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'adsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'adsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'adsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'adsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'adsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'adsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'adsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'zirst', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'scond', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'%ds', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'#dsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'zirst', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'scond', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'%ds', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'#dsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'zirst', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'scond', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'%ds', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'#dsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'zirst', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'scond', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'%ds', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'#dsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'zirst', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'scond', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'%ds', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + { + doc_id:'somerandomid', + first_name:'#dsad', + last_name:'third', + middle_name:'second', + date_created:new Date(), + date_updated:new Date(), + contact_no:'somerandomnumber', + contact_email:'somerandomemail' + }, + + ] } function ContactList () { diff --git a/client/src/pages/test.js b/client/src/pages/test.js.example similarity index 100% rename from client/src/pages/test.js rename to client/src/pages/test.js.example From 801f6e581aa8db47ee07faef6200d7e2253d2798 Mon Sep 17 00:00:00 2001 From: vikyw89 Date: Thu, 16 Mar 2023 23:47:12 +0800 Subject: [PATCH 10/81] progress: added searchFilter and search result --- .../common/searchContactContainer/index.js | 24 ++- client/src/components/contactList/index.js | 190 ++++++++++-------- client/src/lib/hooks/useSync.js | 2 +- client/src/pages/contactList/index.js | 2 + 4 files changed, 126 insertions(+), 92 deletions(-) diff --git a/client/src/common/searchContactContainer/index.js b/client/src/common/searchContactContainer/index.js index a60fc7a..ef0a857 100644 --- a/client/src/common/searchContactContainer/index.js +++ b/client/src/common/searchContactContainer/index.js @@ -1,9 +1,23 @@ -import { Box } from '@mui/material' +import { useSyncGlobalVariable } from '@/lib/hooks/useSync' +import { Box, TextField } from '@mui/material' +import SearchIcon from '@mui/icons-material/Search'; export const SearchContactContainer = () => { - return ( - + const [search, setSearch] = useSyncGlobalVariable('search') - - ) + const searchFieldHandler = (e) => { + setSearch(e.target.value) + } + + return ( + + + + + ) } \ No newline at end of file diff --git a/client/src/components/contactList/index.js b/client/src/components/contactList/index.js index 4473862..9e07546 100644 --- a/client/src/components/contactList/index.js +++ b/client/src/components/contactList/index.js @@ -16,100 +16,118 @@ import { ContactCard } from '@/common/contactsCardContainer/contactCard' import { Box, TextField } from '@mui/material' import { ContactCardsContainer } from '@/common/contactsCardContainer' import { TextFields } from '@mui/icons-material' +import { SearchContactContainer } from '@/common/searchContactContainer' +import { useSyncGlobalVariable } from '@/lib/hooks/useSync' function ContactListComponent({ state, eventsHandler }) { - const theme = useTheme() - // divide state.contacts into each container - const sortedContacts = [...state.contacts].sort((a, b) => a.first_name < b.first_name ? -1 : 1) + const theme = useTheme() + const [search, setSearch] = useSyncGlobalVariable('search') - const groupedSortedContacts = [...sortedContacts].reduce((acc, curr) => { - const capitalizedFirstNameFirstLetterChar = curr.first_name.match(new RegExp( - String.raw`(?^[a-z])|`, 'i'))[0] - if (!capitalizedFirstNameFirstLetterChar) { - if (!acc.misc) acc.misc = [] - acc.misc = [...acc.misc, curr] - } else { - if (!acc[capitalizedFirstNameFirstLetterChar.toUpperCase()]) { - acc[capitalizedFirstNameFirstLetterChar.toUpperCase()] = [] - } - acc[capitalizedFirstNameFirstLetterChar.toUpperCase()] = [ - ...acc[capitalizedFirstNameFirstLetterChar.toUpperCase()], - curr - ] + // divide state.contacts into each container + const sortedContacts = [...state.contacts].sort((a, b) => a.first_name < b.first_name ? -1 : 1) + + const groupedSortedContacts = [...sortedContacts].reduce((acc, curr) => { + const capitalizedFirstNameFirstLetterChar = curr.first_name.match(new RegExp( + String.raw`(?^[a-z])|`, 'i'))[0] + if (!capitalizedFirstNameFirstLetterChar) { + if (!acc.misc) acc.misc = [] + acc.misc = [...acc.misc, curr] + } else { + if (!acc[capitalizedFirstNameFirstLetterChar.toUpperCase()]) { + acc[capitalizedFirstNameFirstLetterChar.toUpperCase()] = [] + } + acc[capitalizedFirstNameFirstLetterChar.toUpperCase()] = [ + ...acc[capitalizedFirstNameFirstLetterChar.toUpperCase()], + curr + ] + } + return acc + }, {}) + + const groupedSortedContactsArr = Object.entries(groupedSortedContacts) + + const filterContacts = (searchText) => { + } - return acc - }, {}) - const groupedSortedContactsArr = Object.entries(groupedSortedContacts) - console.log(groupedSortedContacts) - return ( - - - - - - - - {/* this will show a profile if either only 1 result appear after search, or if user click a profile on the right */} - + return ( + + + + + + + + {/* this will show a profile if either only 1 result appear after search, or if user click a profile on the right */} + View Profile - - - - {/* related setting to print or export pdf */} - + + + + {/* related setting to print or export pdf */} + Settings - - - - - - {groupedSortedContactsArr.map((el, index) => { - return ( - - ) - })} - - - - - ) + + + + + {search + ? + + search results + + : + + {groupedSortedContactsArr.map((el, index) => { + return ( + + ) + })} + + } + + + + ) } ContactListComponent.propTypes = { - state: PropTypes.object, - eventsHandler: PropTypes.func + state: PropTypes.object, + eventsHandler: PropTypes.func } export default ContactListComponent diff --git a/client/src/lib/hooks/useSync.js b/client/src/lib/hooks/useSync.js index f42776b..5a1e609 100644 --- a/client/src/lib/hooks/useSync.js +++ b/client/src/lib/hooks/useSync.js @@ -101,7 +101,7 @@ export const useSyncGlobalVariable = (saveDirectory = 'global') => { } const getServerSnapshot = () => { - return true + return } const emitChange = () => { diff --git a/client/src/pages/contactList/index.js b/client/src/pages/contactList/index.js index 53e55f3..ee2548e 100644 --- a/client/src/pages/contactList/index.js +++ b/client/src/pages/contactList/index.js @@ -1,4 +1,5 @@ import ContactListComponent from '@/components/contactList' +import { useSyncGlobalVariable } from '@/lib/hooks/useSync' import { useEffect, useState } from 'react' @@ -470,6 +471,7 @@ const defaultState = { function ContactList () { const [state, setState] = useState(defaultState) + const eventsHandler = () => { } From 1edc6c110f3125ad19ab0add407d604213616c54 Mon Sep 17 00:00:00 2001 From: vikyw89 Date: Fri, 17 Mar 2023 00:35:40 +0800 Subject: [PATCH 11/81] progress: search results --- client/src/components/contactList/index.js | 53 ++++++++++++++++------ 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/client/src/components/contactList/index.js b/client/src/components/contactList/index.js index 9e07546..8751852 100644 --- a/client/src/components/contactList/index.js +++ b/client/src/components/contactList/index.js @@ -25,30 +25,53 @@ function ContactListComponent({ state, eventsHandler }) { // divide state.contacts into each container const sortedContacts = [...state.contacts].sort((a, b) => a.first_name < b.first_name ? -1 : 1) - - const groupedSortedContacts = [...sortedContacts].reduce((acc, curr) => { + const groupedSortedContacts = [...sortedContacts].reduce((prev, curr) => { const capitalizedFirstNameFirstLetterChar = curr.first_name.match(new RegExp( String.raw`(?^[a-z])|`, 'i'))[0] if (!capitalizedFirstNameFirstLetterChar) { - if (!acc.misc) acc.misc = [] - acc.misc = [...acc.misc, curr] + if (!prev.misc) prev.misc = [] + prev.misc = [...prev.misc, curr] } else { - if (!acc[capitalizedFirstNameFirstLetterChar.toUpperCase()]) { - acc[capitalizedFirstNameFirstLetterChar.toUpperCase()] = [] + if (!prev[capitalizedFirstNameFirstLetterChar.toUpperCase()]) { + prev[capitalizedFirstNameFirstLetterChar.toUpperCase()] = [] } - acc[capitalizedFirstNameFirstLetterChar.toUpperCase()] = [ - ...acc[capitalizedFirstNameFirstLetterChar.toUpperCase()], + prev[capitalizedFirstNameFirstLetterChar.toUpperCase()] = [ + ...prev[capitalizedFirstNameFirstLetterChar.toUpperCase()], curr ] } - return acc + return prev }, {}) const groupedSortedContactsArr = Object.entries(groupedSortedContacts) - + const filterContacts = (searchText) => { - + const filteredContactsByField = [...sortedContacts].reduce((prev, curr) => { + const matchingFirstName = new RegExp(String.raw`${searchText}`, 'i').test(curr.first_name) + const matchingMiddleName = new RegExp(String.raw`${searchText}`, 'i').test(curr.middle_name) + const matchingLastName = new RegExp(String.raw`${searchText}`, 'i').test(curr.last_name) + const matchingContactNo = new RegExp(String.raw`${searchText}`, 'i').test(curr.contact_no) + const matchingContactEmail = new RegExp(String.raw`${searchText}`, 'i').test(curr.contact_email) + + for (let [key, value] of Object.entries(curr)) { + // if (key !== 'first_name' || key !== 'middle_name' || key !== 'last_name' || key !== 'contact_no' || key !== 'contact_email') { + // } else { + console.log(key, value) + if (new RegExp(String.raw`${searchText}`, 'i').test(value)) { + if (!prev[key]) { + prev[key] = [] + } + prev[key] = [...prev[key], curr] + } + } + return prev + }, {}) + return filteredContactsByField } + + const searchResults = filterContacts(search) + const searchResultsArr = Object.entries(searchResults) + return ( {search - ? + ? - search results + {searchResultsArr.map((el, index) => { + return ( + + ) + })} : Date: Fri, 17 Mar 2023 00:43:31 +0800 Subject: [PATCH 12/81] progress: checkpoint before refactor --- client/src/components/contactList/index.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/client/src/components/contactList/index.js b/client/src/components/contactList/index.js index 8751852..d46c5f3 100644 --- a/client/src/components/contactList/index.js +++ b/client/src/components/contactList/index.js @@ -47,16 +47,7 @@ function ContactListComponent({ state, eventsHandler }) { const filterContacts = (searchText) => { const filteredContactsByField = [...sortedContacts].reduce((prev, curr) => { - const matchingFirstName = new RegExp(String.raw`${searchText}`, 'i').test(curr.first_name) - const matchingMiddleName = new RegExp(String.raw`${searchText}`, 'i').test(curr.middle_name) - const matchingLastName = new RegExp(String.raw`${searchText}`, 'i').test(curr.last_name) - const matchingContactNo = new RegExp(String.raw`${searchText}`, 'i').test(curr.contact_no) - const matchingContactEmail = new RegExp(String.raw`${searchText}`, 'i').test(curr.contact_email) - for (let [key, value] of Object.entries(curr)) { - // if (key !== 'first_name' || key !== 'middle_name' || key !== 'last_name' || key !== 'contact_no' || key !== 'contact_email') { - // } else { - console.log(key, value) if (new RegExp(String.raw`${searchText}`, 'i').test(value)) { if (!prev[key]) { prev[key] = [] From f7659d4df4fbdd55d33b56dca2413efd487fd552 Mon Sep 17 00:00:00 2001 From: vikyw89 Date: Fri, 17 Mar 2023 00:59:00 +0800 Subject: [PATCH 13/81] preogress: refactor --- .../contactCardGroup/index.js | 44 +++++++++++++ .../src/common/contactsCardContainer/index.js | 66 +++++++++---------- .../common/searchResultsContainer/index.js | 12 ++++ client/src/components/contactList/index.js | 42 +----------- 4 files changed, 90 insertions(+), 74 deletions(-) create mode 100644 client/src/common/contactsCardContainer/contactCardGroup/index.js create mode 100644 client/src/common/searchResultsContainer/index.js diff --git a/client/src/common/contactsCardContainer/contactCardGroup/index.js b/client/src/common/contactsCardContainer/contactCardGroup/index.js new file mode 100644 index 0000000..654efbf --- /dev/null +++ b/client/src/common/contactsCardContainer/contactCardGroup/index.js @@ -0,0 +1,44 @@ +import { Avatar, Box, Paper, Typography } from '@mui/material' +import { useState } from 'react' +import { ContactCard } from '../contactCard' + + +export const ContactCardsGroup = ({ content }) => { + const { group, contacts } = content + return ( + + + {group === 'misc' ? '' : group} + + + {contacts.map((el, index) => { + return ( + + ) + })} + + + ) +} \ No newline at end of file diff --git a/client/src/common/contactsCardContainer/index.js b/client/src/common/contactsCardContainer/index.js index 2ff6131..d15c557 100644 --- a/client/src/common/contactsCardContainer/index.js +++ b/client/src/common/contactsCardContainer/index.js @@ -1,44 +1,40 @@ import { Avatar, Box, Paper, Typography } from '@mui/material' import { useState } from 'react' import { ContactCard } from './contactCard' +import { ContactCardsGroup } from './contactCardGroup' +export const ContactCardsContainer = ({ state }) => { + const sortedContacts = [...state.contacts].sort((a, b) => a.first_name < b.first_name ? -1 : 1) + const groupedSortedContacts = [...sortedContacts].reduce((prev, curr) => { + const capitalizedFirstNameFirstLetterChar = curr.first_name.match(new RegExp( + String.raw`(?^[a-z])|`, 'i'))[0] + if (!capitalizedFirstNameFirstLetterChar) { + if (!prev.misc) prev.misc = [] + prev.misc = [...prev.misc, curr] + } else { + if (!prev[capitalizedFirstNameFirstLetterChar.toUpperCase()]) { + prev[capitalizedFirstNameFirstLetterChar.toUpperCase()] = [] + } + prev[capitalizedFirstNameFirstLetterChar.toUpperCase()] = [ + ...prev[capitalizedFirstNameFirstLetterChar.toUpperCase()], + curr + ] + } + return prev + }, {}) -export const ContactCardsContainer = ({ content }) => { - const { group, contacts } = content + const groupedSortedContactsArr = Object.entries(groupedSortedContacts) return ( - - - {group === 'misc' ? '' : group} - - - {contacts.map((el, index) => { - return ( - - ) - })} - + + {groupedSortedContactsArr.map((el, index) => { + return ( + + ) + })} + ) } \ No newline at end of file diff --git a/client/src/common/searchResultsContainer/index.js b/client/src/common/searchResultsContainer/index.js new file mode 100644 index 0000000..123af1f --- /dev/null +++ b/client/src/common/searchResultsContainer/index.js @@ -0,0 +1,12 @@ +const { Box } = require("@mui/material") + +export const SearchResultsContainer = () => { + return ( + + + + ) +} \ No newline at end of file diff --git a/client/src/components/contactList/index.js b/client/src/components/contactList/index.js index d46c5f3..ae747dd 100644 --- a/client/src/components/contactList/index.js +++ b/client/src/components/contactList/index.js @@ -18,6 +18,7 @@ import { ContactCardsContainer } from '@/common/contactsCardContainer' import { TextFields } from '@mui/icons-material' import { SearchContactContainer } from '@/common/searchContactContainer' import { useSyncGlobalVariable } from '@/lib/hooks/useSync' +import { SearchResultsContainer } from '@/common/searchResultsContainer' function ContactListComponent({ state, eventsHandler }) { const theme = useTheme() @@ -25,25 +26,6 @@ function ContactListComponent({ state, eventsHandler }) { // divide state.contacts into each container const sortedContacts = [...state.contacts].sort((a, b) => a.first_name < b.first_name ? -1 : 1) - const groupedSortedContacts = [...sortedContacts].reduce((prev, curr) => { - const capitalizedFirstNameFirstLetterChar = curr.first_name.match(new RegExp( - String.raw`(?^[a-z])|`, 'i'))[0] - if (!capitalizedFirstNameFirstLetterChar) { - if (!prev.misc) prev.misc = [] - prev.misc = [...prev.misc, curr] - } else { - if (!prev[capitalizedFirstNameFirstLetterChar.toUpperCase()]) { - prev[capitalizedFirstNameFirstLetterChar.toUpperCase()] = [] - } - prev[capitalizedFirstNameFirstLetterChar.toUpperCase()] = [ - ...prev[capitalizedFirstNameFirstLetterChar.toUpperCase()], - curr - ] - } - return prev - }, {}) - - const groupedSortedContactsArr = Object.entries(groupedSortedContacts) const filterContacts = (searchText) => { const filteredContactsByField = [...sortedContacts].reduce((prev, curr) => { @@ -116,27 +98,9 @@ function ContactListComponent({ state, eventsHandler }) { }}> {search ? - - {searchResultsArr.map((el, index) => { - return ( - - ) - })} - + : - - {groupedSortedContactsArr.map((el, index) => { - return ( - - ) - })} - + } From c43d59ff035c8971f9461e7c4824072ee682cfac Mon Sep 17 00:00:00 2001 From: vikyw89 Date: Fri, 17 Mar 2023 01:12:37 +0800 Subject: [PATCH 14/81] refactor: progress, slow speed rerender --- .../common/searchResultsContainer/index.js | 30 +++++++++++++++++-- client/src/components/contactList/index.js | 23 +------------- client/src/pages/contactList/index.js | 8 ++++- 3 files changed, 36 insertions(+), 25 deletions(-) diff --git a/client/src/common/searchResultsContainer/index.js b/client/src/common/searchResultsContainer/index.js index 123af1f..857c107 100644 --- a/client/src/common/searchResultsContainer/index.js +++ b/client/src/common/searchResultsContainer/index.js @@ -1,12 +1,38 @@ +import { useSyncGlobalVariable } from "@/lib/hooks/useSync" +import { ContactCardsGroup } from "../contactsCardContainer/contactCardGroup" + const { Box } = require("@mui/material") -export const SearchResultsContainer = () => { +export const SearchResultsContainer = ({ state }) => { + const [search, setSearch] = useSyncGlobalVariable('search') + + const filterContacts = (searchText) => { + const filteredContactsByField = [...state.contacts].reduce((prev, curr) => { + for (let [key, value] of Object.entries(curr)) { + if (new RegExp(String.raw`${searchText}`, 'i').test(value)) { + if (!prev[key]) { + prev[key] = [] + } + prev[key] = [...prev[key], curr] + } + } + return prev + }, {}) + return filteredContactsByField + } + + const searchResults = filterContacts(search) + const searchResultsArr = Object.entries(searchResults) return ( - + {searchResultsArr.map((el, index) => { + return ( + + ) + })} ) } \ No newline at end of file diff --git a/client/src/components/contactList/index.js b/client/src/components/contactList/index.js index ae747dd..82c1076 100644 --- a/client/src/components/contactList/index.js +++ b/client/src/components/contactList/index.js @@ -24,27 +24,6 @@ function ContactListComponent({ state, eventsHandler }) { const theme = useTheme() const [search, setSearch] = useSyncGlobalVariable('search') - // divide state.contacts into each container - const sortedContacts = [...state.contacts].sort((a, b) => a.first_name < b.first_name ? -1 : 1) - - const filterContacts = (searchText) => { - const filteredContactsByField = [...sortedContacts].reduce((prev, curr) => { - for (let [key, value] of Object.entries(curr)) { - if (new RegExp(String.raw`${searchText}`, 'i').test(value)) { - if (!prev[key]) { - prev[key] = [] - } - prev[key] = [...prev[key], curr] - } - } - return prev - }, {}) - return filteredContactsByField - } - - const searchResults = filterContacts(search) - const searchResultsArr = Object.entries(searchResults) - return ( {search ? - + : } diff --git a/client/src/pages/contactList/index.js b/client/src/pages/contactList/index.js index ee2548e..aae33bd 100644 --- a/client/src/pages/contactList/index.js +++ b/client/src/pages/contactList/index.js @@ -471,7 +471,13 @@ const defaultState = { function ContactList () { const [state, setState] = useState(defaultState) - + const sortedContacts = [...state.contacts].sort((a, b) => a.first_name < b.first_name ? -1 : 1) + useState(()=>{ + setState(prev=>({ + ...prev, + contacts:sortedContacts + })) + },[]) const eventsHandler = () => { } From 0fafd63cd9ee16dc3275584f203fd5cace4aed50 Mon Sep 17 00:00:00 2001 From: vikyw89 Date: Fri, 17 Mar 2023 11:51:53 +0800 Subject: [PATCH 15/81] fix: lag issue using useDeferredValue --- .../contactCard/index.js | 38 +++++++++---------- .../common/searchResultsContainer/index.js | 19 +++++++--- client/src/components/contactList/index.js | 5 ++- client/src/lib/hooks/useSync.js | 6 +-- 4 files changed, 38 insertions(+), 30 deletions(-) diff --git a/client/src/common/contactsCardContainer/contactCard/index.js b/client/src/common/contactsCardContainer/contactCard/index.js index 07d6de2..c989513 100644 --- a/client/src/common/contactsCardContainer/contactCard/index.js +++ b/client/src/common/contactsCardContainer/contactCard/index.js @@ -1,27 +1,27 @@ import { Avatar, Paper, Typography, useTheme } from '@mui/material' import { useState } from 'react' -export const ContactCard = ({ contact }) => { - const theme = useTheme() - - const backgroundColorGenerator = (name) => { - let value = 0 - for (let i = 0; i < name.length; i++) { - value = value + name.charCodeAt(i) - } - value = value % 255 - return value +const getInitial = (first, second, last) => { + const firstInitial = (first.match(new RegExp( + String.raw`(?^[a-z])`, 'i')) ?? [''])[0] + const middleInitial = (second.match(new RegExp( + String.raw`(?^[a-z])`, 'i')) ?? [''])[0] + const lastInitial = (last.match(new RegExp( + String.raw`(?^[a-z])`, 'i')) ?? [''])[0] + return `${firstInitial}${middleInitial}${lastInitial}` +} +const backgroundColorGenerator = (name) => { + let value = 0 + for (let i = 0; i < name.length; i++) { + value = value + name.charCodeAt(i) } + value = value % 255 + return value +} - const getInitial = (first, second, last) => { - const firstInitial = (first.match(new RegExp( - String.raw`(?^[a-z])`, 'i')) ?? [''])[0] - const middleInitial = (second.match(new RegExp( - String.raw`(?^[a-z])`, 'i')) ?? [''])[0] - const lastInitial = (second.match(new RegExp( - String.raw`(?^[a-z])`, 'i')) ?? [''])[0] - return `${firstInitial}${middleInitial}${lastInitial}` - } + +export const ContactCard = ({ contact }) => { + const theme = useTheme() const initial = getInitial(contact.first_name, contact.middle_name, contact.last_name).toUpperCase() diff --git a/client/src/common/searchResultsContainer/index.js b/client/src/common/searchResultsContainer/index.js index 857c107..469c352 100644 --- a/client/src/common/searchResultsContainer/index.js +++ b/client/src/common/searchResultsContainer/index.js @@ -1,10 +1,12 @@ import { useSyncGlobalVariable } from "@/lib/hooks/useSync" +import { useDeferredValue, useEffect, useState } from "react" import { ContactCardsGroup } from "../contactsCardContainer/contactCardGroup" const { Box } = require("@mui/material") export const SearchResultsContainer = ({ state }) => { const [search, setSearch] = useSyncGlobalVariable('search') + const deferredSearch = useDeferredValue(search) const filterContacts = (searchText) => { const filteredContactsByField = [...state.contacts].reduce((prev, curr) => { @@ -21,18 +23,23 @@ export const SearchResultsContainer = ({ state }) => { return filteredContactsByField } - const searchResults = filterContacts(search) + const searchResults = filterContacts(deferredSearch) const searchResultsArr = Object.entries(searchResults) + + return ( - {searchResultsArr.map((el, index) => { - return ( - - ) - })} + { + searchResultsArr.map((el, index) => { + return ( + //
{el[0]}
+ + ) + }) + }
) } \ No newline at end of file diff --git a/client/src/components/contactList/index.js b/client/src/components/contactList/index.js index 82c1076..f5649fd 100644 --- a/client/src/components/contactList/index.js +++ b/client/src/components/contactList/index.js @@ -19,11 +19,12 @@ import { TextFields } from '@mui/icons-material' import { SearchContactContainer } from '@/common/searchContactContainer' import { useSyncGlobalVariable } from '@/lib/hooks/useSync' import { SearchResultsContainer } from '@/common/searchResultsContainer' +import { useDeferredValue } from 'react' function ContactListComponent({ state, eventsHandler }) { const theme = useTheme() const [search, setSearch] = useSyncGlobalVariable('search') - + const deferredSearch = useDeferredValue(search) return ( - {search + {deferredSearch ? : diff --git a/client/src/lib/hooks/useSync.js b/client/src/lib/hooks/useSync.js index 5a1e609..30d08ef 100644 --- a/client/src/lib/hooks/useSync.js +++ b/client/src/lib/hooks/useSync.js @@ -97,7 +97,7 @@ export const useSyncGlobalVariable = (saveDirectory = 'global') => { } const getSnapshot = () => { - return JSON.stringify(globalVariable[saveDirectory]) + return globalVariable[saveDirectory] } const getServerSnapshot = () => { @@ -111,11 +111,11 @@ export const useSyncGlobalVariable = (saveDirectory = 'global') => { } const setState = (newState) => { - globalVariable[saveDirectory] = newState + globalVariable[saveDirectory] = JSON.stringify(newState) emitChange() } const state = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) - return [state ? JSON.parse(state) : undefined, setState] + return [state ? JSON.parse(state) : undefined, setState, globalVariable] } \ No newline at end of file From ea56642b88c881ba87f9b0dad544b8e3375aad9d Mon Sep 17 00:00:00 2001 From: vikyw89 Date: Fri, 17 Mar 2023 12:34:22 +0800 Subject: [PATCH 16/81] feature: added opacity on deferred value --- .../common/searchResultsContainer/index.js | 28 ++++++++--- .../searchResultsGroup/index.js | 50 +++++++++++++++++++ client/src/components/contactList/index.js | 1 - 3 files changed, 70 insertions(+), 9 deletions(-) create mode 100644 client/src/common/searchResultsContainer/searchResultsGroup/index.js diff --git a/client/src/common/searchResultsContainer/index.js b/client/src/common/searchResultsContainer/index.js index 469c352..4e6a41f 100644 --- a/client/src/common/searchResultsContainer/index.js +++ b/client/src/common/searchResultsContainer/index.js @@ -1,6 +1,8 @@ import { useSyncGlobalVariable } from "@/lib/hooks/useSync" import { useDeferredValue, useEffect, useState } from "react" import { ContactCardsGroup } from "../contactsCardContainer/contactCardGroup" +import Typography from '@mui/material/Typography' +import { SearchResultsGroup } from "./searchResultsGroup" const { Box } = require("@mui/material") @@ -32,14 +34,24 @@ export const SearchResultsContainer = ({ state }) => { width: '100%', height: '100%', }}> - { - searchResultsArr.map((el, index) => { - return ( - //
{el[0]}
- - ) - }) - } + + Search result(s): + + + { + searchResultsArr.map((el, index) => { + return ( + + ) + }) + } +
) } \ No newline at end of file diff --git a/client/src/common/searchResultsContainer/searchResultsGroup/index.js b/client/src/common/searchResultsContainer/searchResultsGroup/index.js new file mode 100644 index 0000000..af5705c --- /dev/null +++ b/client/src/common/searchResultsContainer/searchResultsGroup/index.js @@ -0,0 +1,50 @@ +import { ContactCard } from '@/common/contactsCardContainer/contactCard' +import { Avatar, Box, Paper, Typography } from '@mui/material' +import { useState } from 'react' + +const groupNameMapping = { + first_name: 'Matching First Name', + middle_name: 'Matching Middle Name', + last_name: 'Matching Last Name', + contact_no: 'Matching Contact Number', + contact_email: 'Matching Contact Email' +} + +export const SearchResultsGroup = ({ content }) => { + const { group, contacts } = content + if (!groupNameMapping[group]) { + return + } + return ( + + + {groupNameMapping[group]} + + + {contacts.map((el, index) => { + return ( + + ) + })} + + + ) +} \ No newline at end of file diff --git a/client/src/components/contactList/index.js b/client/src/components/contactList/index.js index f5649fd..d344005 100644 --- a/client/src/components/contactList/index.js +++ b/client/src/components/contactList/index.js @@ -65,7 +65,6 @@ function ContactListComponent({ state, eventsHandler }) { sx={{ flex: 6.8, minWidth: '500px', - border: '2px solid black', maxHeight: '100%', width: '100%', height: '100%', From 3e941715c1909add204bf5d0641d6b8738619d53 Mon Sep 17 00:00:00 2001 From: vikyw89 Date: Fri, 17 Mar 2023 13:19:57 +0800 Subject: [PATCH 17/81] progress: on embeded viewContact page --- .../contactCard/index.js | 45 +++++++++----- .../common/searchResultsContainer/index.js | 12 +++- .../searchResultsGroup/index.js | 1 + client/src/components/contactList/index.js | 62 ++++++++++++++++--- 4 files changed, 93 insertions(+), 27 deletions(-) diff --git a/client/src/common/contactsCardContainer/contactCard/index.js b/client/src/common/contactsCardContainer/contactCard/index.js index c989513..a42602e 100644 --- a/client/src/common/contactsCardContainer/contactCard/index.js +++ b/client/src/common/contactsCardContainer/contactCard/index.js @@ -1,3 +1,4 @@ +import { useSyncGlobalVariable } from '@/lib/hooks/useSync' import { Avatar, Paper, Typography, useTheme } from '@mui/material' import { useState } from 'react' @@ -22,31 +23,41 @@ const backgroundColorGenerator = (name) => { export const ContactCard = ({ contact }) => { const theme = useTheme() + const [viewContact, setViewContact] = useSyncGlobalVariable('viewContact') const initial = getInitial(contact.first_name, contact.middle_name, contact.last_name).toUpperCase() const backgroundColor = backgroundColorGenerator(initial) + + const contactClickHandler = (e) => { + e.stopPropagation() + setViewContact(contact) + } return ( - + {initial} diff --git a/client/src/common/searchResultsContainer/index.js b/client/src/common/searchResultsContainer/index.js index 4e6a41f..fe5bdf1 100644 --- a/client/src/common/searchResultsContainer/index.js +++ b/client/src/common/searchResultsContainer/index.js @@ -8,6 +8,8 @@ const { Box } = require("@mui/material") export const SearchResultsContainer = ({ state }) => { const [search, setSearch] = useSyncGlobalVariable('search') + const [viewContact, setViewContact] = useSyncGlobalVariable('viewContact') + const deferredSearch = useDeferredValue(search) const filterContacts = (searchText) => { @@ -27,7 +29,15 @@ export const SearchResultsContainer = ({ state }) => { const searchResults = filterContacts(deferredSearch) const searchResultsArr = Object.entries(searchResults) - + + if (searchResultsArr.length > 1) { + setViewContact() + } else if (searchResultsArr.length === 1) { + for (let [key, value] of Object.entries(searchResults)) { + if (value.length !== 1) break + setViewContact(value[0]) + } + } return ( @@ -37,23 +38,66 @@ function ContactListComponent({ state, eventsHandler }) { elevation={10} sx={{ flex: 3.14, + display: 'flex', + flexDirection: 'column', minWidth: '200px', zIndex: '50', backgroundColor: 'inherit', padding: '20px', overflow: 'hidden', width: '100%', + gap: '30px' }} > - - {/* this will show a profile if either only 1 result appear after search, or if user click a profile on the right */} - - View Profile - - + {viewContact && + + {/* this will show a profile if either only 1 result appear after search, or if user click a profile on the right */} + + Contact Detail : + + + + + + First Name : {viewContact.first_name} + + + Middle Name: + {viewContact.middle_name} + + + Last Name: + {viewContact.last_name} + + + Contact Number: + {viewContact.contact_no} + + + Contact Email: + {viewContact.contact_email} + + + + } {/* related setting to print or export pdf */} @@ -77,9 +121,9 @@ function ContactListComponent({ state, eventsHandler }) { }}> {deferredSearch ? - + : - + } From 76d10e3bd0b958e035ec74e7c720b36cbb489ee5 Mon Sep 17 00:00:00 2001 From: vikyw89 Date: Fri, 17 Mar 2023 13:50:20 +0800 Subject: [PATCH 18/81] adjustment: view on mobile --- .../contactCardGroup/index.js | 5 ++- .../common/searchContactContainer/index.js | 3 +- .../common/searchResultsContainer/index.js | 21 +++++----- client/src/components/contactList/index.js | 42 ++++++++++--------- 4 files changed, 38 insertions(+), 33 deletions(-) diff --git a/client/src/common/contactsCardContainer/contactCardGroup/index.js b/client/src/common/contactsCardContainer/contactCardGroup/index.js index 654efbf..41838c4 100644 --- a/client/src/common/contactsCardContainer/contactCardGroup/index.js +++ b/client/src/common/contactsCardContainer/contactCardGroup/index.js @@ -9,8 +9,9 @@ export const ContactCardsGroup = ({ content }) => { diff --git a/client/src/common/searchContactContainer/index.js b/client/src/common/searchContactContainer/index.js index ef0a857..bf556ff 100644 --- a/client/src/common/searchContactContainer/index.js +++ b/client/src/common/searchContactContainer/index.js @@ -10,10 +10,11 @@ export const SearchContactContainer = () => { } return ( - + { const searchResults = filterContacts(deferredSearch) const searchResultsArr = Object.entries(searchResults) - - if (searchResultsArr.length > 1) { - setViewContact() - } else if (searchResultsArr.length === 1) { - for (let [key, value] of Object.entries(searchResults)) { - if (value.length !== 1) break - setViewContact(value[0]) + useEffect(()=>{ + if (searchResultsArr.length === 1) { + for (let [key, value] of Object.entries(searchResults)) { + if (value.length !== 1) break + setViewContact(value[0]) + } + } else { + setViewContact() } - } + },[deferredSearch]) return ( - Search result(s): - +
*/} diff --git a/client/src/components/contactList/index.js b/client/src/components/contactList/index.js index 46016a4..733e4d2 100644 --- a/client/src/components/contactList/index.js +++ b/client/src/components/contactList/index.js @@ -24,7 +24,8 @@ import { useDeferredValue } from 'react' function ContactListComponent({ state, eventsHandler }) { const theme = useTheme() const [search, setSearch] = useSyncGlobalVariable('search') - const [viewContact, setViewContact] = useSyncGlobalVariable('viewContact') + const [viewContact, setViewContact, status] = useSyncGlobalVariable('viewContact') + console.log(status) const deferredSearch = useDeferredValue(search) return ( @@ -46,7 +47,7 @@ function ContactListComponent({ state, eventsHandler }) { padding: '20px', overflow: 'hidden', width: '100%', - gap: '30px' + gap: '20px' }} > @@ -67,43 +68,44 @@ function ContactListComponent({ state, eventsHandler }) { Contact Detail :
- First Name : {viewContact.first_name} + First Name : - - Middle Name: - {viewContact.middle_name} + + + Middle Name : - + + Last Name: - {viewContact.last_name} - + + Contact Number: - {viewContact.contact_no} - + + Contact Email: - {viewContact.contact_email} +
} - - {/* related setting to print or export pdf */} - - Settings - - Date: Fri, 17 Mar 2023 14:14:57 +0800 Subject: [PATCH 19/81] progress: revisit later --- .../contactCardGroup/index.js | 6 +-- client/src/components/contactList/index.js | 50 +++++++++++-------- 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/client/src/common/contactsCardContainer/contactCardGroup/index.js b/client/src/common/contactsCardContainer/contactCardGroup/index.js index 41838c4..38bb245 100644 --- a/client/src/common/contactsCardContainer/contactCardGroup/index.js +++ b/client/src/common/contactsCardContainer/contactCardGroup/index.js @@ -11,14 +11,14 @@ export const ContactCardsGroup = ({ content }) => { display: 'grid', gridTemplateColumns: '40px 1fr', pt: '10px', - pb:'10px', - borderBottom:'1px dashed grey' + pb: '10px', + borderBottom: '1px dashed grey' }} > {/* this will show a profile if either only 1 result appear after search, or if user click a profile on the right */} - - Contact Detail : - - - First Name : - - - + + + First Name : + + + + + Middle Name : - - + + Last Name: - - + + Contact Number: - - + + Contact Email: - + } From 1ec806bf31213cc7f25a4f49804116e1df8abb23 Mon Sep 17 00:00:00 2001 From: vikyw89 Date: Fri, 17 Mar 2023 14:25:48 +0800 Subject: [PATCH 20/81] fix: hide footer on mobile --- client/src/common/layout/footer/styles.js | 1 + client/src/components/contactList/index.js | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/common/layout/footer/styles.js b/client/src/common/layout/footer/styles.js index 6a268cf..49ea3ab 100644 --- a/client/src/common/layout/footer/styles.js +++ b/client/src/common/layout/footer/styles.js @@ -1,5 +1,6 @@ const styles = { footer: { + display: { xs: 'none' , md:'block'}, width: '100%', minHeight: (theme) => theme.spacing(4), padding: (theme) => theme.spacing(2), diff --git a/client/src/components/contactList/index.js b/client/src/components/contactList/index.js index d65bac2..a5938b8 100644 --- a/client/src/components/contactList/index.js +++ b/client/src/components/contactList/index.js @@ -58,7 +58,6 @@ function ContactListComponent({ state, eventsHandler }) { sx={{ display: 'grid', gap: '10px', - overflow:'hidden' }} > {/* this will show a profile if either only 1 result appear after search, or if user click a profile on the right */} @@ -70,7 +69,7 @@ function ContactListComponent({ state, eventsHandler }) { maxWidth: '100%', border: '1px solid black', padding: '20px', - overflowY:'scroll' + }}> Date: Fri, 17 Mar 2023 16:17:00 +0800 Subject: [PATCH 21/81] adjustment: small repositioning of elements --- .../common/searchContactContainer/index.js | 24 ---- .../contactCard/index.js | 58 +++++++-- .../contactCardGroup/index.js | 3 +- .../contactsCardContainer/index.js | 0 .../contactList/contactListDisplay/index.js | 34 +++++ .../searchResultsContainer/index.js | 7 -- .../searchResultsGroup/index.js | 3 +- .../contactList/contactListSidebar/index.js | 101 +++++++++++++++ client/src/components/contactList/index.js | 119 ++---------------- 9 files changed, 201 insertions(+), 148 deletions(-) delete mode 100644 client/src/common/searchContactContainer/index.js rename client/src/{common => components/contactList/contactListDisplay}/contactsCardContainer/contactCard/index.js (60%) rename client/src/{common => components/contactList/contactListDisplay}/contactsCardContainer/contactCardGroup/index.js (96%) rename client/src/{common => components/contactList/contactListDisplay}/contactsCardContainer/index.js (100%) create mode 100644 client/src/components/contactList/contactListDisplay/index.js rename client/src/{common => components/contactList/contactListDisplay}/searchResultsContainer/index.js (91%) rename client/src/{common => components/contactList/contactListDisplay}/searchResultsContainer/searchResultsGroup/index.js (95%) create mode 100644 client/src/components/contactList/contactListSidebar/index.js diff --git a/client/src/common/searchContactContainer/index.js b/client/src/common/searchContactContainer/index.js deleted file mode 100644 index bf556ff..0000000 --- a/client/src/common/searchContactContainer/index.js +++ /dev/null @@ -1,24 +0,0 @@ -import { useSyncGlobalVariable } from '@/lib/hooks/useSync' -import { Box, TextField } from '@mui/material' -import SearchIcon from '@mui/icons-material/Search'; - -export const SearchContactContainer = () => { - const [search, setSearch] = useSyncGlobalVariable('search') - - const searchFieldHandler = (e) => { - setSearch(e.target.value) - } - - return ( - - - - - ) -} \ No newline at end of file diff --git a/client/src/common/contactsCardContainer/contactCard/index.js b/client/src/components/contactList/contactListDisplay/contactsCardContainer/contactCard/index.js similarity index 60% rename from client/src/common/contactsCardContainer/contactCard/index.js rename to client/src/components/contactList/contactListDisplay/contactsCardContainer/contactCard/index.js index a42602e..407f570 100644 --- a/client/src/common/contactsCardContainer/contactCard/index.js +++ b/client/src/components/contactList/contactListDisplay/contactsCardContainer/contactCard/index.js @@ -1,5 +1,5 @@ import { useSyncGlobalVariable } from '@/lib/hooks/useSync' -import { Avatar, Paper, Typography, useTheme } from '@mui/material' +import { Avatar, Divider, Paper, Typography, useTheme } from '@mui/material' import { useState } from 'react' const getInitial = (first, second, last) => { @@ -34,12 +34,13 @@ export const ContactCard = ({ contact }) => { setViewContact(contact) } return ( + <> { alignItems: 'center', borderRadius: '10px', width: 'auto', - maxWidth: '70vw', - border: '1px solid grey', + maxWidth: '80vw', + // border: '1px solid grey', '&:hover': { backdropFilter: 'contrast(120%)' }, }} - onClick={contactClickHandler}> + onClick={contactClickHandler} + > { { {contact.first_name} {contact.middle_name} {contact.last_name} + + + {initial} + + + + {contact.first_name} {contact.middle_name} {contact.last_name} + + + ) } \ No newline at end of file diff --git a/client/src/common/contactsCardContainer/contactCardGroup/index.js b/client/src/components/contactList/contactListDisplay/contactsCardContainer/contactCardGroup/index.js similarity index 96% rename from client/src/common/contactsCardContainer/contactCardGroup/index.js rename to client/src/components/contactList/contactListDisplay/contactsCardContainer/contactCardGroup/index.js index 38bb245..8ff9836 100644 --- a/client/src/common/contactsCardContainer/contactCardGroup/index.js +++ b/client/src/components/contactList/contactListDisplay/contactsCardContainer/contactCardGroup/index.js @@ -10,8 +10,7 @@ export const ContactCardsGroup = ({ content }) => { sx={{ display: 'grid', gridTemplateColumns: '40px 1fr', - pt: '10px', - pb: '10px', + p: '25px', borderBottom: '1px dashed grey' }} > diff --git a/client/src/common/contactsCardContainer/index.js b/client/src/components/contactList/contactListDisplay/contactsCardContainer/index.js similarity index 100% rename from client/src/common/contactsCardContainer/index.js rename to client/src/components/contactList/contactListDisplay/contactsCardContainer/index.js diff --git a/client/src/components/contactList/contactListDisplay/index.js b/client/src/components/contactList/contactListDisplay/index.js new file mode 100644 index 0000000..0186835 --- /dev/null +++ b/client/src/components/contactList/contactListDisplay/index.js @@ -0,0 +1,34 @@ +import { useSyncGlobalVariable } from "@/lib/hooks/useSync" +import { Box } from "@mui/material" +import { useDeferredValue } from "react" +import { ContactCardsContainer } from "./contactsCardContainer" +import { SearchResultsContainer } from "./searchResultsContainer" + +export const ContactListDisplay = ({state}) => { + const [search, setSearch] = useSyncGlobalVariable('search') + const deferredSearch = useDeferredValue(search) + return ( + <> + + {deferredSearch + ? + + : + + } + + + ) +} \ No newline at end of file diff --git a/client/src/common/searchResultsContainer/index.js b/client/src/components/contactList/contactListDisplay/searchResultsContainer/index.js similarity index 91% rename from client/src/common/searchResultsContainer/index.js rename to client/src/components/contactList/contactListDisplay/searchResultsContainer/index.js index c7d50fe..437cc19 100644 --- a/client/src/common/searchResultsContainer/index.js +++ b/client/src/components/contactList/contactListDisplay/searchResultsContainer/index.js @@ -45,13 +45,6 @@ export const SearchResultsContainer = ({ state }) => { width: '100%', height: '100%', }}> - {/* - Search result(s): - */} diff --git a/client/src/common/searchResultsContainer/searchResultsGroup/index.js b/client/src/components/contactList/contactListDisplay/searchResultsContainer/searchResultsGroup/index.js similarity index 95% rename from client/src/common/searchResultsContainer/searchResultsGroup/index.js rename to client/src/components/contactList/contactListDisplay/searchResultsContainer/searchResultsGroup/index.js index 0be5d5f..3e62780 100644 --- a/client/src/common/searchResultsContainer/searchResultsGroup/index.js +++ b/client/src/components/contactList/contactListDisplay/searchResultsContainer/searchResultsGroup/index.js @@ -1,7 +1,8 @@ -import { ContactCard } from '@/common/contactsCardContainer/contactCard' + import { useSyncGlobalVariable } from '@/lib/hooks/useSync' import { Avatar, Box, Paper, Typography } from '@mui/material' import { useState } from 'react' +import { ContactCard } from '../../contactsCardContainer/contactCard' const groupNameMapping = { first_name: 'Matching First Name', diff --git a/client/src/components/contactList/contactListSidebar/index.js b/client/src/components/contactList/contactListSidebar/index.js new file mode 100644 index 0000000..66815e0 --- /dev/null +++ b/client/src/components/contactList/contactListSidebar/index.js @@ -0,0 +1,101 @@ +import { useSyncGlobalVariable } from '@/lib/hooks/useSync' +import { Avatar, Box, Paper, TextField, Typography } from '@mui/material' +import SearchIcon from '@mui/icons-material/Search'; + +export const ContactListSidebar = () => { + const [search, setSearch] = useSyncGlobalVariable('search') + const [viewContact, setViewContact] = useSyncGlobalVariable('viewContact') + const searchFieldHandler = (e) => { + setSearch(e.target.value) + } + + return ( + + + + + + {viewContact && + + {/* this will show a profile if either only 1 result appear after search, or if user click a profile on the right */} + + + + + + First Name : + + + + + + Middle Name : + + + + Last Name: + + + + Contact Number: + + + + Contact Email: + + + + + } + + ) +} \ No newline at end of file diff --git a/client/src/components/contactList/index.js b/client/src/components/contactList/index.js index a5938b8..7ccbf03 100644 --- a/client/src/components/contactList/index.js +++ b/client/src/components/contactList/index.js @@ -12,21 +12,23 @@ import Paper from '@mui/material/Paper' import SimpleSnackbar from '@/common/snackbars/simpleSnackbar' import TransparentTextfield from '@/common/ui/transparentfield' import { useTheme } from '@emotion/react' -import { ContactCard } from '@/common/contactsCardContainer/contactCard' + import { Avatar, Box, TextField } from '@mui/material' -import { ContactCardsContainer } from '@/common/contactsCardContainer' + import { TextFields } from '@mui/icons-material' -import { SearchContactContainer } from '@/common/searchContactContainer' + import { useSyncGlobalVariable } from '@/lib/hooks/useSync' -import { SearchResultsContainer } from '@/common/searchResultsContainer' + import { useDeferredValue } from 'react' +import { SearchResultsContainer } from './contactListDisplay/searchResultsContainer' +import { ContactCardsContainer } from './contactListDisplay/contactsCardContainer' +import { ContactListSidebar } from './contactListSidebar' +import { ContactListDisplay } from './contactListDisplay' function ContactListComponent({ state, eventsHandler }) { const theme = useTheme() - const [search, setSearch] = useSyncGlobalVariable('search') const [viewContact, setViewContact, status] = useSyncGlobalVariable('viewContact') - console.log(status) - const deferredSearch = useDeferredValue(search) + return ( - - - - - {viewContact && - - {/* this will show a profile if either only 1 result appear after search, or if user click a profile on the right */} - - - - - - First Name : - - - - - - Middle Name : - - - - Last Name: - - - - Contact Number: - - - - Contact Email: - - - - - } - - - {deferredSearch - ? - - : - - } - + + ) } + ContactListComponent.propTypes = { state: PropTypes.object, eventsHandler: PropTypes.func From 21d57f36a480475f6c4e65950cf22721b8dd289c Mon Sep 17 00:00:00 2001 From: vikyw89 Date: Fri, 17 Mar 2023 16:45:47 +0800 Subject: [PATCH 22/81] feature: scroll window into viewContact when itcha --- .../contactList/contactListSidebar/index.js | 88 +++---------------- .../searchField/searchField.js | 23 +++++ .../viewContact.js/index.js | 73 +++++++++++++++ client/src/components/contactList/index.js | 3 +- 4 files changed, 108 insertions(+), 79 deletions(-) create mode 100644 client/src/components/contactList/contactListSidebar/searchField/searchField.js create mode 100644 client/src/components/contactList/contactListSidebar/viewContact.js/index.js diff --git a/client/src/components/contactList/contactListSidebar/index.js b/client/src/components/contactList/contactListSidebar/index.js index 66815e0..72a932e 100644 --- a/client/src/components/contactList/contactListSidebar/index.js +++ b/client/src/components/contactList/contactListSidebar/index.js @@ -1,13 +1,11 @@ import { useSyncGlobalVariable } from '@/lib/hooks/useSync' import { Avatar, Box, Paper, TextField, Typography } from '@mui/material' -import SearchIcon from '@mui/icons-material/Search'; +import { SearchField } from './searchField/searchField.js'; + +import { ViewContact } from './viewContact.js'; export const ContactListSidebar = () => { - const [search, setSearch] = useSyncGlobalVariable('search') - const [viewContact, setViewContact] = useSyncGlobalVariable('viewContact') - const searchFieldHandler = (e) => { - setSearch(e.target.value) - } + return ( { backgroundColor: 'inherit', padding: '20px', paddingBottom:'0', - // overflow: 'hidden', width: '100%', gap: '20px', + overflowY: 'scroll', + userSelect: 'none', + '&::-webkit-scrollbar': { + display: 'none', + }, }} > - - - - - {viewContact && - - {/* this will show a profile if either only 1 result appear after search, or if user click a profile on the right */} - - - - - - First Name : - - - - - - Middle Name : - - - - Last Name: - - - - Contact Number: - - - - Contact Email: - - - - - } + + ) } \ No newline at end of file diff --git a/client/src/components/contactList/contactListSidebar/searchField/searchField.js b/client/src/components/contactList/contactListSidebar/searchField/searchField.js new file mode 100644 index 0000000..7ee513f --- /dev/null +++ b/client/src/components/contactList/contactListSidebar/searchField/searchField.js @@ -0,0 +1,23 @@ +import { useSyncGlobalVariable } from "@/lib/hooks/useSync" +import { Box, TextField } from "@mui/material" +import SearchIcon from '@mui/icons-material/Search'; + +export const SearchField = () => { + const [search, setSearch] = useSyncGlobalVariable('search') + const searchFieldHandler = (e) => { + setSearch(e.target.value) + } + + return ( + + + + + ) +} \ No newline at end of file diff --git a/client/src/components/contactList/contactListSidebar/viewContact.js/index.js b/client/src/components/contactList/contactListSidebar/viewContact.js/index.js new file mode 100644 index 0000000..423a82b --- /dev/null +++ b/client/src/components/contactList/contactListSidebar/viewContact.js/index.js @@ -0,0 +1,73 @@ +import { useSyncGlobalVariable } from "@/lib/hooks/useSync" +import { Avatar, Box, TextField, Typography } from "@mui/material" +import { useRef } from "react" + +export const ViewContact = () => { + const [viewContact, setViewContact] = useSyncGlobalVariable('viewContact') + const focusElement = useRef() + if (!viewContact) { + return + } + + focusElement?.current?.scrollIntoView({behaviour:'smooth'}) + return ( + + {/* this will show a profile if either only 1 result appear after search, or if user click a profile on the right */} + + + + + + First Name : + + + + + + Middle Name : + + + + Last Name: + + + + Contact Number: + + + + Contact Email: + + + + + ) +} \ No newline at end of file diff --git a/client/src/components/contactList/index.js b/client/src/components/contactList/index.js index 7ccbf03..2fa7024 100644 --- a/client/src/components/contactList/index.js +++ b/client/src/components/contactList/index.js @@ -27,7 +27,6 @@ import { ContactListDisplay } from './contactListDisplay' function ContactListComponent({ state, eventsHandler }) { const theme = useTheme() - const [viewContact, setViewContact, status] = useSyncGlobalVariable('viewContact') return ( @@ -35,7 +34,7 @@ function ContactListComponent({ state, eventsHandler }) { flex: 1, display: 'flex', flexWrap: 'wrap', - overflow: 'hidden', + // overflow: 'hidden', }}> From 1720f74a5973af28c7bfa1363b2828cd79a4c332 Mon Sep 17 00:00:00 2001 From: vikyw89 Date: Fri, 17 Mar 2023 17:00:32 +0800 Subject: [PATCH 23/81] chore: controlled field on viewContact --- .../viewContact.js/index.js | 85 ++++++++++++++----- 1 file changed, 62 insertions(+), 23 deletions(-) diff --git a/client/src/components/contactList/contactListSidebar/viewContact.js/index.js b/client/src/components/contactList/contactListSidebar/viewContact.js/index.js index 423a82b..c632b7b 100644 --- a/client/src/components/contactList/contactListSidebar/viewContact.js/index.js +++ b/client/src/components/contactList/contactListSidebar/viewContact.js/index.js @@ -1,15 +1,26 @@ import { useSyncGlobalVariable } from "@/lib/hooks/useSync" -import { Avatar, Box, TextField, Typography } from "@mui/material" -import { useRef } from "react" +import { Avatar, Box, Button, TextField, Typography } from "@mui/material" +import { useRef, useState } from "react" export const ViewContact = () => { const [viewContact, setViewContact] = useSyncGlobalVariable('viewContact') + const [state, setState] = useState({isUpdated:false}) + const focusElement = useRef() if (!viewContact) { return } - - focusElement?.current?.scrollIntoView({behaviour:'smooth'}) + + focusElement?.current?.scrollIntoView({ behaviour: 'smooth' }) + + const editContactHandler = (e) => { + const fieldID = e.target.id + const fieldValue = e.target.value + setViewContact({ + ...viewContact, + [fieldID]:fieldValue + }) + } return ( { alignItems: 'center', gap: '10px' }}> - + First Name : - - + + + + + Middle Name : + + + + + + Last Name: + + + + + + Contact Number: + + + + + + Contact Email: + + - - Middle Name : - - - - Last Name: - - - - Contact Number: - - - - Contact Email: - - +
) From 797c0c68c36eafb99650471c4459cf48b475ee07 Mon Sep 17 00:00:00 2001 From: vikyw89 Date: Fri, 17 Mar 2023 18:06:19 +0800 Subject: [PATCH 24/81] adjusted: searchField icon when focus vs not --- client/src/common/layout/header/index.js | 44 ++--- .../contactCard/index.js | 154 +++++++++--------- .../contactList/contactListSidebar/index.js | 4 +- .../searchField/searchField.js | 36 ++-- .../viewContact.js/index.js | 47 +++--- client/src/components/contactList/index.js | 9 +- 6 files changed, 161 insertions(+), 133 deletions(-) diff --git a/client/src/common/layout/header/index.js b/client/src/common/layout/header/index.js index e448318..df0df72 100644 --- a/client/src/common/layout/header/index.js +++ b/client/src/common/layout/header/index.js @@ -79,15 +79,17 @@ function Header() { router.push(route) } } - const {themeHandler, handleOpenNavMenu, handleOpenUserMenu, handleCloseNavMenu, handleCloseUserMenu, handleClickNavMenu} = eventsHandler + const { themeHandler, handleOpenNavMenu, handleOpenUserMenu, handleCloseNavMenu, handleCloseUserMenu, handleClickNavMenu } = eventsHandler return ( + }} + id="appBar" + > @@ -102,14 +104,14 @@ function Header() { fontWeight: 700, letterSpacing: '.1rem', textDecoration: 'none', - color: (theme)=>theme.palette.text.primary, + color: (theme) => theme.palette.text.primary, }} > myPhonebook - + - + theme.palette.text.primary, + color: (theme) => theme.palette.text.primary, }} > myPhonebook @@ -164,10 +166,10 @@ function Header() { {pages.map((page) => ( - + @@ -223,7 +225,7 @@ function Header() { display: { xs: 'none', md: 'flex' }, }} > - theme.palette.text.primary, }}> + theme.palette.text.primary, }}> Login @@ -236,7 +238,7 @@ function Header() { display: { xs: 'none', md: 'flex' }, }} > - theme.palette.text.primary, }}> + theme.palette.text.primary, }}> Register @@ -248,17 +250,17 @@ function Header() { sx={{ color: 'black', display: { xs: 'flex', md: 'none' }, - justifyContent:'center', - alignItems:'center', + justifyContent: 'center', + alignItems: 'center', }} > {activeTheme === 'dark' ? + }} /> : - + } @@ -267,15 +269,15 @@ function Header() { sx={{ color: 'black', display: { xs: 'flex', md: 'none' }, - justifyContent:'center', - alignItems:'center', + justifyContent: 'center', + alignItems: 'center', }} > {activeTheme === 'dark' ? + }} /> : } @@ -284,13 +286,13 @@ function Header() { {activeTheme === 'dark' ? - + : } diff --git a/client/src/components/contactList/contactListDisplay/contactsCardContainer/contactCard/index.js b/client/src/components/contactList/contactListDisplay/contactsCardContainer/contactCard/index.js index 407f570..11b07ef 100644 --- a/client/src/components/contactList/contactListDisplay/contactsCardContainer/contactCard/index.js +++ b/client/src/components/contactList/contactListDisplay/contactsCardContainer/contactCard/index.js @@ -32,92 +32,96 @@ export const ContactCard = ({ contact }) => { const contactClickHandler = (e) => { e.stopPropagation() setViewContact(contact) + // window.scrollTo({ + // top: 0, + // behavior: 'smooth' // for smoothly scrolling + // }); } return ( <> - - - {initial} - + + {initial} + - + {contact.first_name} {contact.middle_name} {contact.last_name} + + + - {contact.first_name} {contact.middle_name} {contact.last_name} - - - - - {initial} - + + {initial} + - - {contact.first_name} {contact.middle_name} {contact.last_name} - - + + {contact.first_name} {contact.middle_name} {contact.last_name} + + ) } \ No newline at end of file diff --git a/client/src/components/contactList/contactListSidebar/index.js b/client/src/components/contactList/contactListSidebar/index.js index 72a932e..8bb1737 100644 --- a/client/src/components/contactList/contactListSidebar/index.js +++ b/client/src/components/contactList/contactListSidebar/index.js @@ -28,8 +28,8 @@ export const ContactListSidebar = () => { }, }} > - - + + ) } \ No newline at end of file diff --git a/client/src/components/contactList/contactListSidebar/searchField/searchField.js b/client/src/components/contactList/contactListSidebar/searchField/searchField.js index 7ee513f..a714e12 100644 --- a/client/src/components/contactList/contactListSidebar/searchField/searchField.js +++ b/client/src/components/contactList/contactListSidebar/searchField/searchField.js @@ -1,23 +1,37 @@ import { useSyncGlobalVariable } from "@/lib/hooks/useSync" import { Box, TextField } from "@mui/material" import SearchIcon from '@mui/icons-material/Search'; +import { useState } from "react"; export const SearchField = () => { const [search, setSearch] = useSyncGlobalVariable('search') + const [state, setState] = useState({ focused: false }) const searchFieldHandler = (e) => { setSearch(e.target.value) } - + + const focusHandler = () => { + setState(prev => ({ + ...prev, + focused: !prev.focused + })) + } return ( - - - - + + { + !state.focused && + + } + + ) } \ No newline at end of file diff --git a/client/src/components/contactList/contactListSidebar/viewContact.js/index.js b/client/src/components/contactList/contactListSidebar/viewContact.js/index.js index c632b7b..f1cca98 100644 --- a/client/src/components/contactList/contactListSidebar/viewContact.js/index.js +++ b/client/src/components/contactList/contactListSidebar/viewContact.js/index.js @@ -1,31 +1,32 @@ import { useSyncGlobalVariable } from "@/lib/hooks/useSync" +import { useTheme } from "@emotion/react" import { Avatar, Box, Button, TextField, Typography } from "@mui/material" import { useRef, useState } from "react" export const ViewContact = () => { + const theme = useTheme() const [viewContact, setViewContact] = useSyncGlobalVariable('viewContact') - const [state, setState] = useState({isUpdated:false}) + const [state, setState] = useState({ isUpdated: false }) - const focusElement = useRef() if (!viewContact) { return } - focusElement?.current?.scrollIntoView({ behaviour: 'smooth' }) - const editContactHandler = (e) => { const fieldID = e.target.id const fieldValue = e.target.value setViewContact({ ...viewContact, - [fieldID]:fieldValue + [fieldID]: fieldValue }) } return ( - {/* this will show a profile if either only 1 result appear after search, or if user click a profile on the right */} @@ -34,11 +35,8 @@ export const ViewContact = () => { flexDirection: 'column', gap: '10px', alignItems: 'center', - maxWidth: '100%', - border: '1px solid black', - padding: '20px', - - + width: '100%', + padding: '30px', }}> { height: '50vw', justifySelf: 'center', gridColumn: '1/-1', - border: '1px dashed grey' + border: '5px dashed gray' }}> First Name : - + Middle Name : - + Last Name: - + Contact Number: - + Contact Email: - + diff --git a/client/src/components/contactList/index.js b/client/src/components/contactList/index.js index 2fa7024..b7b8f93 100644 --- a/client/src/components/contactList/index.js +++ b/client/src/components/contactList/index.js @@ -19,7 +19,7 @@ import { TextFields } from '@mui/icons-material' import { useSyncGlobalVariable } from '@/lib/hooks/useSync' -import { useDeferredValue } from 'react' +import { useDeferredValue, useRef } from 'react' import { SearchResultsContainer } from './contactListDisplay/searchResultsContainer' import { ContactCardsContainer } from './contactListDisplay/contactsCardContainer' import { ContactListSidebar } from './contactListSidebar' @@ -35,8 +35,13 @@ function ContactListComponent({ state, eventsHandler }) { display: 'flex', flexWrap: 'wrap', // overflow: 'hidden', + overflowY: 'scroll', + userSelect: 'none', + '&::-webkit-scrollbar': { + display: 'none', + }, }}> - + From 5eba389564b21ffd86824b6284bc5d53881daf31 Mon Sep 17 00:00:00 2001 From: vikyw89 Date: Fri, 17 Mar 2023 18:57:45 +0800 Subject: [PATCH 25/81] fix: lint --- client/src/common/layout/header/index.js | 2 +- client/src/common/layout/section/index.js | 1 - .../contactCard/index.js | 5 +- .../contactCardGroup/index.js | 3 +- .../contactsCardContainer/index.js | 32 ++- .../contactList/contactListDisplay/index.js | 62 +++--- .../searchResultsContainer/index.js | 102 +++++---- .../searchResultsGroup/index.js | 86 ++++---- .../contactList/contactListSidebar/index.js | 59 +++-- .../searchField/searchField.js | 64 +++--- .../viewContact.js/index.js | 206 +++++++++--------- client/src/components/contactList/index.js | 67 ++---- client/src/pages/contactList/index.js | 4 +- 13 files changed, 329 insertions(+), 364 deletions(-) diff --git a/client/src/common/layout/header/index.js b/client/src/common/layout/header/index.js index df0df72..a20a95e 100644 --- a/client/src/common/layout/header/index.js +++ b/client/src/common/layout/header/index.js @@ -88,7 +88,7 @@ function Header() { background: 'inherit', backdropFilter: 'blur(5px)', }} - id="appBar" + id="appBar" > diff --git a/client/src/common/layout/section/index.js b/client/src/common/layout/section/index.js index d74c271..812c814 100644 --- a/client/src/common/layout/section/index.js +++ b/client/src/common/layout/section/index.js @@ -1,6 +1,5 @@ import styles from './styles' import Box from '@mui/material/Box' -import { Container } from '@mui/material' function Section ({ children }) { return ( diff --git a/client/src/components/contactList/contactListDisplay/contactsCardContainer/contactCard/index.js b/client/src/components/contactList/contactListDisplay/contactsCardContainer/contactCard/index.js index 11b07ef..b48efd7 100644 --- a/client/src/components/contactList/contactListDisplay/contactsCardContainer/contactCard/index.js +++ b/client/src/components/contactList/contactListDisplay/contactsCardContainer/contactCard/index.js @@ -1,6 +1,5 @@ import { useSyncGlobalVariable } from '@/lib/hooks/useSync' -import { Avatar, Divider, Paper, Typography, useTheme } from '@mui/material' -import { useState } from 'react' +import { Avatar, Paper, Typography, useTheme } from '@mui/material' const getInitial = (first, second, last) => { const firstInitial = (first.match(new RegExp( @@ -23,7 +22,7 @@ const backgroundColorGenerator = (name) => { export const ContactCard = ({ contact }) => { const theme = useTheme() - const [viewContact, setViewContact] = useSyncGlobalVariable('viewContact') + const [, setViewContact] = useSyncGlobalVariable('viewContact') const initial = getInitial(contact.first_name, contact.middle_name, contact.last_name).toUpperCase() diff --git a/client/src/components/contactList/contactListDisplay/contactsCardContainer/contactCardGroup/index.js b/client/src/components/contactList/contactListDisplay/contactsCardContainer/contactCardGroup/index.js index 8ff9836..a891161 100644 --- a/client/src/components/contactList/contactListDisplay/contactsCardContainer/contactCardGroup/index.js +++ b/client/src/components/contactList/contactListDisplay/contactsCardContainer/contactCardGroup/index.js @@ -1,5 +1,4 @@ -import { Avatar, Box, Paper, Typography } from '@mui/material' -import { useState } from 'react' +import { Box, Typography } from '@mui/material' import { ContactCard } from '../contactCard' diff --git a/client/src/components/contactList/contactListDisplay/contactsCardContainer/index.js b/client/src/components/contactList/contactListDisplay/contactsCardContainer/index.js index d15c557..c3f0f60 100644 --- a/client/src/components/contactList/contactListDisplay/contactsCardContainer/index.js +++ b/client/src/components/contactList/contactListDisplay/contactsCardContainer/index.js @@ -1,26 +1,24 @@ -import { Avatar, Box, Paper, Typography } from '@mui/material' -import { useState } from 'react' -import { ContactCard } from './contactCard' +import { Box } from '@mui/material' import { ContactCardsGroup } from './contactCardGroup' export const ContactCardsContainer = ({ state }) => { const sortedContacts = [...state.contacts].sort((a, b) => a.first_name < b.first_name ? -1 : 1) const groupedSortedContacts = [...sortedContacts].reduce((prev, curr) => { - const capitalizedFirstNameFirstLetterChar = curr.first_name.match(new RegExp( - String.raw`(?^[a-z])|`, 'i'))[0] - if (!capitalizedFirstNameFirstLetterChar) { - if (!prev.misc) prev.misc = [] - prev.misc = [...prev.misc, curr] - } else { - if (!prev[capitalizedFirstNameFirstLetterChar.toUpperCase()]) { - prev[capitalizedFirstNameFirstLetterChar.toUpperCase()] = [] - } - prev[capitalizedFirstNameFirstLetterChar.toUpperCase()] = [ - ...prev[capitalizedFirstNameFirstLetterChar.toUpperCase()], - curr - ] + const capitalizedFirstNameFirstLetterChar = curr.first_name.match(new RegExp( + String.raw`(?^[a-z])|`, 'i'))[0] + if (!capitalizedFirstNameFirstLetterChar) { + if (!prev.misc) prev.misc = [] + prev.misc = [...prev.misc, curr] + } else { + if (!prev[capitalizedFirstNameFirstLetterChar.toUpperCase()]) { + prev[capitalizedFirstNameFirstLetterChar.toUpperCase()] = [] } - return prev + prev[capitalizedFirstNameFirstLetterChar.toUpperCase()] = [ + ...prev[capitalizedFirstNameFirstLetterChar.toUpperCase()], + curr + ] + } + return prev }, {}) const groupedSortedContactsArr = Object.entries(groupedSortedContacts) diff --git a/client/src/components/contactList/contactListDisplay/index.js b/client/src/components/contactList/contactListDisplay/index.js index 0186835..5c01ffe 100644 --- a/client/src/components/contactList/contactListDisplay/index.js +++ b/client/src/components/contactList/contactListDisplay/index.js @@ -1,34 +1,34 @@ -import { useSyncGlobalVariable } from "@/lib/hooks/useSync" -import { Box } from "@mui/material" -import { useDeferredValue } from "react" -import { ContactCardsContainer } from "./contactsCardContainer" -import { SearchResultsContainer } from "./searchResultsContainer" +import { useSyncGlobalVariable } from '@/lib/hooks/useSync' +import { Box } from '@mui/material' +import { useDeferredValue } from 'react' +import { ContactCardsContainer } from './contactsCardContainer' +import { SearchResultsContainer } from './searchResultsContainer' export const ContactListDisplay = ({state}) => { - const [search, setSearch] = useSyncGlobalVariable('search') - const deferredSearch = useDeferredValue(search) - return ( - <> - - {deferredSearch - ? - - : - - } - - - ) + const [search] = useSyncGlobalVariable('search') + const deferredSearch = useDeferredValue(search) + return ( + <> + + {deferredSearch + ? + + : + + } + + + ) } \ No newline at end of file diff --git a/client/src/components/contactList/contactListDisplay/searchResultsContainer/index.js b/client/src/components/contactList/contactListDisplay/searchResultsContainer/index.js index 437cc19..f8a15d2 100644 --- a/client/src/components/contactList/contactListDisplay/searchResultsContainer/index.js +++ b/client/src/components/contactList/contactListDisplay/searchResultsContainer/index.js @@ -1,61 +1,59 @@ -import { useSyncGlobalVariable } from "@/lib/hooks/useSync" -import { useDeferredValue, useEffect, useState } from "react" -import { ContactCardsGroup } from "../contactsCardContainer/contactCardGroup" -import Typography from '@mui/material/Typography' -import { SearchResultsGroup } from "./searchResultsGroup" +import { useSyncGlobalVariable } from '@/lib/hooks/useSync' +import { useDeferredValue, useEffect} from 'react' +import { SearchResultsGroup } from './searchResultsGroup' -const { Box } = require("@mui/material") +const { Box } = require('@mui/material') export const SearchResultsContainer = ({ state }) => { - const [search, setSearch] = useSyncGlobalVariable('search') - const [viewContact, setViewContact] = useSyncGlobalVariable('viewContact') + const [search] = useSyncGlobalVariable('search') + const [, setViewContact] = useSyncGlobalVariable('viewContact') - const deferredSearch = useDeferredValue(search) + const deferredSearch = useDeferredValue(search) - const filterContacts = (searchText) => { - const filteredContactsByField = [...state.contacts].reduce((prev, curr) => { - for (let [key, value] of Object.entries(curr)) { - if (new RegExp(String.raw`${searchText}`, 'i').test(value)) { - if (!prev[key]) { - prev[key] = [] - } - prev[key] = [...prev[key], curr] - } - } - return prev - }, {}) - return filteredContactsByField + const filterContacts = (searchText) => { + const filteredContactsByField = [...state.contacts].reduce((prev, curr) => { + for (let [key, value] of Object.entries(curr)) { + if (new RegExp(String.raw`${searchText}`, 'i').test(value)) { + if (!prev[key]) { + prev[key] = [] + } + prev[key] = [...prev[key], curr] + } + } + return prev + }, {}) + return filteredContactsByField + } + + const searchResults = filterContacts(deferredSearch) + const searchResultsArr = Object.entries(searchResults) + useEffect(()=>{ + if (searchResultsArr.length === 1) { + for (let [, value] of Object.entries(searchResults)) { + if (value.length !== 1) break + setViewContact(value[0]) + } + } else { + setViewContact() } + },[searchResults, searchResultsArr, setViewContact, deferredSearch]) - const searchResults = filterContacts(deferredSearch) - const searchResultsArr = Object.entries(searchResults) - useEffect(()=>{ - if (searchResultsArr.length === 1) { - for (let [key, value] of Object.entries(searchResults)) { - if (value.length !== 1) break - setViewContact(value[0]) - } - } else { - setViewContact() + return ( + + + { + searchResultsArr.map((el, index) => { + return ( + + ) + }) } - },[deferredSearch]) - - return ( - - - { - searchResultsArr.map((el, index) => { - return ( - - ) - }) - } - - - ) + + + ) } \ No newline at end of file diff --git a/client/src/components/contactList/contactListDisplay/searchResultsContainer/searchResultsGroup/index.js b/client/src/components/contactList/contactListDisplay/searchResultsContainer/searchResultsGroup/index.js index 3e62780..8fce544 100644 --- a/client/src/components/contactList/contactListDisplay/searchResultsContainer/searchResultsGroup/index.js +++ b/client/src/components/contactList/contactListDisplay/searchResultsContainer/searchResultsGroup/index.js @@ -1,52 +1,50 @@ -import { useSyncGlobalVariable } from '@/lib/hooks/useSync' -import { Avatar, Box, Paper, Typography } from '@mui/material' -import { useState } from 'react' +import { Box, Typography } from '@mui/material' import { ContactCard } from '../../contactsCardContainer/contactCard' const groupNameMapping = { - first_name: 'Matching First Name', - middle_name: 'Matching Middle Name', - last_name: 'Matching Last Name', - contact_no: 'Matching Contact Number', - contact_email: 'Matching Contact Email' + first_name: 'Matching First Name', + middle_name: 'Matching Middle Name', + last_name: 'Matching Last Name', + contact_no: 'Matching Contact Number', + contact_email: 'Matching Contact Email' } export const SearchResultsGroup = ({ content }) => { - const { group, contacts } = content - if (!groupNameMapping[group]) { - return - } - return ( - - - {groupNameMapping[group]} - - - {contacts.map((el, index) => { - return ( - - ) - })} - - - ) + const { group, contacts } = content + if (!groupNameMapping[group]) { + return + } + return ( + + + {groupNameMapping[group]} + + + {contacts.map((el, index) => { + return ( + + ) + })} + + + ) } \ No newline at end of file diff --git a/client/src/components/contactList/contactListSidebar/index.js b/client/src/components/contactList/contactListSidebar/index.js index 8bb1737..f49e087 100644 --- a/client/src/components/contactList/contactListSidebar/index.js +++ b/client/src/components/contactList/contactListSidebar/index.js @@ -1,35 +1,32 @@ -import { useSyncGlobalVariable } from '@/lib/hooks/useSync' -import { Avatar, Box, Paper, TextField, Typography } from '@mui/material' -import { SearchField } from './searchField/searchField.js'; +import { Paper } from '@mui/material' +import { SearchField } from './searchField/searchField.js' -import { ViewContact } from './viewContact.js'; +import { ViewContact } from './viewContact.js' export const ContactListSidebar = () => { - - - return ( - - - - - ) + return ( + + + + + ) } \ No newline at end of file diff --git a/client/src/components/contactList/contactListSidebar/searchField/searchField.js b/client/src/components/contactList/contactListSidebar/searchField/searchField.js index a714e12..f547055 100644 --- a/client/src/components/contactList/contactListSidebar/searchField/searchField.js +++ b/client/src/components/contactList/contactListSidebar/searchField/searchField.js @@ -1,37 +1,37 @@ -import { useSyncGlobalVariable } from "@/lib/hooks/useSync" -import { Box, TextField } from "@mui/material" -import SearchIcon from '@mui/icons-material/Search'; -import { useState } from "react"; +import { useSyncGlobalVariable } from '@/lib/hooks/useSync' +import { Box, TextField } from '@mui/material' +import SearchIcon from '@mui/icons-material/Search' +import { useState } from 'react' export const SearchField = () => { - const [search, setSearch] = useSyncGlobalVariable('search') - const [state, setState] = useState({ focused: false }) - const searchFieldHandler = (e) => { - setSearch(e.target.value) - } + const [search, setSearch] = useSyncGlobalVariable('search') + const [state, setState] = useState({ focused: false }) + const searchFieldHandler = (e) => { + setSearch(e.target.value) + } - const focusHandler = () => { - setState(prev => ({ - ...prev, - focused: !prev.focused - })) - } - return ( - - { - !state.focused && + const focusHandler = () => { + setState(prev => ({ + ...prev, + focused: !prev.focused + })) + } + return ( + + { + !state.focused && - } - - - ) + } + + + ) } \ No newline at end of file diff --git a/client/src/components/contactList/contactListSidebar/viewContact.js/index.js b/client/src/components/contactList/contactListSidebar/viewContact.js/index.js index f1cca98..55a089d 100644 --- a/client/src/components/contactList/contactListSidebar/viewContact.js/index.js +++ b/client/src/components/contactList/contactListSidebar/viewContact.js/index.js @@ -1,115 +1,113 @@ -import { useSyncGlobalVariable } from "@/lib/hooks/useSync" -import { useTheme } from "@emotion/react" -import { Avatar, Box, Button, TextField, Typography } from "@mui/material" -import { useRef, useState } from "react" +import { useSyncGlobalVariable } from '@/lib/hooks/useSync' +import { Avatar, Box, Button, TextField, Typography } from '@mui/material' +import { useState } from 'react' export const ViewContact = () => { - const theme = useTheme() - const [viewContact, setViewContact] = useSyncGlobalVariable('viewContact') - const [state, setState] = useState({ isUpdated: false }) + const [viewContact, setViewContact] = useSyncGlobalVariable('viewContact') + const [state] = useState({ isUpdated: false }) - if (!viewContact) { - return - } + if (!viewContact) { + return + } - const editContactHandler = (e) => { - const fieldID = e.target.id - const fieldValue = e.target.value - setViewContact({ - ...viewContact, - [fieldID]: fieldValue - }) - } - return ( - - {/* this will show a profile if either only 1 result appear after search, or if user click a profile on the right */} - - - - - + const editContactHandler = (e) => { + const fieldID = e.target.id + const fieldValue = e.target.value + setViewContact({ + ...viewContact, + [fieldID]: fieldValue + }) + } + return ( + + {/* this will show a profile if either only 1 result appear after search, or if user click a profile on the right */} + + + + + First Name : - - - - - + + + + + Middle Name : - - - - - + + + + + Last Name: - - - - - + + + + + Contact Number: - - - - - + + + + + Contact Email: - - - - - + + - ) + + + + ) } \ No newline at end of file diff --git a/client/src/components/contactList/index.js b/client/src/components/contactList/index.js index b7b8f93..ff9b6d7 100644 --- a/client/src/components/contactList/index.js +++ b/client/src/components/contactList/index.js @@ -1,56 +1,35 @@ import PropTypes from 'prop-types' - -// common import Page from '@/common/layout/page' - -// lib -import Typography from '@mui/material/Typography' -import Button from '@mui/material/Button' -import Link from 'next/link' -import CheckIcon from '@mui/icons-material/Check' -import Paper from '@mui/material/Paper' -import SimpleSnackbar from '@/common/snackbars/simpleSnackbar' -import TransparentTextfield from '@/common/ui/transparentfield' -import { useTheme } from '@emotion/react' - -import { Avatar, Box, TextField } from '@mui/material' - -import { TextFields } from '@mui/icons-material' - -import { useSyncGlobalVariable } from '@/lib/hooks/useSync' - -import { useDeferredValue, useRef } from 'react' -import { SearchResultsContainer } from './contactListDisplay/searchResultsContainer' -import { ContactCardsContainer } from './contactListDisplay/contactsCardContainer' +import { Box} from '@mui/material' import { ContactListSidebar } from './contactListSidebar' import { ContactListDisplay } from './contactListDisplay' function ContactListComponent({ state, eventsHandler }) { - const theme = useTheme() - - return ( - - - - - - - ) + return ( + + + + + + + + + ) } ContactListComponent.propTypes = { - state: PropTypes.object, - eventsHandler: PropTypes.func + state: PropTypes.object, + eventsHandler: PropTypes.func } export default ContactListComponent diff --git a/client/src/pages/contactList/index.js b/client/src/pages/contactList/index.js index aae33bd..11d89fb 100644 --- a/client/src/pages/contactList/index.js +++ b/client/src/pages/contactList/index.js @@ -1,6 +1,5 @@ import ContactListComponent from '@/components/contactList' -import { useSyncGlobalVariable } from '@/lib/hooks/useSync' -import { useEffect, useState } from 'react' +import { useState } from 'react' const defaultState = { @@ -472,6 +471,7 @@ const defaultState = { function ContactList () { const [state, setState] = useState(defaultState) const sortedContacts = [...state.contacts].sort((a, b) => a.first_name < b.first_name ? -1 : 1) + useState(()=>{ setState(prev=>({ ...prev, From 421d160d3002ebb720f638d83ba6e9c82eb86c3b Mon Sep 17 00:00:00 2001 From: vikyw89 Date: Fri, 17 Mar 2023 23:46:28 +0800 Subject: [PATCH 26/81] chore: added wrapper for firestore --- client/src/lib/hooks/useSync.js | 17 +- client/src/lib/utils/firebase/firestore.js | 107 +++ client/src/pages/contactList/index.js | 950 +++++++++++---------- 3 files changed, 597 insertions(+), 477 deletions(-) create mode 100644 client/src/lib/utils/firebase/firestore.js diff --git a/client/src/lib/hooks/useSync.js b/client/src/lib/hooks/useSync.js index 30d08ef..9322648 100644 --- a/client/src/lib/hooks/useSync.js +++ b/client/src/lib/hooks/useSync.js @@ -1,4 +1,5 @@ -import { useSyncExternalStore } from 'react' +import { useCallback, useSyncExternalStore } from 'react' +import { getRandomJoke } from '../services/random' const useSyncLocalStorageSubscribers = {} const useSyncSessionStorageSubscribers = {} @@ -83,18 +84,18 @@ export const useSyncSessionStorage = (saveDirectory = 'global') => { } export const useSyncGlobalVariable = (saveDirectory = 'global') => { - if (!useSyncGlobalVariableSubscribers[saveDirectory]) { - useSyncGlobalVariableSubscribers[saveDirectory] = [] - } - - const subscribe = (callback) => { + + const subscribe = useCallback((callback) => { + if (!useSyncGlobalVariableSubscribers[saveDirectory]) { + useSyncGlobalVariableSubscribers[saveDirectory] = [] + } useSyncGlobalVariableSubscribers[saveDirectory] = [...useSyncGlobalVariableSubscribers[saveDirectory], callback] return () => { useSyncGlobalVariableSubscribers[saveDirectory] = useSyncGlobalVariableSubscribers[saveDirectory].filter( (el) => el !== callback ) } - } + }) const getSnapshot = () => { return globalVariable[saveDirectory] @@ -118,4 +119,4 @@ export const useSyncGlobalVariable = (saveDirectory = 'global') => { const state = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) return [state ? JSON.parse(state) : undefined, setState, globalVariable] -} \ No newline at end of file +} diff --git a/client/src/lib/utils/firebase/firestore.js b/client/src/lib/utils/firebase/firestore.js new file mode 100644 index 0000000..a04686b --- /dev/null +++ b/client/src/lib/utils/firebase/firestore.js @@ -0,0 +1,107 @@ +import { app } from "./config" +import { collection, getDocs, getFirestore, query, updateDoc, addDoc, doc, deleteDoc, setDoc, getDoc, serverTimestamp, collectionGroup, onSnapshot } from "firebase/firestore"; + +export class FirebaseFirestore { + static db = getFirestore(app) + /** + * + * + * @static + * @param {*} path put a colRef path eg: collection + * @param {*} data put a data object eg: {data:'data'} + * @memberof FirebaseFirestore + */ + static createDoc = async (path, data) => { + const colRef = collection(this.db, path) + const docRef = doc(colRef) + const uid = docRef.id + const response = await setDoc(docRef, { + ...data, + uid, + date_created: serverTimestamp(), + date_updated: serverTimestamp() + }) + return response + } + /** + * + * + * @static + * @param {*} path docRef eg: /collection/ + * @memberof FirebaseFirestore + */ + static readDoc = async (path) => { + const docRef = doc(this.db, path) + const response = await getDoc(docRef) + const data = response.data() + return data + } +/** + * + * + * @static + * @param {*} path colRef eg: /collection + * @param {*} queryDef eg: (where('', '==','')) + * @memberof FirebaseFirestore + */ +static readCollection = async (path, queryDef) => { + const colRef = collection(this.db, path) + const q = query(colRef, queryDef) + const snapshot = await getDocs(q) + const data = snapshot.docs + .map((doc) => { + return { + ...doc.data() + } + }) + return data + } + + static readCollectionGroup = async (path, queryDef) => { + const colGroupRef = collectionGroup(this.db, path) + const q = query(colGroupRef, queryDef) + const snapshot = await getDocs(q) + const data = snapshot.docs + .map((doc) => { + return { + ...doc.data() + } + }) + return data + } + + static updateDoc = async (path, data) => { + const docRef = doc(this.db, path) + const response = await updateDoc(docRef, { + ...data, + date_updated: serverTimestamp() + }) + return response + } + + static deleteDoc = async (path) => { + const docRef = doc(this.db, path) + const response = await deleteDoc(docRef) + return response + } + + static subscribeDoc = async (path, callback) => { + const docRef = doc(this.db, path) + const unsubscribe = onSnapshot(docRef, callback) + return unsubscribe + } + + static subscribeCollection = async (path, queryDef, callback) => { + const colRef = collection(this.db, path) + const q = query(colRef, queryDef) + const unsubscribe = onSnapshot(q, callback) + return unsubscribe + } + + static subscribeCollectionGroup = async (path, queryDef, callback) => { + const colGroupRef = collectionGroup(this.db, path) + const q = query(colGroupRef, queryDef) + const unsubscribe = onSnapshot(q, callback) + return unsubscribe + } +} diff --git a/client/src/pages/contactList/index.js b/client/src/pages/contactList/index.js index 11d89fb..c19fc48 100644 --- a/client/src/pages/contactList/index.js +++ b/client/src/pages/contactList/index.js @@ -1,491 +1,503 @@ import ContactListComponent from '@/components/contactList' +import { CreateUseSyncExternalStore, useSyncGlobalVariable } from '@/lib/hooks/useSync' +import { getRandomJoke } from '@/lib/services/random' +import { FirebaseFirestore } from '@/lib/utils/firebase/firestore' import { useState } from 'react' const defaultState = { - contacts:[ - { - doc_id:'somerandomid', - first_name:'zirst', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'scond', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'ads', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'adsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'zirst', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'scond', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'bds', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'cdsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'zirst', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'dcond', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'eds', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'edsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'zirst', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'fcond', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'gds', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'adsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'adsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'adsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'adsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'adsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'adsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'adsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'adsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'adsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'adsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'adsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'zirst', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'scond', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'%ds', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'#dsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'zirst', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'scond', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'%ds', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'#dsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'zirst', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'scond', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'%ds', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'#dsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'zirst', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'scond', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'%ds', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'#dsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'zirst', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'scond', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'%ds', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' - }, - { - doc_id:'somerandomid', - first_name:'#dsad', - last_name:'third', - middle_name:'second', - date_created:new Date(), - date_updated:new Date(), - contact_no:'somerandomnumber', - contact_email:'somerandomemail' + contacts: [ + { + doc_id: 'somerandomid', + first_name: 'zirst', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'scond', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'ads', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'adsad', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'zirst', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'scond', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'bds', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'cdsad', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'zirst', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'dcond', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'eds', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'edsad', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'zirst', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'fcond', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'gds', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'adsad', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'adsad', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'adsad', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'adsad', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'adsad', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'adsad', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'adsad', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'adsad', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'adsad', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'adsad', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'adsad', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'zirst', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'scond', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: '%ds', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: '#dsad', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'zirst', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'scond', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: '%ds', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: '#dsad', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'zirst', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'scond', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: '%ds', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: '#dsad', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'zirst', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'scond', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: '%ds', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: '#dsad', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'zirst', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: 'scond', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: '%ds', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' + }, + { + doc_id: 'somerandomid', + first_name: '#dsad', + last_name: 'third', + middle_name: 'second', + date_created: new Date(), + date_updated: new Date(), + contact_no: 'somerandomnumber', + contact_email: 'somerandomemail' }, ] } -function ContactList () { +function ContactList() { const [state, setState] = useState(defaultState) + const sortedContacts = [...state.contacts].sort((a, b) => a.first_name < b.first_name ? -1 : 1) + const [api, setApi] = useSyncGlobalVariable('api') - useState(()=>{ - setState(prev=>({ + useState(() => { + setState(prev => ({ ...prev, - contacts:sortedContacts + contacts: sortedContacts })) - },[]) + FirebaseFirestore.subscribeDoc('/test/2SCdmBqBHRda31sxq9Z6', (doc)=>{ + setApi(doc.data()) + }) + }, []) + + const eventsHandler = () => { } return ( - + <> + + ) } From a7e605ed0af0a0ec4a9f08595f4b8de47ffa8f01 Mon Sep 17 00:00:00 2001 From: vikyw89 Date: Fri, 17 Mar 2023 23:51:05 +0800 Subject: [PATCH 27/81] fix: lint --- client/src/lib/hooks/useSync.js | 5 +- client/src/lib/utils/firebase/firestore.js | 170 ++++++++++----------- client/src/pages/contactList/index.js | 7 - 3 files changed, 87 insertions(+), 95 deletions(-) diff --git a/client/src/lib/hooks/useSync.js b/client/src/lib/hooks/useSync.js index 9322648..f359ba8 100644 --- a/client/src/lib/hooks/useSync.js +++ b/client/src/lib/hooks/useSync.js @@ -1,5 +1,4 @@ import { useCallback, useSyncExternalStore } from 'react' -import { getRandomJoke } from '../services/random' const useSyncLocalStorageSubscribers = {} const useSyncSessionStorageSubscribers = {} @@ -84,7 +83,7 @@ export const useSyncSessionStorage = (saveDirectory = 'global') => { } export const useSyncGlobalVariable = (saveDirectory = 'global') => { - + const subscribe = useCallback((callback) => { if (!useSyncGlobalVariableSubscribers[saveDirectory]) { useSyncGlobalVariableSubscribers[saveDirectory] = [] @@ -95,7 +94,7 @@ export const useSyncGlobalVariable = (saveDirectory = 'global') => { (el) => el !== callback ) } - }) + },[saveDirectory]) const getSnapshot = () => { return globalVariable[saveDirectory] diff --git a/client/src/lib/utils/firebase/firestore.js b/client/src/lib/utils/firebase/firestore.js index a04686b..7db2b9d 100644 --- a/client/src/lib/utils/firebase/firestore.js +++ b/client/src/lib/utils/firebase/firestore.js @@ -1,9 +1,9 @@ -import { app } from "./config" -import { collection, getDocs, getFirestore, query, updateDoc, addDoc, doc, deleteDoc, setDoc, getDoc, serverTimestamp, collectionGroup, onSnapshot } from "firebase/firestore"; +import { app } from './config' +import { collection, getDocs, getFirestore, query, updateDoc, doc, deleteDoc, setDoc, getDoc, serverTimestamp, collectionGroup, onSnapshot } from 'firebase/firestore' export class FirebaseFirestore { - static db = getFirestore(app) - /** + static db = getFirestore(app) + /** * * * @static @@ -11,97 +11,97 @@ export class FirebaseFirestore { * @param {*} data put a data object eg: {data:'data'} * @memberof FirebaseFirestore */ - static createDoc = async (path, data) => { - const colRef = collection(this.db, path) - const docRef = doc(colRef) - const uid = docRef.id - const response = await setDoc(docRef, { - ...data, - uid, - date_created: serverTimestamp(), - date_updated: serverTimestamp() - }) - return response - } - /** + static createDoc = async (path, data) => { + const colRef = collection(this.db, path) + const docRef = doc(colRef) + const uid = docRef.id + const response = await setDoc(docRef, { + ...data, + uid, + date_created: serverTimestamp(), + date_updated: serverTimestamp() + }) + return response + } + /** * * * @static * @param {*} path docRef eg: /collection/ * @memberof FirebaseFirestore */ - static readDoc = async (path) => { - const docRef = doc(this.db, path) - const response = await getDoc(docRef) - const data = response.data() - return data - } -/** - * - * - * @static - * @param {*} path colRef eg: /collection - * @param {*} queryDef eg: (where('', '==','')) - * @memberof FirebaseFirestore - */ -static readCollection = async (path, queryDef) => { - const colRef = collection(this.db, path) - const q = query(colRef, queryDef) - const snapshot = await getDocs(q) - const data = snapshot.docs - .map((doc) => { - return { - ...doc.data() - } - }) - return data - } + static readDoc = async (path) => { + const docRef = doc(this.db, path) + const response = await getDoc(docRef) + const data = response.data() + return data + } + /** + * + * + * @static + * @param {*} path colRef eg: /collection + * @param {*} queryDef eg: (where('', '==','')) + * @memberof FirebaseFirestore + */ + static readCollection = async (path, queryDef) => { + const colRef = collection(this.db, path) + const q = query(colRef, queryDef) + const snapshot = await getDocs(q) + const data = snapshot.docs + .map((doc) => { + return { + ...doc.data() + } + }) + return data + } - static readCollectionGroup = async (path, queryDef) => { - const colGroupRef = collectionGroup(this.db, path) - const q = query(colGroupRef, queryDef) - const snapshot = await getDocs(q) - const data = snapshot.docs - .map((doc) => { - return { - ...doc.data() - } - }) - return data - } + static readCollectionGroup = async (path, queryDef) => { + const colGroupRef = collectionGroup(this.db, path) + const q = query(colGroupRef, queryDef) + const snapshot = await getDocs(q) + const data = snapshot.docs + .map((doc) => { + return { + ...doc.data() + } + }) + return data + } - static updateDoc = async (path, data) => { - const docRef = doc(this.db, path) - const response = await updateDoc(docRef, { - ...data, - date_updated: serverTimestamp() - }) - return response - } + static updateDoc = async (path, data) => { + const docRef = doc(this.db, path) + const response = await updateDoc(docRef, { + ...data, + date_updated: serverTimestamp() + }) + return response + } - static deleteDoc = async (path) => { - const docRef = doc(this.db, path) - const response = await deleteDoc(docRef) - return response - } + static deleteDoc = async (path) => { + const docRef = doc(this.db, path) + const response = await deleteDoc(docRef) + return response + } - static subscribeDoc = async (path, callback) => { - const docRef = doc(this.db, path) - const unsubscribe = onSnapshot(docRef, callback) - return unsubscribe - } + static subscribeDoc = async (path, callback) => { + const docRef = doc(this.db, path) + const unsubscribe = onSnapshot(docRef, callback) + return unsubscribe + } - static subscribeCollection = async (path, queryDef, callback) => { - const colRef = collection(this.db, path) - const q = query(colRef, queryDef) - const unsubscribe = onSnapshot(q, callback) - return unsubscribe - } + static subscribeCollection = async (path, queryDef, callback) => { + const colRef = collection(this.db, path) + const q = query(colRef, queryDef) + const unsubscribe = onSnapshot(q, callback) + return unsubscribe + } - static subscribeCollectionGroup = async (path, queryDef, callback) => { - const colGroupRef = collectionGroup(this.db, path) - const q = query(colGroupRef, queryDef) - const unsubscribe = onSnapshot(q, callback) - return unsubscribe - } + static subscribeCollectionGroup = async (path, queryDef, callback) => { + const colGroupRef = collectionGroup(this.db, path) + const q = query(colGroupRef, queryDef) + const unsubscribe = onSnapshot(q, callback) + return unsubscribe + } } diff --git a/client/src/pages/contactList/index.js b/client/src/pages/contactList/index.js index c19fc48..f724f93 100644 --- a/client/src/pages/contactList/index.js +++ b/client/src/pages/contactList/index.js @@ -1,7 +1,4 @@ import ContactListComponent from '@/components/contactList' -import { CreateUseSyncExternalStore, useSyncGlobalVariable } from '@/lib/hooks/useSync' -import { getRandomJoke } from '@/lib/services/random' -import { FirebaseFirestore } from '@/lib/utils/firebase/firestore' import { useState } from 'react' @@ -475,16 +472,12 @@ function ContactList() { const [state, setState] = useState(defaultState) const sortedContacts = [...state.contacts].sort((a, b) => a.first_name < b.first_name ? -1 : 1) - const [api, setApi] = useSyncGlobalVariable('api') useState(() => { setState(prev => ({ ...prev, contacts: sortedContacts })) - FirebaseFirestore.subscribeDoc('/test/2SCdmBqBHRda31sxq9Z6', (doc)=>{ - setApi(doc.data()) - }) }, []) From eae4c36542c5c9d9d523f2817ce4ac623018c37f Mon Sep 17 00:00:00 2001 From: vikyw89 Date: Sat, 18 Mar 2023 00:01:40 +0800 Subject: [PATCH 28/81] doc: added documentation for firestore wrapper --- client/src/lib/utils/firebase/firestore.js | 58 ++++++++++++++++++++-- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/client/src/lib/utils/firebase/firestore.js b/client/src/lib/utils/firebase/firestore.js index 7db2b9d..0cbae97 100644 --- a/client/src/lib/utils/firebase/firestore.js +++ b/client/src/lib/utils/firebase/firestore.js @@ -7,8 +7,8 @@ export class FirebaseFirestore { * * * @static - * @param {*} path put a colRef path eg: collection - * @param {*} data put a data object eg: {data:'data'} + * @param {*} path + * @param {*} data {:} * @memberof FirebaseFirestore */ static createDoc = async (path, data) => { @@ -27,7 +27,7 @@ export class FirebaseFirestore { * * * @static - * @param {*} path docRef eg: /collection/ + * @param {*} path / * @memberof FirebaseFirestore */ static readDoc = async (path) => { @@ -40,7 +40,7 @@ export class FirebaseFirestore { * * * @static - * @param {*} path colRef eg: /collection + * @param {String} path // * @param {*} queryDef eg: (where('', '==','')) * @memberof FirebaseFirestore */ @@ -56,7 +56,14 @@ export class FirebaseFirestore { }) return data } - + /** + * + * + * @static + * @param {*} path + * @param {*} queryDef eg: where('','==','') + * @memberof FirebaseFirestore + */ static readCollectionGroup = async (path, queryDef) => { const colGroupRef = collectionGroup(this.db, path) const q = query(colGroupRef, queryDef) @@ -70,6 +77,14 @@ export class FirebaseFirestore { return data } + /** + * + * + * @static + * @param {*} path eg:// + * @param {*} data {:} + * @memberof FirebaseFirestore + */ static updateDoc = async (path, data) => { const docRef = doc(this.db, path) const response = await updateDoc(docRef, { @@ -79,18 +94,42 @@ export class FirebaseFirestore { return response } + /** + * + * + * @static + * @param {*} path eg:// + * @memberof FirebaseFirestore + */ static deleteDoc = async (path) => { const docRef = doc(this.db, path) const response = await deleteDoc(docRef) return response } + /** + * + * + * @static + * @param {*} path eg:// + * @param {*} callback eg: (doc)=>console.log(doc.data()) + * @memberof FirebaseFirestore + */ static subscribeDoc = async (path, callback) => { const docRef = doc(this.db, path) const unsubscribe = onSnapshot(docRef, callback) return unsubscribe } + /** + * + * + * @static + * @param {*} path // + * @param {*} queryDef where('','==','') + * @param {*} callback (doc)=>console.log(doc.data()) + * @memberof FirebaseFirestore + */ static subscribeCollection = async (path, queryDef, callback) => { const colRef = collection(this.db, path) const q = query(colRef, queryDef) @@ -98,6 +137,15 @@ export class FirebaseFirestore { return unsubscribe } + /** + * + * + * @static + * @param {*} path + * @param {*} queryDef where('','==','') + * @param {*} callback (doc)=>console.log(doc.data()) + * @memberof FirebaseFirestore + */ static subscribeCollectionGroup = async (path, queryDef, callback) => { const colGroupRef = collectionGroup(this.db, path) const q = query(colGroupRef, queryDef) From eec1712d084723588df328ac0afc5fa76717b94f Mon Sep 17 00:00:00 2001 From: vikyw89 Date: Sat, 18 Mar 2023 00:05:02 +0800 Subject: [PATCH 29/81] refactor doc --- client/src/lib/utils/firebase/firestore.js | 34 +++++++++++----------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/client/src/lib/utils/firebase/firestore.js b/client/src/lib/utils/firebase/firestore.js index 0cbae97..d67ddc3 100644 --- a/client/src/lib/utils/firebase/firestore.js +++ b/client/src/lib/utils/firebase/firestore.js @@ -7,8 +7,8 @@ export class FirebaseFirestore { * * * @static - * @param {*} path - * @param {*} data {:} + * @param {String} path + * @param {Object} data {:} * @memberof FirebaseFirestore */ static createDoc = async (path, data) => { @@ -27,7 +27,7 @@ export class FirebaseFirestore { * * * @static - * @param {*} path / + * @param {String} path / * @memberof FirebaseFirestore */ static readDoc = async (path) => { @@ -41,7 +41,7 @@ export class FirebaseFirestore { * * @static * @param {String} path // - * @param {*} queryDef eg: (where('', '==','')) + * @param {Object} queryDef eg: (where('', '==','')) * @memberof FirebaseFirestore */ static readCollection = async (path, queryDef) => { @@ -60,8 +60,8 @@ export class FirebaseFirestore { * * * @static - * @param {*} path - * @param {*} queryDef eg: where('','==','') + * @param {String} path + * @param {Object} queryDef eg: where('','==','') * @memberof FirebaseFirestore */ static readCollectionGroup = async (path, queryDef) => { @@ -81,8 +81,8 @@ export class FirebaseFirestore { * * * @static - * @param {*} path eg:// - * @param {*} data {:} + * @param {String} path eg:// + * @param {Object} data {:} * @memberof FirebaseFirestore */ static updateDoc = async (path, data) => { @@ -98,7 +98,7 @@ export class FirebaseFirestore { * * * @static - * @param {*} path eg:// + * @param {String} path eg:// * @memberof FirebaseFirestore */ static deleteDoc = async (path) => { @@ -111,8 +111,8 @@ export class FirebaseFirestore { * * * @static - * @param {*} path eg:// - * @param {*} callback eg: (doc)=>console.log(doc.data()) + * @param {String} path eg:// + * @param {Function} callback eg: (doc)=>console.log(doc.data()) * @memberof FirebaseFirestore */ static subscribeDoc = async (path, callback) => { @@ -125,9 +125,9 @@ export class FirebaseFirestore { * * * @static - * @param {*} path // - * @param {*} queryDef where('','==','') - * @param {*} callback (doc)=>console.log(doc.data()) + * @param {String} path // + * @param {Object} queryDef where('','==','') + * @param {Function} callback (doc)=>console.log(doc.data()) * @memberof FirebaseFirestore */ static subscribeCollection = async (path, queryDef, callback) => { @@ -141,9 +141,9 @@ export class FirebaseFirestore { * * * @static - * @param {*} path - * @param {*} queryDef where('','==','') - * @param {*} callback (doc)=>console.log(doc.data()) + * @param {String} path + * @param {Object} queryDef where('','==','') + * @param {Function} callback (doc)=>console.log(doc.data()) * @memberof FirebaseFirestore */ static subscribeCollectionGroup = async (path, queryDef, callback) => { From 6f7616b05e519a9a713ddb253077e1c81cb0ba22 Mon Sep 17 00:00:00 2001 From: vikyw89 Date: Sat, 18 Mar 2023 00:09:48 +0800 Subject: [PATCH 30/81] fix: lint --- client/src/lib/utils/firebase/firestore.js | 83 +++++++++++++--------- 1 file changed, 48 insertions(+), 35 deletions(-) diff --git a/client/src/lib/utils/firebase/firestore.js b/client/src/lib/utils/firebase/firestore.js index d67ddc3..986c3d7 100644 --- a/client/src/lib/utils/firebase/firestore.js +++ b/client/src/lib/utils/firebase/firestore.js @@ -1,16 +1,29 @@ import { app } from './config' -import { collection, getDocs, getFirestore, query, updateDoc, doc, deleteDoc, setDoc, getDoc, serverTimestamp, collectionGroup, onSnapshot } from 'firebase/firestore' +import { + collection, + getDocs, + getFirestore, + query, + updateDoc, + doc, + deleteDoc, + setDoc, + getDoc, + serverTimestamp, + collectionGroup, + onSnapshot, +} from 'firebase/firestore' export class FirebaseFirestore { static db = getFirestore(app) /** - * - * - * @static - * @param {String} path - * @param {Object} data {:} - * @memberof FirebaseFirestore - */ + * + * + * @static + * @param {String} path + * @param {Object} data {:} + * @memberof FirebaseFirestore + */ static createDoc = async (path, data) => { const colRef = collection(this.db, path) const docRef = doc(colRef) @@ -19,17 +32,17 @@ export class FirebaseFirestore { ...data, uid, date_created: serverTimestamp(), - date_updated: serverTimestamp() + date_updated: serverTimestamp(), }) return response } /** - * - * - * @static - * @param {String} path / - * @memberof FirebaseFirestore - */ + * + * + * @static + * @param {String} path / + * @memberof FirebaseFirestore + */ static readDoc = async (path) => { const docRef = doc(this.db, path) const response = await getDoc(docRef) @@ -37,23 +50,22 @@ export class FirebaseFirestore { return data } /** - * - * - * @static - * @param {String} path // - * @param {Object} queryDef eg: (where('', '==','')) - * @memberof FirebaseFirestore - */ + * + * + * @static + * @param {String} path // + * @param {Object} queryDef eg: (where('', '==','')) + * @memberof FirebaseFirestore + */ static readCollection = async (path, queryDef) => { const colRef = collection(this.db, path) const q = query(colRef, queryDef) const snapshot = await getDocs(q) - const data = snapshot.docs - .map((doc) => { - return { - ...doc.data() - } - }) + const data = snapshot.docs.map((doc) => { + return { + ...doc.data(), + } + }) return data } /** @@ -68,12 +80,11 @@ export class FirebaseFirestore { const colGroupRef = collectionGroup(this.db, path) const q = query(colGroupRef, queryDef) const snapshot = await getDocs(q) - const data = snapshot.docs - .map((doc) => { - return { - ...doc.data() - } - }) + const data = snapshot.docs.map((doc) => { + return { + ...doc.data(), + } + }) return data } @@ -84,12 +95,14 @@ export class FirebaseFirestore { * @param {String} path eg:// * @param {Object} data {:} * @memberof FirebaseFirestore + * @returns + * A Promise resolved once the data has been successfully written to the backend (note that it won't resolve while you're offline). */ static updateDoc = async (path, data) => { const docRef = doc(this.db, path) const response = await updateDoc(docRef, { ...data, - date_updated: serverTimestamp() + date_updated: serverTimestamp(), }) return response } From 83211d9024001087ffbf3873bc4c9efab513a03c Mon Sep 17 00:00:00 2001 From: vikyw89 Date: Sat, 18 Mar 2023 00:41:29 +0800 Subject: [PATCH 31/81] added: firestore create with docID --- client/src/lib/hooks/useSyncFirestore.js | 11 +++++++ client/src/lib/utils/firebase/firestore.js | 36 +++++++++++++++------- 2 files changed, 36 insertions(+), 11 deletions(-) create mode 100644 client/src/lib/hooks/useSyncFirestore.js diff --git a/client/src/lib/hooks/useSyncFirestore.js b/client/src/lib/hooks/useSyncFirestore.js new file mode 100644 index 0000000..a4aa70c --- /dev/null +++ b/client/src/lib/hooks/useSyncFirestore.js @@ -0,0 +1,11 @@ +import { FirebaseFirestore } from "../utils/firebase/firestore" +import { useSyncGlobalVariable } from "./useSync" + +export const useSyncFirestore = (path) => { + const [clientModel, setClientModel] = useState('clientModel') + + const setFirestore = (arg) => { + FirebaseFirestore. + } + return [clientModel, setFirestore] +} \ No newline at end of file diff --git a/client/src/lib/utils/firebase/firestore.js b/client/src/lib/utils/firebase/firestore.js index 986c3d7..ab2bfea 100644 --- a/client/src/lib/utils/firebase/firestore.js +++ b/client/src/lib/utils/firebase/firestore.js @@ -22,20 +22,34 @@ export class FirebaseFirestore { * @static * @param {String} path * @param {Object} data {:} + * @param {String} path optional * @memberof FirebaseFirestore */ - static createDoc = async (path, data) => { - const colRef = collection(this.db, path) - const docRef = doc(colRef) - const uid = docRef.id - const response = await setDoc(docRef, { - ...data, - uid, - date_created: serverTimestamp(), - date_updated: serverTimestamp(), - }) - return response + static createDoc = async (path, data, id) => { + if (id) { + const docRef = doc(this.db, path, id) + const uid = id + const response = await setDoc(docRef, { + ...data, + uid, + date_created: serverTimestamp(), + date_updated: serverTimestamp(), + }) + return response + } else { + const colRef = collection(this.db, path) + const docRef = doc(colRef) + const uid = docRef.id + const response = await setDoc(docRef, { + ...data, + uid, + date_created: serverTimestamp(), + date_updated: serverTimestamp(), + }) + return response + } } + /** * * From 1a6b513d8e4915b75894e1d9ada15a788ec1c39c Mon Sep 17 00:00:00 2001 From: vikyw89 Date: Sat, 18 Mar 2023 10:39:36 +0800 Subject: [PATCH 32/81] doc: added javadoc to useSyncFirestore hook --- client/src/lib/hooks/useSync.js | 11 +- client/src/lib/hooks/useSyncFirestore.js | 47 ++++- client/src/lib/utils/firebase/firestore.js | 193 ++++++++++++--------- client/src/pages/contactList/index.js | 22 ++- 4 files changed, 174 insertions(+), 99 deletions(-) diff --git a/client/src/lib/hooks/useSync.js b/client/src/lib/hooks/useSync.js index f359ba8..d3e0cdf 100644 --- a/client/src/lib/hooks/useSync.js +++ b/client/src/lib/hooks/useSync.js @@ -83,18 +83,17 @@ export const useSyncSessionStorage = (saveDirectory = 'global') => { } export const useSyncGlobalVariable = (saveDirectory = 'global') => { - - const subscribe = useCallback((callback) => { - if (!useSyncGlobalVariableSubscribers[saveDirectory]) { - useSyncGlobalVariableSubscribers[saveDirectory] = [] - } + if (!useSyncGlobalVariableSubscribers[saveDirectory]) { + useSyncGlobalVariableSubscribers[saveDirectory] = [] + } + const subscribe = (callback) => { useSyncGlobalVariableSubscribers[saveDirectory] = [...useSyncGlobalVariableSubscribers[saveDirectory], callback] return () => { useSyncGlobalVariableSubscribers[saveDirectory] = useSyncGlobalVariableSubscribers[saveDirectory].filter( (el) => el !== callback ) } - },[saveDirectory]) + } const getSnapshot = () => { return globalVariable[saveDirectory] diff --git a/client/src/lib/hooks/useSyncFirestore.js b/client/src/lib/hooks/useSyncFirestore.js index a4aa70c..a9f54b5 100644 --- a/client/src/lib/hooks/useSyncFirestore.js +++ b/client/src/lib/hooks/useSyncFirestore.js @@ -1,11 +1,40 @@ -import { FirebaseFirestore } from "../utils/firebase/firestore" -import { useSyncGlobalVariable } from "./useSync" +import { useEffect } from 'react' +import { FirebaseFirestore } from '../utils/firebase/firestore' +import { useSyncGlobalVariable } from './useSync' -export const useSyncFirestore = (path) => { - const [clientModel, setClientModel] = useState('clientModel') +/** + * Custom hook that synchronizes a Firestore document with a client react syncGlobalVariable state + * + * @param {string} colPath - The path of the collection containing the document. + * @param {string} docId - The ID of the document to synchronize. + * @returns {[object, function]} An array containing the synchronized client model and a function to update the Firestore document. + * @usage inside react component, const [model, setModel] = useSyncFirestore(firestoreColPath, firestoreDocId) + * @abstract this will generate a synced object model of js private global variable using react useSyncExternalStore hook, that is subscribing + * to a firestore document + */ +export const useSyncFirestore = (colPath, docId) => { + const [clientModel, setClientModel] = useSyncGlobalVariable(docId); + const docPath = `${colPath}/${docId}`; - const setFirestore = (arg) => { - FirebaseFirestore. - } - return [clientModel, setFirestore] -} \ No newline at end of file + useEffect(() => { + // Create the document if it does not exist + FirebaseFirestore.createDoc(colPath, {}, docId); + + // Subscribe to document changes + FirebaseFirestore.subscribeDoc(docPath, (doc) => { + setClientModel(doc.data()); + }); + }, []); + + /** + * Updates the Firestore document with the provided data. + * + * @param {object} data - The data to update the Firestore document with. + * @returns {Promise} A Promise that resolves when the Firestore document is successfully updated. + */ + const setFirestore = (data) => { + return FirebaseFirestore.updateDoc(docPath, data); + }; + + return [clientModel, setFirestore]; +} diff --git a/client/src/lib/utils/firebase/firestore.js b/client/src/lib/utils/firebase/firestore.js index ab2bfea..b5ddd93 100644 --- a/client/src/lib/utils/firebase/firestore.js +++ b/client/src/lib/utils/firebase/firestore.js @@ -16,167 +16,200 @@ import { export class FirebaseFirestore { static db = getFirestore(app) + /** - * - * - * @static - * @param {String} path - * @param {Object} data {:} - * @param {String} path optional - * @memberof FirebaseFirestore + * Creates or updates a Firestore document in the specified collection path. + * @param {string} colPath - The path of the Firestore collection. + * @param {Object} data - The data to be stored in the Firestore document. + * @param {string} [docId] - The ID of the Firestore document, if updating an existing document. + * @returns {Promise} A Promise that resolves to the Firestore response object. */ - static createDoc = async (path, data, id) => { - if (id) { - const docRef = doc(this.db, path, id) - const uid = id + static async createDoc(colPath, data, docId) { + // If updating an existing document, get a reference to the document and its ID. + if (docId) { + const docRef = doc(this.db, colPath, docId) + const uid = docRef.id + + // Set the data for the document, including the UID and timestamps. const response = await setDoc(docRef, { ...data, uid, date_created: serverTimestamp(), date_updated: serverTimestamp(), - }) + }, { merge: true }) + return response - } else { - const colRef = collection(this.db, path) + } + // If creating a new document, get a reference to the collection and create a new document with an autogenerated ID. + else { + const colRef = collection(this.db, colPath) const docRef = doc(colRef) const uid = docRef.id + + // Set the data for the document, including the UID and timestamps. const response = await setDoc(docRef, { ...data, uid, date_created: serverTimestamp(), date_updated: serverTimestamp(), - }) + }, { merge: true }) + return response } } + + /** - * - * - * @static - * @param {String} path / - * @memberof FirebaseFirestore + * Reads a Firestore document from the specified path. + * @param {string} docPath - The path of the Firestore document. + * @returns {Promise} A Promise that resolves to the data contained in the Firestore document. */ - static readDoc = async (path) => { - const docRef = doc(this.db, path) + static async readDoc(docPath) { + // Get a reference to the document. + const docRef = doc(this.db, docPath) + + // Get the document data. const response = await getDoc(docRef) const data = response.data() + return data } + + /** - * - * - * @static - * @param {String} path // - * @param {Object} queryDef eg: (where('', '==','')) - * @memberof FirebaseFirestore + * Reads a Firestore collection from the specified path with the given query definition. + * @param {string} colPath - The path of the Firestore collection. + * @param {Object} queryDef - The query definition object for filtering and sorting the collection. + * @returns {Promise} A Promise that resolves to an array of data objects from the Firestore documents in the collection. */ - static readCollection = async (path, queryDef) => { - const colRef = collection(this.db, path) + static async readCollection(colPath, queryDef) { + // Get a reference to the collection and apply the query definition. + const colRef = collection(this.db, colPath) const q = query(colRef, queryDef) + + // Get the documents in the collection that match the query. const snapshot = await getDocs(q) + + // Map the documents to an array of data objects and return it. const data = snapshot.docs.map((doc) => { return { ...doc.data(), } }) + return data } + + /** - * - * - * @static - * @param {String} path - * @param {Object} queryDef eg: where('','==','') - * @memberof FirebaseFirestore + * Reads a Firestore collection group with the given ID and query definition. + * @param {string} colGroupId - The ID of the Firestore collection group. + * @param {Object} queryDef - The query definition object for filtering and sorting the collection group. + * @returns {Promise} A Promise that resolves to an array of data objects from the Firestore documents in the collection group. */ - static readCollectionGroup = async (path, queryDef) => { - const colGroupRef = collectionGroup(this.db, path) + static async readCollectionGroup(colGroupId, queryDef) { + // Get a reference to the collection group and apply the query definition. + const colGroupRef = collectionGroup(this.db, colGroupId) const q = query(colGroupRef, queryDef) + + // Get the documents in the collection group that match the query. const snapshot = await getDocs(q) + + // Map the documents to an array of data objects and return it. const data = snapshot.docs.map((doc) => { return { ...doc.data(), } }) + return data } + + /** - * - * - * @static - * @param {String} path eg:// - * @param {Object} data {:} - * @memberof FirebaseFirestore - * @returns - * A Promise resolved once the data has been successfully written to the backend (note that it won't resolve while you're offline). + * Updates a Firestore document with the given data. + * @param {string} docPath - The path of the Firestore document to update. + * @param {Object} data - The data object to update the document with. + * @returns {Promise} A Promise that resolves to the Firestore response object. */ - static updateDoc = async (path, data) => { - const docRef = doc(this.db, path) + static async updateDoc(docPath, data) { + // Get a reference to the document and update it with the given data. + const docRef = doc(this.db, docPath) const response = await updateDoc(docRef, { ...data, date_updated: serverTimestamp(), }) + + // Return the Firestore response object. return response } + /** - * - * - * @static - * @param {String} path eg:// - * @memberof FirebaseFirestore + * Deletes a Firestore document with the given path. + * @param {string} docPath - The path of the Firestore document to delete. + * @returns {Promise} A Promise that resolves to the Firestore response object. */ - static deleteDoc = async (path) => { - const docRef = doc(this.db, path) + static async deleteDoc(docPath) { + // Get a reference to the document and delete it. + const docRef = doc(this.db, docPath) const response = await deleteDoc(docRef) + + // Return the Firestore response object. return response } + + /** - * - * - * @static - * @param {String} path eg:// - * @param {Function} callback eg: (doc)=>console.log(doc.data()) - * @memberof FirebaseFirestore + * Subscribes to changes in a Firestore document with the given path. + * @param {string} docPath - The path of the Firestore document to subscribe to. + * @param {function} callback - A callback function to be called when the document changes. + * @returns {function} A function to unsubscribe from the Firestore document. */ - static subscribeDoc = async (path, callback) => { - const docRef = doc(this.db, path) + static async subscribeDoc(docPath, callback) { + // Get a reference to the document and subscribe to changes. + const docRef = doc(this.db, docPath) const unsubscribe = onSnapshot(docRef, callback) + + // Return the function to unsubscribe from the Firestore document. return unsubscribe } + /** - * - * - * @static - * @param {String} path // - * @param {Object} queryDef where('','==','') - * @param {Function} callback (doc)=>console.log(doc.data()) - * @memberof FirebaseFirestore + * Subscribes to changes in a Firestore collection with the given path and query. + * @param {string} colPath - The path of the Firestore collection to subscribe to. + * @param {object} queryDef - A query definition object to define the filters and sorting of the data. + * @param {function} callback - A callback function to be called when the collection changes. + * @returns {function} A function to unsubscribe from the Firestore collection. */ - static subscribeCollection = async (path, queryDef, callback) => { - const colRef = collection(this.db, path) + static async subscribeCollection(colPath, queryDef, callback) { + // Get a reference to the collection and query, and subscribe to changes. + const colRef = collection(this.db, colPath) const q = query(colRef, queryDef) const unsubscribe = onSnapshot(q, callback) + + // Return the function to unsubscribe from the Firestore collection. return unsubscribe } + /** + * Subscribes to changes in a collection group with the specified query definition. * - * - * @static - * @param {String} path - * @param {Object} queryDef where('','==','') - * @param {Function} callback (doc)=>console.log(doc.data()) - * @memberof FirebaseFirestore + * @param {string} colGroupId - The ID of the collection group to subscribe to. + * @param {object} queryDef - The query definition object. + * @param {function} callback - The callback function to be called when the subscribed collection group changes. + * @returns {function} The unsubscribe function to stop listening to changes in the subscribed collection group. */ - static subscribeCollectionGroup = async (path, queryDef, callback) => { - const colGroupRef = collectionGroup(this.db, path) + static subscribeCollectionGroup = async (colGroupId, queryDef, callback) => { + const colGroupRef = collectionGroup(this.db, colGroupId) const q = query(colGroupRef, queryDef) const unsubscribe = onSnapshot(q, callback) return unsubscribe } + } diff --git a/client/src/pages/contactList/index.js b/client/src/pages/contactList/index.js index f724f93..c924d59 100644 --- a/client/src/pages/contactList/index.js +++ b/client/src/pages/contactList/index.js @@ -1,5 +1,8 @@ import ContactListComponent from '@/components/contactList' -import { useState } from 'react' +import { useSyncFirestore } from '@/lib/hooks/useSyncFirestore' +import { useState, useSyncExternalStore } from 'react' +import Button from '@mui/material/Button' +import { useSyncGlobalVariable } from '@/lib/hooks/useSync' const defaultState = { @@ -470,9 +473,8 @@ const defaultState = { function ContactList() { const [state, setState] = useState(defaultState) - + const [model, setModel, status] = useSyncFirestore('config','configData') const sortedContacts = [...state.contacts].sort((a, b) => a.first_name < b.first_name ? -1 : 1) - useState(() => { setState(prev => ({ ...prev, @@ -480,12 +482,24 @@ function ContactList() { })) }, []) - + const clickHandler = () => { + if (!model.number) { + setModel({number: 1}) + } + // setModel({ + // ...model, + // number:model.number + 1 + // }) + setModel('test') + console.log(model, status) + } const eventsHandler = () => { } return ( <> + {model?.number ?? ''} + Date: Sat, 18 Mar 2023 21:12:15 +0800 Subject: [PATCH 33/81] chore: Replace the invalid_oob_code error with description error messages, #87 --- client/src/components/account/index.js | 12 +++++++++--- client/src/pages/account/index.js | 4 +++- client/src/pages/account/messages.json | 9 ++++++--- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/client/src/components/account/index.js b/client/src/components/account/index.js index 4433b92..31993ad 100644 --- a/client/src/components/account/index.js +++ b/client/src/components/account/index.js @@ -22,7 +22,13 @@ function AccountComponent ({ state, handleFormSubmit }) { borderRadius: '8px', boxShadow: 'inset 0 0 0.5px 1px hsla(0, 0%, 100%, 0.1), 0 0 0 1px hsla(230, 13%, 9%, 0.075), 0 0.3px 0.4px hsla(230, 13%, 9%, 0.02), 0 0.9px 1.5px hsla(230, 13%, 9%, 0.045), 0 3.5px 6px hsla(230, 13%, 9%, 0.09)', textAlign: 'center', - padding: '24px' + padding: '24px', + '& a': (theme) => ({ + fontSize: '12px', + textAlign: 'center', + marginTop: '-5px', + color: theme.palette.text.primary, + }) }}>
{state.message && @@ -42,8 +48,8 @@ function AccountComponent ({ state, handleFormSubmit }) { {state.loading ? : (state.error !== '') - ? - Error: {state.error} + ? + : {state.success} diff --git a/client/src/pages/account/index.js b/client/src/pages/account/index.js index bb2d9d9..3843078 100644 --- a/client/src/pages/account/index.js +++ b/client/src/pages/account/index.js @@ -79,7 +79,9 @@ function Account () { state={{ ...state, loading, - error: state.error || asyncError, + error: (asyncError) + ? messages.find(item => item.mode === state.mode)?.error ?? '' + : state.error, locked: (status === RequestStatus.ERROR && state.mode === ACCOUNT_ACTION.RESET_PASSWORD), success: (status === RequestStatus.SUCCESS && !state.checkCode) ? messages.find(item => item.mode === state.mode)?.success : '', diff --git a/client/src/pages/account/messages.json b/client/src/pages/account/messages.json index bd1b49c..5c2fc81 100644 --- a/client/src/pages/account/messages.json +++ b/client/src/pages/account/messages.json @@ -2,16 +2,19 @@ { "mode": "verifyEmail", "message": "Please wait while we verify your email.", - "success": "Success! Email verified." + "success": "Success! Email verified. You can now sign in with your new account.", + "error": "Try verifying your email again.
Your request to verify your email has expired or the link has already been used." }, { "mode": "resetPassword", "message": "Reset your password.", - "success": "Success! Password changed." + "success": "Success! Password changed. You can now sign in using your new password.", + "error": "Try resetting your password again.
Your request to change your password has expired or the link has already been used." }, { "mode": "recoverEmail", "message": "", - "success": "" + "success": "", + "error": "" } ] From 81c36e3f4ed82c6040bbdc74ab4846af21a10a6b Mon Sep 17 00:00:00 2001 From: weaponsforge Date: Sat, 18 Mar 2023 21:54:49 +0800 Subject: [PATCH 34/81] chore: Disable the reset password button on the reset password handler page --- client/src/common/ui/transparentbox/index.js | 9 ++++++--- client/src/domain/account/resetpassword.js | 4 +++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/client/src/common/ui/transparentbox/index.js b/client/src/common/ui/transparentbox/index.js index 35abd07..dd07980 100644 --- a/client/src/common/ui/transparentbox/index.js +++ b/client/src/common/ui/transparentbox/index.js @@ -4,12 +4,15 @@ function TransparentBox ({ children }) { return ( Submit From c738514ea781baba9362a4b53288378348ce0494 Mon Sep 17 00:00:00 2001 From: weaponsforge Date: Sun, 19 Mar 2023 00:12:09 +0800 Subject: [PATCH 35/81] chore: Use html links in the account page's error/success messages --- client/src/pages/account/messages.json | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/client/src/pages/account/messages.json b/client/src/pages/account/messages.json index 5c2fc81..3bb94ba 100644 --- a/client/src/pages/account/messages.json +++ b/client/src/pages/account/messages.json @@ -2,19 +2,23 @@ { "mode": "verifyEmail", "message": "Please wait while we verify your email.", - "success": "Success! Email verified. You can now sign in with your new account.", - "error": "Try verifying your email again.
Your request to verify your email has expired or the link has already been used." + "success": "Success! Email verified. You can now sign in with your new account.", + "error": "Try verifying your email again.
Your request to verify your email has expired or the link has already been used.

Resend email verification? Resend" }, { "mode": "resetPassword", "message": "Reset your password.", - "success": "Success! Password changed. You can now sign in using your new password.", + "success": "Success! Password changed. You can now sign in using your new password.", "error": "Try resetting your password again.
Your request to change your password has expired or the link has already been used." }, + { + "mode": "resend_email_verification", + "message": "Enter your registration email", + "success": "Success! Email verification sent." + }, { "mode": "recoverEmail", "message": "", - "success": "", - "error": "" + "success": "" } ] From 8d48d4d40fd3bda9f082fc6a637e2a323f03b18d Mon Sep 17 00:00:00 2001 From: weaponsforge Date: Sun, 19 Mar 2023 00:12:49 +0800 Subject: [PATCH 36/81] chore: Add a promise name in the usePromise state --- client/src/lib/hooks/usePromise.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/client/src/lib/hooks/usePromise.js b/client/src/lib/hooks/usePromise.js index ff1c634..d4d7c25 100644 --- a/client/src/lib/hooks/usePromise.js +++ b/client/src/lib/hooks/usePromise.js @@ -2,6 +2,7 @@ import { useState, useEffect } from 'react' import PromiseWrapper from '@/utils/promiseWrapper' const defaultState = { + name: 'default', response: null, loading: false, error: '', @@ -14,15 +15,17 @@ const RequestStatus = PromiseWrapper.STATUS * A helper/wrapper hook for handling async service methods (Promises). * Returns the success, error or loading status and response of a Promise as React states. * @param {Promise} promise - An async javascript method (Promise) that resolves and rejects. - * @returns {Object} React state object { response, loading, error, status } + * @param {String} name - Optional descriptive promise name identifier + * @returns {Object} React state object { name, response, loading, error, status } */ -function usePromise (promise) { +function usePromise (promise, name = 'default') { const [state, setState] = useState(defaultState) useEffect(() => { if (promise !== null) { setState(prev => ({ ...prev, + name, response: null, loading: true, error: '', @@ -48,9 +51,10 @@ function usePromise (promise) { } ) } - }, [promise]) + }, [promise, name]) return { + name: state.name, response: state.response, loading: state.loading, error: state.error, From 9775ce2754c1442eb3f057215715bef0eaf5e7fa Mon Sep 17 00:00:00 2001 From: weaponsforge Date: Sun, 19 Mar 2023 00:14:47 +0800 Subject: [PATCH 37/81] feat: Allow resending the email verification link on the ui, ##22, #23 --- client/src/components/account/index.js | 24 ++++-- .../src/domain/account/resendverification.js | 80 +++++++++++++++++++ client/src/domain/account/resetpassword.js | 6 +- client/src/lib/services/account/index.js | 3 +- client/src/lib/utils/promiseWrapper.js | 1 + client/src/pages/account/index.js | 72 ++++++++++++----- 6 files changed, 158 insertions(+), 28 deletions(-) create mode 100644 client/src/domain/account/resendverification.js diff --git a/client/src/components/account/index.js b/client/src/components/account/index.js index 31993ad..d6bbb6e 100644 --- a/client/src/components/account/index.js +++ b/client/src/components/account/index.js @@ -6,9 +6,14 @@ import Typography from '@mui/material/Typography' import Page from '@/common/layout/page' import ResetPasswordComponent from '@/domain/account/resetpassword' +import ResendVerificationComponent from '@/domain/account/resendverification' import { ACCOUNT_ACTION } from '@/services/account' -function AccountComponent ({ state, handleFormSubmit }) { +function AccountComponent ({ + state, + handleResetPasswordSubmit, + handleResendEmailVerification +}) { return ( @@ -40,7 +45,15 @@ function AccountComponent ({ state, handleFormSubmit }) { + } + + {(state.mode === ACCOUNT_ACTION.RESEND_EMAIL_VERIFICATION) && + } @@ -51,8 +64,8 @@ function AccountComponent ({ state, handleFormSubmit }) { ? - : - {state.success} + : + }
@@ -64,7 +77,8 @@ function AccountComponent ({ state, handleFormSubmit }) { AccountComponent.propTypes = { state: PropTypes.object, - handleFormSubmit: PropTypes.func + handleResetPasswordSubmit: PropTypes.func, + ResendVerificationComponent: PropTypes.func } export default AccountComponent diff --git a/client/src/domain/account/resendverification.js b/client/src/domain/account/resendverification.js new file mode 100644 index 0000000..4239b65 --- /dev/null +++ b/client/src/domain/account/resendverification.js @@ -0,0 +1,80 @@ +import { useState } from 'react' +import PropTypes from 'prop-types' + +import { Validate } from '@/lib/utils/textValidation' + +import Button from '@mui/material/Button' +import Box from '@mui/material/Box' +import TransparentTextfield from '@/common/ui/transparentfield' +import { useSyncLocalStorage } from '@/lib/hooks/useSync' + +const defaultState = { helperText: '', error: false, color: '', value: '' } + +function ResendVerificationComponent ({ + loading, + locked, + handleResendEmailVerification +}) { + const [email, setPassword] = useState(defaultState) + const [activeTheme] = useSyncLocalStorage('activeTheme') + + const handleInputChange = (e) => { + const { id } = e.target + + if (id === 'email') { + setPassword({ + ...Validate.email(e.target.value), + value: e.target.value + }) + } + } + + return ( + +
+ + + + +
+ ) +} + +ResendVerificationComponent.propTypes = { + loading: PropTypes.bool, + locked: PropTypes.bool, + ResendVerificationComponent: PropTypes.func +} + +export default ResendVerificationComponent diff --git a/client/src/domain/account/resetpassword.js b/client/src/domain/account/resetpassword.js index 331ac7d..efe0f70 100644 --- a/client/src/domain/account/resetpassword.js +++ b/client/src/domain/account/resetpassword.js @@ -15,7 +15,7 @@ const INPUT_ID = { CONFIRM_PASSWORD: 'confirmpassword' } -function ResetPasswordComponent ({ loading, locked, handleFormSubmit }) { +function ResetPasswordComponent ({ loading, locked, handleResetPasswordSubmit }) { const [password, setPassword] = useState(defaultState) const [confirmpassword, setConfirmPassword] = useState(defaultState) const [activeTheme] = useSyncLocalStorage('activeTheme') @@ -50,7 +50,7 @@ function ResetPasswordComponent ({ loading, locked, handleFormSubmit }) { width: '100%' } }}> -
+ { if (router.isReady) { const { mode, actionCode } = router.query @@ -36,21 +47,30 @@ function Account () { return } - if (mode && actionCode) { + if (mode) { setState(prev => ({ ...prev, message: messages.find(item => item.mode === mode)?.message ?? '', - mode, - actionCode + mode })) + } + + if (actionCode) { + setState(prev => ({ ...prev, actionCode })) switch (mode) { case ACCOUNT_ACTION.VERIFY_EMAIL: - setMethod(verifyAccountEmail(actionCode)) + setMethod({ + promise: verifyAccountEmail(actionCode), + name: ACCOUNT_ACTION.VERIFY_EMAIL + }) break case ACCOUNT_ACTION.RESET_PASSWORD: setState(prev => ({ ...prev, checkCode: true })) - setMethod(verifyPasswordResetCode(auth, actionCode)) + setMethod({ + promise: verifyPasswordResetCode(auth, actionCode), + name: 'checkcode' + }) break default: setState(prev => ({ ...prev, @@ -58,35 +78,49 @@ function Account () { break } } else { - setState(prev => ({ ...prev, - loading: false, error: 'Invalid request' })) + if (mode !== ACCOUNT_ACTION.RESEND_EMAIL_VERIFICATION) { + setState(prev => ({ ...prev, + loading: false, error: 'Invalid request' })) + } } } }, [router.isReady, router.query]) - const handleFormSubmit = (e) => { + const handleResetPasswordSubmit = (e) => { e.preventDefault() setState(prev => ({ ...prev, checkCode: false })) - setMethod(resetAccountPassword( - state.actionCode, - e?.target?.password?.value ?? '') + setMethod({ + promise: resetAccountPassword( + state.actionCode, + e?.target?.password?.value ?? ''), + name: ACCOUNT_ACTION.RESET_PASSWORD + } ) } + const handleResendEmailVerification = (e) => { + e.preventDefault() + setMethod({ + promise: sendEmailVerification(e?.target?.email?.value ?? ''), + name: ACCOUNT_ACTION.RESEND_EMAIL_VERIFICATION + }) + } + return ( item.mode === state.mode)?.error ?? '' - : state.error, locked: (status === RequestStatus.ERROR && state.mode === ACCOUNT_ACTION.RESET_PASSWORD), + error: (asyncError && asyncError !== 'Network Error') + ? messages.find(item => item.mode === state.mode)?.error ?? asyncError + : asyncError, success: (status === RequestStatus.SUCCESS && !state.checkCode) ? - messages.find(item => item.mode === state.mode)?.success : '', + messages.find(item => item.mode === name)?.success : '', }} - handleFormSubmit={handleFormSubmit} + handleResetPasswordSubmit={handleResetPasswordSubmit} + handleResendEmailVerification={handleResendEmailVerification} /> ) } From c8745cebf212371a9ab52396c19e7a128a24a139 Mon Sep 17 00:00:00 2001 From: vikyw89 Date: Sun, 19 Mar 2023 16:32:21 +0800 Subject: [PATCH 38/81] refactor: bg animate to use css, no more js. --- client/src/common/layout/page/index.js | 53 +++++++------------------- client/src/lib/hooks/useBgColor.js | 39 ------------------- client/src/pages/contactList/index.js | 16 ++++---- 3 files changed, 21 insertions(+), 87 deletions(-) delete mode 100644 client/src/lib/hooks/useBgColor.js diff --git a/client/src/common/layout/page/index.js b/client/src/common/layout/page/index.js index 05fd475..94357d6 100644 --- a/client/src/common/layout/page/index.js +++ b/client/src/common/layout/page/index.js @@ -3,22 +3,11 @@ import Box from '@mui/material/Box' import Footer from '@/common/layout/footer' import Header from '@/common/layout/header' import Section from '@/common/layout/section' -import { useEffect } from 'react' -import { useBgColor } from '@/lib/hooks/useBgColor' -import { useSyncLocalStorage } from '@/lib/hooks/useSync' const Background = () => { - const [activeTheme] = useSyncLocalStorage('activeTheme') - const [bgColor, animateBgColor] = useBgColor() - - useEffect(()=>{ - animateBgColor() - },[animateBgColor]) - return ( { top: 0, width: '100%', height: '100%', - background: `hsla(${bgColor},40%,80%,${activeTheme === 'dark' ? '10%' : '80%'})`, - animation: 'animate 90s linear infinite', + animation: `animate 90s linear infinite, colorSwitcher 21s alternate infinite` }, - '&:after':{ + '&:after': { left: '15vw', }, - '&:before':{ + '&:before': { right: '15vw', animationDelay: '-30s', animationDirection: 'reverse', }, '@keyframes colorSwitcher': { '0%': { - background: '#74C390', - }, - '16%': { - background: '#5DBDB6', - }, - '33%': { - background: '#59D4E1', - }, - '50%': { - background: '#51BCE8', - }, - '66%': { - background: '#FA5374', - }, - '83%': { - background: '#E46653', + backgroundColor: `hsla(0,40%,80%,30%)`, }, '100%': { - background: '#74C390', + backgroundColor: `hsla(255,40%,80%,30%)`, } }, '@keyframes animate': { - '0%': { + 'from': { transform: 'rotate(0deg)', }, - '100%': { + 'to': { transform: 'rotate(360deg)' } } @@ -73,20 +46,20 @@ const Background = () => { ) } -function Page ({ children }) { +function Page({ children }) { return ( <> - +
- { children } + {children}