CCMeMan (Conference Call Meeting Manager) === An all-in-one platform (or portal) for meeting (con-call) management ## API ✅ : Highest priority to implement. - `/api/v1/users` - [ ] POST: ✅ Register current user - [ ] GET: ✅ Get current user - [ ] PUT: ✅ Update current user - [ ] DELETE: Delete current user Question: I'm not sure if it's better to use `/api/v1/users/me` or `api/v1/me` for `GET`, `PUT`, and `DELETE` requests on current user? - `/api/v1/groups` - [ ] GET: ✅ Get group list (that current user participated in) - [ ] POST: ✅ Create a group (set current user to be a MANAGER of the new sub-group) - `/api/v1/groups/:groupNanoId` - [ ] POST: Create a sub-group (set current user to be a MANAGER of the new sub-group) - [ ] GET: ✅ Get the group - [ ] PUT: Update the group (current user must be a MANAGER of this group) - [ ] DELETE: Delete the group (current user must be a MANAGER of this group) - `/api/v1/meetings/:groupNanoId` - [ ] POST: ✅ Create meeting (current user must be a MANAGER/MEMBER of this group) - [ ] GET: ✅ Get meeting list (that current user participated in) - `?future=true` : Return future meetings only - `?future=false` : Return past meetings only - `?sortBy=time` : Sort by meeting time - `/api/v1/meetings?meeting-nano-id=<meetingNanoId>` - [x] GET: ✅ Get the meeting (if current user participated in) - [ ] PUT: Update the meeting (current user must be a MANAGER/MEMBER of this meeting) - [ ] DELETE: Delete the meeting (current user must be a MANAGER of this meeting) --- Voting for Meeting time (I'm not sure which way is better) - `/api/v1/votings/:groupNanoId/:meetingNanoId` - `/api/v1/meetings/:groupNanoId/:meetingNanoId/voting` 😁 --- (I'm not sure if this is a good way to quit from a group/meeting) - `/api/v1/me/meetings/:groupNanoId/:meetingNanoId` - [ ] DELETE: Quit the meeting - `/api/v1/me/groups/:groupNanoId` - [ ] DELETE: Quit the group --- (I'm not sure how to add a user to a group/meeting as a MANAGER/MEMBER/GUEST) Could be ``` PATCH /api/v1/groups/:groupNanoId { op: "add|remove", data: { users: { id: <userId>, role:<Role> } } } ``` ? ## Premission Matrix Create Read Update Delete ### Operations on group - After creation, the user will be assigned as a MANAGER to the new group by default. | Group | Manager | Member | Guest | None | | ---------- | ------- | ------ | ----- | ---- | | Operations | CRUD | CRU | R | | ### Operations on sub-group - **C**: To Create a new sub-group under a group, you have to be either MANAGER or MEMBER of that parent group. - After creation, the user will be assigned as a MANAGER to the new sub-group by default. | Sub-Group \ Group | Manager | Member | Guest | None | | ----------------- | ------- | ------ | ----- | ---- | | Manager | CRUD | CRUD | RUD | RUD | | Member | CRUD | CRU | RU | RU | | Guest | CRD | CR | R | R | | None | CD | C | | | ### Operations on meeting - **C**: To Create a new meeting under a group, you have to be either MANAGER or MEMBER of that group. - If a user doesn't participated in the parent group of the meeting (i.e., **None**), they can only access the meeting page from a direct link ( `meeting/<groupNanoId>/<meetingNanoId>` ) in the front-end. It won't be browsable from a list of meetings in a group page. - After creation, the user will be assigned as a MANAGER to the new meeting by default. | Meeting \ Group | Manager | Member | Guest | None (direct link only) | | ----------------------- | ------- | ------ | ----- | ----------------------- | | Manager | CRUD | CRUD | RUD | RUD | | Member | CRUD | CRU | RU | RU | | Guest | CRD | CR | R | R | | None (direct link only) | CD | C | | | - Example code: ```javascript // in meetings/<groupNanoId>/<meetingNanoId> const READ = 1; const UPDATE = 2; const DELETE = 4; let premission = 0; if (isManager) { premission |= READ+UPDATE+DELETE }; if (isMember) { premission |= READ+UPDATE }; if (isGuest) { premission |= READ }; if (isGroupManager) { premission |= DELETE }; ``` ## Features ### Core Feature - Create Group - manage members (name/email) - one-time meeting - repeated meetings - Scheduling (support multiple timezone display) - Voting for time slots (ref: https://www.when2meet.com/) - Time Selector - React Schedule Selector - https://github.com/bibekg/react-schedule-selector#readme - https://medium.com/bruinmeet/react-schedule-selector-6cd5bf1f4968 - DateTime Picker - https://github.com/its-danny/use-lilius - https://github.com/msnegurski/tailwind-react-datepicker - https://github.com/Hacker0x01/react-datepicker/ - https://gist.github.com/igoro00/99e9d244677ccafbf39667c24b5b35ed - Zoom link shortcut - Add to calendar (iCal/Google...) - https://github.com/add2cal/add-to-calendar-button - Generate invitation email - Plan text for copy/paste - SendGrid (SMTP)? ### Additional Features - Minutes - markdown (hackmd.io or CodiMD) - Google Docs (just link or integration) - Any external links to shared documents services - Batch exporting - Google Drive - Github/Gitlab private repo - Local files ### Medium-term Feature - iOS app (swift vs react native) - macOS app ### Long-term Feature 太遠了先別想 ## Database schema? - https://github.com/CCMeMan/ccmeman-backend/blob/dev/src/prisma/schema.prisma ``` person: id name email preferred time zone group: id admin(person) members(person) Meetings(meeting) MeetingGroups(meeting_group) meeting_group: id Meetings(meeting) meeting: id Name DateTime Con-call link minutes ``` ## Possible things to learn ### Front-end - React.js - CSS - Bootstrap - Tailwind - Material UI - Figma (UI/UX designer) ### Back-end - Node.js? ### DB - ~~GraphQL vs SQL?~~ Using ORM - MongoDB vs PostgreSQL? - ORM (Object-Relational Mapping) - Prisma - Sequelize - MikroORM ### Authentication - ~~OAuth? Google (and probably the fucking Office365)~~ - Firebase Auth ### Hosting - Linode / Vultr / Digital Ocean - Firebase - GCP (free VM)? ### Deployment - Docker + K8S - Web App hosting ### Realtime Colabration - Survey - https://stackoverflow.com/questions/2043165/operational-transformation-library - https://bestofjs.org/projects?tags=realtime&tags=db - CRDT (Commutative Replicated Data Type) - https://github.com/automerge/automerge - https://github.com/yjs/yjs - OT ( Operational Transformation) - https://github.com/share/sharedb --- # Learning ## Authentication and Authorization - JWT(JSON Web Tokens) - Basic: https://youtu.be/7Q17ubqLfaM - Tutorial: https://youtu.be/mbsmsi7l3r4 - Display JWT: https://jwt.io/ - https://next-auth.js.org/ - Firebase - Google Identity Services Login with React ✅ - https://www.youtube.com/watch?v=roxC8SMs7HU - https://yeeeeees.medium.com/user-authentication-with-node-js-jwt-and-google-oauth-2-0-backend-cookbook-5-e54f40dce0e5 - passport.js - Auth0 - JWT - https://community.auth0.com/t/auth0-nextjs-auth0-tokens-and-external-api-help/71072 - AWS Cognito ## API - https://learn.microsoft.com/en-us/azure/architecture/best-practices/api-design ### Documenting API - https://dev.to/kabartolo/how-to-document-an-express-api-with-swagger-ui-and-jsdoc-50do ### How to communicate - https://stackoverflow.com/questions/66739797/how-to-handle-a-post-request-in-next-js ## ID - https://npmtrends.com/cuid-vs-nanoid-vs-short-uuid-vs-uuid ## Search - Elasticsearch (ELK) ## Database - GUI Viewer - DBeaver https://dbeaver.io/ ### PostgreSQL - Setup using Docker - https://zhao-li.medium.com/getting-started-with-postgresql-using-docker-compose-34d6b808c47c ### Prisma - Learn Prisma In 60 Minutes https://youtu.be/RebA5J-rlwg ## Theme - https://chakra-ui.com/docs/components - How to use Tailwind with Chakra-UI https://github.com/chakra-ui/chakra-ui/issues/634 - Toast https://github.com/tailwindlabs/headlessui/discussions/439 - !!!❤️ https://chakra-ui.com/docs/components/toast#custom-component ## JavaScript - Package Selection - https://npmtrends.com/ - Async/Await - Promise - JavaScript Promises In 10 Minutes https://youtu.be/DHvZLI7Db8E - JavaScript Async Await https://youtu.be/V_Kr9OSfDeU - https://stackoverflow.com/questions/43302584/why-doesnt-the-code-after-await-run-right-away-isnt-it-supposed-to-be-non-blo - Destructuring Assignment ```javascript const obj = { a: 1, b: 2 }; const { a, b } = obj; // is equivalent to: // const a = obj.a; // const b = obj.b; ``` - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment - import - Named import - import A, { myA, Something } from './A' - https://stackoverflow.com/questions/36795819/when-should-i-use-curly-braces-for-es6-import - Scope: @XXXX/YYY - https://stackoverflow.com/questions/36293481/use-of-symbol-in-node-module-names ### React.js/Next.js - Documentation - https://beta.reactjs.org/ - React Hooks - https://reactjs.org/docs/hooks-reference.html - `useContext` https://beta.reactjs.org/apis/react/useContext - `useState` - Why React Hook useState uses const and not let https://stackoverflow.com/questions/58860021/why-react-hook-usestate-uses-const-and-not-let - `useRef` vs `useState` If you don’t want to update DOM elements but want to get a value (having a state in component), you can go with useRef as an alternative to useState. - Render fetched data - https://devtrium.com/posts/async-functions-useeffect - https://stackoverflow.com/questions/49938266/how-to-return-values-from-async-functions-using-async-await-from-function - https://daveceddia.com/react-before-render/