├── .changeset ├── README.md └── config.json ├── .dockerignore ├── .eslintrc.js ├── .github └── workflows │ └── build.yml ├── .gitignore ├── .husky └── pre-commit ├── .lintstagedrc.json ├── .npmrc ├── .prettierrc.js ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── _tmp_20316_7dc36939daf51af4aa2d17bfc8441de8 ├── apps ├── core-admin │ ├── .dockerignore │ ├── .eslintrc.js │ ├── CHANGELOG.md │ ├── Dockerfile │ ├── package.json │ ├── src │ │ ├── controllers │ │ │ ├── attributes.ts │ │ │ ├── events.ts │ │ │ ├── extras.ts │ │ │ ├── mail.ts │ │ │ ├── organizations.ts │ │ │ ├── participants.ts │ │ │ ├── registration.ts │ │ │ └── users.ts │ │ ├── index.ts │ │ ├── middlewares │ │ │ ├── auth0.ts │ │ │ ├── authorization.ts │ │ │ └── validateParams.ts │ │ ├── routes.ts │ │ ├── unprotectedRoutes.ts │ │ └── utils │ │ │ ├── database.ts │ │ │ ├── supabase.ts │ │ │ └── supabaseTypes.ts │ ├── tests │ │ └── routes │ │ │ ├── auth.route.test.ts │ │ │ ├── event.route.test.ts │ │ │ ├── onsite.route.ts │ │ │ └── organization.route.test.ts │ ├── tsconfig.json │ └── webpack.config.js ├── core-auth0-actions │ ├── .dockerignore │ ├── .eslintrc.js │ ├── CHANGELOG.md │ ├── Dockerfile │ ├── README.md │ ├── package.json │ ├── src │ │ ├── controllers │ │ │ ├── newuser.ts │ │ │ └── user.ts │ │ ├── index.ts │ │ ├── middlewares │ │ │ └── auth.ts │ │ └── utils │ │ │ └── database.ts │ ├── tsconfig.json │ └── webpack.config.js ├── core-mailer │ ├── .dockerignore │ ├── .eslintrc.js │ ├── CHANGELOG.md │ ├── Dockerfile │ ├── package.json │ ├── src │ │ ├── config │ │ │ ├── NodeMailer.ts │ │ │ └── Redis.ts │ │ ├── controllers │ │ │ └── MailController.ts │ │ ├── index.ts │ │ ├── middlewares │ │ │ └── auth.ts │ │ ├── models │ │ │ └── Mail.ts │ │ └── services │ │ │ └── MailService.ts │ ├── tsconfig.json │ └── webpack.config.js ├── registration-admin │ ├── .dockerignore │ ├── .gitignore │ ├── Dockerfile │ ├── README.md │ ├── eslint.config.js │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── vite.svg │ ├── src │ │ ├── App.tsx │ │ ├── hooks │ │ │ ├── useAlert.tsx │ │ │ └── useFetch.tsx │ │ ├── index.css │ │ ├── main.tsx │ │ ├── pages │ │ │ ├── Form.tsx │ │ │ ├── NotFound.tsx │ │ │ ├── Registered.tsx │ │ │ └── RegistrationClosed.tsx │ │ └── vite-env.d.ts │ ├── tsconfig.app.json │ ├── tsconfig.app.tsbuildinfo │ ├── tsconfig.json │ ├── tsconfig.node.json │ ├── tsconfig.node.tsbuildinfo │ ├── vite.config.ts │ ├── vite.config.ts.timestamp-1729018167203-e533c06553425.mjs │ ├── vite.config.ts.timestamp-1729269251856-984e362a25cee.mjs │ └── vite.config.ts.timestamp-1734261834064-a590afbf0f3ab.mjs └── web-admin │ ├── .dockerignore │ ├── .eslintrc.json │ ├── .gitignore │ ├── CHANGELOG.md │ ├── Dockerfile │ ├── README.md │ ├── components.json │ ├── jsconfig.json │ ├── next.config.js │ ├── package.json │ ├── postcss.config.js │ ├── public │ ├── QrTemplate.txt │ ├── ThisIsAnORG.png │ ├── assets │ │ └── events │ │ │ ├── Adduser.png │ │ │ ├── Box.png │ │ │ ├── Heartbeat.png │ │ │ ├── ProgressBar.png │ │ │ └── Users.png │ ├── next.svg │ └── vercel.svg │ ├── src │ ├── assets │ │ ├── Dark.png │ │ └── Light.png │ ├── components │ │ ├── AddParticipant.jsx │ │ ├── CertificateUploadBox.jsx │ │ ├── ComingSoon.jsx │ │ ├── DataDisplay.jsx │ │ ├── DataDisplayNew.jsx │ │ ├── EventsDisplay.jsx │ │ ├── ItemCard.jsx │ │ ├── MarkdownEditor.jsx │ │ ├── MultiFormEmail.jsx │ │ ├── NewCalendar.jsx │ │ ├── ProtectedRoute.jsx │ │ ├── Scanner.jsx │ │ ├── Sidebar.jsx │ │ ├── landing │ │ │ ├── assets │ │ │ │ ├── images │ │ │ │ │ └── dash_frame.png │ │ │ │ ├── index.js │ │ │ │ └── logo │ │ │ │ │ ├── logo+text.svg │ │ │ │ │ └── logo.svg │ │ │ ├── components │ │ │ │ ├── calltoaction.jsx │ │ │ │ ├── footer.jsx │ │ │ │ └── navbar.jsx │ │ │ ├── index.jsx │ │ │ └── theme.js │ │ ├── modals │ │ │ ├── AddParticipantModal │ │ │ │ └── AddParticipant.jsx │ │ │ ├── Check-Out-Scanner │ │ │ │ └── index.jsx │ │ │ ├── Check-OutParticipant │ │ │ │ └── index.jsx │ │ │ ├── Check-in-Scanner │ │ │ │ └── index.jsx │ │ │ ├── Check-inParticipant │ │ │ │ └── index.jsx │ │ │ ├── EditModal_of_Settings │ │ │ │ └── OrganizationSettingsModal.js │ │ │ ├── MultiStageScanner │ │ │ │ └── index.jsx │ │ │ └── SendEmailWithQRModal │ │ │ │ └── MultiFormEmail.jsx │ │ └── ui │ │ │ ├── StyledComponents.jsx │ │ │ ├── button.jsx │ │ │ └── fonts.jsx │ ├── contexts │ │ └── MyContext.jsx │ ├── hooks │ │ ├── useAccessToken.jsx │ │ ├── useAlert.jsx │ │ ├── useDebounce.jsx │ │ ├── useFetch.jsx │ │ ├── useLocalStorage.jsx │ │ ├── useRequests.jsx │ │ ├── useTimeout.jsx │ │ └── useWrapper.jsx │ ├── layouts │ │ ├── DashboardLayout.jsx │ │ └── OrganizationSettingsModal.js │ ├── lib │ │ └── utils.js │ ├── pages │ │ ├── 404.jsx │ │ ├── CustomStyledBox.jsx │ │ ├── [orgId] │ │ │ ├── emailer │ │ │ │ └── index.jsx │ │ │ ├── events │ │ │ │ ├── [eventId] │ │ │ │ │ ├── attributes │ │ │ │ │ │ ├── [attributeId] │ │ │ │ │ │ │ └── index.jsx │ │ │ │ │ │ ├── index.jsx │ │ │ │ │ │ └── new │ │ │ │ │ │ │ └── index.jsx │ │ │ │ │ ├── extras │ │ │ │ │ │ ├── [extraId] │ │ │ │ │ │ │ ├── check-in │ │ │ │ │ │ │ │ ├── index.jsx │ │ │ │ │ │ │ │ └── scanner │ │ │ │ │ │ │ │ │ └── index.jsx │ │ │ │ │ │ │ └── index.jsx │ │ │ │ │ │ ├── index.jsx │ │ │ │ │ │ └── new │ │ │ │ │ │ │ └── index.jsx │ │ │ │ │ ├── index.jsx │ │ │ │ │ ├── navigationmenu.jsx │ │ │ │ │ ├── participants │ │ │ │ │ │ ├── [participantId] │ │ │ │ │ │ │ ├── attributes │ │ │ │ │ │ │ │ ├── index.jsx │ │ │ │ │ │ │ │ └── set │ │ │ │ │ │ │ │ │ └── index.jsx │ │ │ │ │ │ │ ├── edit │ │ │ │ │ │ │ │ └── index.jsx │ │ │ │ │ │ │ └── index.jsx │ │ │ │ │ │ ├── check-in │ │ │ │ │ │ │ ├── in │ │ │ │ │ │ │ │ ├── index.jsx │ │ │ │ │ │ │ │ └── scanner │ │ │ │ │ │ │ │ │ └── index.jsx │ │ │ │ │ │ │ ├── index.jsx │ │ │ │ │ │ │ ├── multi-in │ │ │ │ │ │ │ │ └── index.jsx │ │ │ │ │ │ │ └── out │ │ │ │ │ │ │ │ ├── index.jsx │ │ │ │ │ │ │ │ └── scanner │ │ │ │ │ │ │ │ └── index.jsx │ │ │ │ │ │ ├── index.jsx │ │ │ │ │ │ └── new │ │ │ │ │ │ │ ├── index.jsx │ │ │ │ │ │ │ └── upload-csv │ │ │ │ │ │ │ └── index.jsx │ │ │ │ │ ├── paymentstatus │ │ │ │ │ │ └── index.jsx │ │ │ │ │ └── registrants │ │ │ │ │ │ └── index.jsx │ │ │ │ ├── index.jsx │ │ │ │ └── new │ │ │ │ │ └── index.jsx │ │ │ ├── index.jsx │ │ │ ├── members │ │ │ │ ├── index.jsx │ │ │ │ └── new │ │ │ │ │ └── index.jsx │ │ │ ├── mycertificates │ │ │ │ └── index.jsx │ │ │ ├── profile │ │ │ │ └── index.jsx │ │ │ └── settings │ │ │ │ └── index.jsx │ │ ├── _app.jsx │ │ ├── _document.jsx │ │ ├── index.jsx │ │ └── onboarding │ │ │ └── verify-email │ │ │ └── index.jsx │ ├── styles │ │ └── globals.css │ └── utils │ │ ├── Rectangle 1.svg │ │ ├── logo.jsx │ │ ├── prisma.js │ │ └── rectangle.svg │ └── tailwind.config.js ├── package.json ├── packages ├── auth0-flows │ ├── CHANGELOG.md │ ├── package-lock.json │ ├── package.json │ └── src │ │ └── add-user-to-database │ │ └── index.js ├── database │ ├── .env.example │ ├── .gitignore │ ├── CHANGELOG.md │ ├── index.ts │ ├── package-lock.json │ ├── package.json │ └── prisma │ │ ├── migrations │ │ ├── 20240229052022_init │ │ │ └── migration.sql │ │ ├── 20240229114739_ │ │ │ └── migration.sql │ │ ├── 20241025092424_registration │ │ │ └── migration.sql │ │ └── migration_lock.toml │ │ └── schema.prisma ├── eslint-config-custom │ ├── index.js │ ├── package.json │ └── tsconfig.json └── tsconfig │ ├── package-lock.json │ ├── package.json │ └── tsconfig.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml └── turbo.json /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json", 3 | "changelog": "@changesets/cli/changelog", 4 | "commit": false, 5 | "fixed": [], 6 | "linked": [], 7 | "access": "restricted", 8 | "baseBranch": "main", 9 | "updateInternalDependencies": "patch", 10 | "ignore": [] 11 | } 12 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | **/build 3 | **/dist 4 | **/.env* -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | // This tells ESLint to load the config from the package `eslint-config-custom` 4 | extends: ['custom'], 5 | settings: { 6 | next: { 7 | rootDir: ['apps/*/'], 8 | }, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build and Push Docker Image 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | build-and-push-core-admin: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | - run: | 15 | docker build -t techno-event-core-admin -f apps/core-admin/Dockerfile . 16 | 17 | - run: | 18 | docker login -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PASSWORD }} 19 | - run: | 20 | docker tag techno-event-core-admin ${{ secrets.DOCKER_USER }}/techno-event-core-admin:latest 21 | docker push ${{ secrets.DOCKER_USER }}/techno-event-core-admin:latest 22 | 23 | redeploy-core-admin: 24 | needs: build-and-push-core-admin 25 | runs-on: ubuntu-latest 26 | steps: 27 | - name: Call deploy hook 28 | run: | 29 | curl -X GET ${{ secrets.DEV_CORE_ADMIN_DEPLOY_HOOK }} 30 | curl -X GET ${{ secrets.PROD_CORE_ADMIN_DEPLOY_HOOK }} 31 | 32 | build-and-push-core-mailer: 33 | runs-on: ubuntu-latest 34 | 35 | steps: 36 | - uses: actions/checkout@v2 37 | - run: | 38 | docker build -t techno-event-core-mailer -f apps/core-mailer/Dockerfile . 39 | 40 | - run: | 41 | docker login -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PASSWORD }} 42 | - run: | 43 | docker tag techno-event-core-mailer ${{ secrets.DOCKER_USER }}/techno-event-core-mailer:latest 44 | docker push ${{ secrets.DOCKER_USER }}/techno-event-core-mailer:latest 45 | 46 | redeploy-core-mailer: 47 | needs: build-and-push-core-mailer 48 | runs-on: ubuntu-latest 49 | steps: 50 | - name: Call deploy hook 51 | run: | 52 | curl -X GET ${{ secrets.DEV_CORE_MAILER_DEPLOY_HOOK }} 53 | curl -X GET ${{ secrets.PROD_CORE_MAILER_DEPLOY_HOOK }} 54 | 55 | build-and-push-core-auth0-actions: 56 | runs-on: ubuntu-latest 57 | 58 | steps: 59 | - uses: actions/checkout@v2 60 | - run: | 61 | docker build -t techno-event-core-auth0-actions -f apps/core-auth0-actions/Dockerfile . 62 | 63 | - run: | 64 | docker login -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PASSWORD }} 65 | - run: | 66 | docker tag techno-event-core-auth0-actions ${{ secrets.DOCKER_USER }}/techno-event-core-auth0-actions:latest 67 | docker push ${{ secrets.DOCKER_USER }}/techno-event-core-auth0-actions:latest 68 | 69 | redeploy-core-auth0-actions: 70 | needs: build-and-push-core-auth0-actions 71 | runs-on: ubuntu-latest 72 | steps: 73 | - name: Call deploy hook 74 | run: | 75 | curl -X GET ${{ secrets.DEV_CORE_AUTH0_ACTIONS_DEPLOY_HOOK }} 76 | curl -X GET ${{ secrets.PROD_CORE_AUTH0_ACTIONS_DEPLOY_HOOK }} 77 | 78 | build-and-push-registration-admin: 79 | runs-on: ubuntu-latest 80 | 81 | steps: 82 | - uses: actions/checkout@v2 83 | - run: | 84 | docker build -t registration-admin -f apps/registration-admin/Dockerfile . 85 | 86 | - run: | 87 | docker login -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PASSWORD }} 88 | - run: | 89 | docker tag registration-admin ${{ secrets.DOCKER_USER }}/registration-admin:latest 90 | docker push ${{ secrets.DOCKER_USER }}/registration-admin:latest 91 | 92 | build-and-push-web-admin: 93 | runs-on: ubuntu-latest 94 | 95 | steps: 96 | - uses: actions/checkout@v2 97 | - run: | 98 | docker build -t web-admin -f apps/web-admin/Dockerfile . 99 | 100 | - run: | 101 | docker login -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PASSWORD }} 102 | - run: | 103 | docker tag web-admin ${{ secrets.DOCKER_USER }}/web-admin:latest 104 | docker push ${{ secrets.DOCKER_USER }}/web-admin:latest 105 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | node_modules 3 | .pnp 4 | .pnp.js 5 | 6 | # docs 7 | docs 8 | 9 | # testing 10 | coverage 11 | 12 | # build files 13 | build 14 | dist 15 | 16 | # misc 17 | .DS_Store 18 | *.pem 19 | 20 | # debug 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | # local env files 26 | .env* 27 | !.env.example 28 | 29 | # ide 30 | .vim 31 | .eclipse 32 | .atom 33 | .idea 34 | .vscode 35 | 36 | # turbo 37 | .turbo 38 | 39 | # vercel 40 | .vercel -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | # pnpm dlx lint-staged 5 | -------------------------------------------------------------------------------- /.lintstagedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "*.ts": ["prettier --write", "eslint"], 3 | "*.js": ["prettier --write", "eslint"], 4 | "*.tsx": ["prettier --write", "eslint"], 5 | "*.jsx": ["prettier --write", "eslint"], 6 | "*.css": ["prettier --write", "stylelint"] 7 | } 8 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | auto-install-peers = true 2 | public-hoist-pattern[] = *prisma* -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: true, 3 | trailingComma: 'all', 4 | singleQuote: true, 5 | printWidth: 100, 6 | tabWidth: 2, 7 | }; 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

Techno Event Management

3 |

A powerful event management suite.

4 |
5 | 6 |
7 | techno-event-management.vercel.app 8 |
9 | 10 |
11 | 12 |
13 | 14 | GitHub Repo stars 15 | 16 | 17 | AGPL v3 license 18 | 19 | 20 | 21 |
22 | 23 |
24 | 25 | Techno Event Management is an event management suite which helps with managing all aspects of an event from registration to tracking participants during the event and more. 26 | 27 | ## Features 28 | 29 | - **Real-time participant tracking:** Track all the participants using a qr code during the event. 30 | - **Analytics:** Get insights about the event. 31 | 32 | 35 | 36 | ## Getting Started 37 | 38 | ### Prerequisites 39 | 40 | Here's what you need to be able to run Techno Event: 41 | 42 | - Node.js (version >= 18) 43 | 44 | ### 1. Clone the repository 45 | 46 | ```shell 47 | git clone https://github.com/iedcmec/techno-event-management.git 48 | cd techno-event-management 49 | ``` 50 | 51 | ### 2. Install npm dependencies 52 | 53 | ```shell 54 | pnpm install 55 | ``` 56 | 57 | ### 3. Set up the development environment 58 | 59 | ```shell 60 | pnpm run setup 61 | ``` 62 | 63 | ### 4. Run the dev server 64 | 65 | ```shell 66 | pnpm run dev 67 | ``` 68 | 69 | ### 4.1 Only run the frontend dev server 70 | 71 | ```shell 72 | cd apps/web-admin 73 | pnpm run dev 74 | ``` 75 | 76 | ### 4.2 Only run the backend dev server 77 | 78 | ```shell 79 | cd apps/core-admin 80 | pnpm run dev 81 | ``` 82 | 83 | ### 5. Open the app in your browser 84 | 85 | Visit [http://localhost:3000](http://localhost:3000) in your browser. 86 | 87 | ## Contributing 88 | 89 | We welcome contributions from the community! If you're interested in contributing to Techno Event Management, please read our [Contribution Guidelines](https://github.com/IEDCMEC/techno-event-management/wiki/Contributing) to get started. 90 | 91 | For major changes, please open an issue first to discuss what you would like to change. 92 | 93 | ### Our Contributors ✨ 94 | 95 | 96 | 97 | 98 | 99 | # License 100 | 101 | Released under the [AGPL v3 license](LICENSE). 102 | -------------------------------------------------------------------------------- /_tmp_20316_7dc36939daf51af4aa2d17bfc8441de8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IEDCMEC/techno-event-management/b97f2f3cf8e9cf3ec8b4d8ac32167fc9e4f5393e/_tmp_20316_7dc36939daf51af4aa2d17bfc8441de8 -------------------------------------------------------------------------------- /apps/core-admin/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build -------------------------------------------------------------------------------- /apps/core-admin/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: ['custom'], 4 | }; 5 | -------------------------------------------------------------------------------- /apps/core-admin/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # techno-event-core-admin 2 | 3 | ## 0.1.0 4 | 5 | ### Minor Changes 6 | 7 | - Initialise changeset 8 | 9 | ### Patch Changes 10 | 11 | - Updated dependencies 12 | - database@0.1.0 13 | -------------------------------------------------------------------------------- /apps/core-admin/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-alpine3.16 AS builder 2 | RUN apk add --no-cache libc6-compat 3 | RUN apk update 4 | 5 | WORKDIR /app 6 | 7 | RUN npm install -g pnpm turbo 8 | 9 | COPY ../../. . 10 | 11 | RUN apk add --no-cache \ 12 | build-base \ 13 | cairo-dev \ 14 | pango-dev \ 15 | jpeg-dev \ 16 | giflib-dev \ 17 | librsvg \ 18 | python3 19 | 20 | ENV PYTHON=/usr/bin/python3 21 | 22 | RUN pnpm install 23 | 24 | RUN pnpm run build --filter=techno-event-core-admin... 25 | 26 | FROM node:18-alpine3.16 27 | 28 | RUN apk --no-cache add openssl 29 | 30 | COPY --from=builder /app/apps/core-admin/dist . 31 | COPY --from=builder /app/node_modules/.pnpm/@prisma+client*/node_modules/.prisma/client/*.node . 32 | 33 | ENV PORT=80 34 | 35 | CMD ["app.js"] 36 | -------------------------------------------------------------------------------- /apps/core-admin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "techno-event-core-admin", 3 | "version": "0.1.0", 4 | "description": "", 5 | "main": "build/index.js", 6 | "scripts": { 7 | "build": "npm run clean & webpack --config webpack.config.js", 8 | "clean": "rimraf dist & rimraf build", 9 | "dev": "concurrently \"npx tsc --watch\" \"nodemon -q build/index.js\"", 10 | "setup": "node -e \"require('fs').copyFile('.env.example', '.env', (err) => {if (err) {//console.log(err);} else {//console.log('Environment variables set up successfully');}});\"", 11 | "start": "node dist/app.js", 12 | "test": "mocha -r ts-node/register 'tests/**/*.ts' --timeout 10000 --exit", 13 | "lint": "eslint --ext .ts src" 14 | }, 15 | "keywords": [], 16 | "author": "", 17 | "license": "ISC", 18 | "dependencies": { 19 | "@supabase/supabase-js": "^2.46.1", 20 | "@typescript-eslint/parser": "6.0.0", 21 | "axios": "^1.5.1", 22 | "bcryptjs": "^2.4.3", 23 | "body-parser": "^1.20.2", 24 | "chalk": "^5.3.0", 25 | "cors": "^2.8.5", 26 | "database": "workspace:*", 27 | "dotenv": "^16.0.3", 28 | "express": "^4.18.2", 29 | "express-oauth2-jwt-bearer": "^1.6.0", 30 | "form-data": "^4.0.1", 31 | "jsonwebtoken": "^9.0.0", 32 | "marked": "^15.0.0", 33 | "passport": "^0.7.0", 34 | "passport-local": "^1.0.0", 35 | "pg": "^8.11.0", 36 | "uuid": "^9.0.0" 37 | }, 38 | "devDependencies": { 39 | "@types/chai": "^4.3.5", 40 | "@types/express": "^4.17.17", 41 | "@types/mocha": "^10.0.1", 42 | "@types/node": "^20.10.5", 43 | "@types/pg": "^8.10.1", 44 | "@typescript-eslint/eslint-plugin": "^6.7.3", 45 | "chai": "^4.3.7", 46 | "chai-http": "^4.3.0", 47 | "concurrently": "^8.0.1", 48 | "eslint": "^8.56.0", 49 | "eslint-config-custom": "*", 50 | "eslint-config-standard-with-typescript": "^43.0.0", 51 | "eslint-plugin-import": "^2.27.5", 52 | "eslint-plugin-n": "^16.5.0", 53 | "eslint-plugin-prettier": "^5.0.0", 54 | "eslint-plugin-promise": "^6.1.1", 55 | "mocha": "^10.2.0", 56 | "nodemon": "^3.0.1", 57 | "prettier": "3.1.1", 58 | "rimraf": "^5.0.1", 59 | "ts-loader": "^9.5.1", 60 | "tsconfig": "workspace:*", 61 | "typescript": "^5.0.4", 62 | "webpack": "^5.89.0", 63 | "webpack-cli": "^5.1.4" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /apps/core-admin/src/controllers/users.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | 3 | import prisma from '../utils/database'; 4 | 5 | export const fetchAccountDetails = async (req: Request, res: Response) => { 6 | try { 7 | const userId = req?.auth?.payload?.sub; 8 | 9 | if (!userId) { 10 | return res.status(401).json({ error: 'Unauthorized' }); 11 | } 12 | // //console.log(prisma.) 13 | const user = await prisma.user.findUnique({ 14 | where: { 15 | id: userId, 16 | }, 17 | }); 18 | 19 | if (!user) { 20 | return res.status(404).json({ error: 'User not found' }); 21 | } 22 | 23 | return res.status(200).json({ accountDetails: user }); 24 | } catch (err: any) { 25 | console.error(err); 26 | return res.status(500).json({ error: 'Something went wrong' }); 27 | } 28 | }; 29 | export const myCredential = async (req: Request, res: Response) => { 30 | try { 31 | const userId = req?.auth?.payload?.sub; 32 | // //console.log(userId); 33 | 34 | if (!userId) { 35 | return res.status(401).json({ error: 'Unauthorized' }); 36 | } 37 | // //console.log(prisma.) 38 | const userDetails = await prisma.organizationUser.findMany({ 39 | where: { 40 | userId: userId, // assuming userId is a unique identifier 41 | }, 42 | }); 43 | // //console.log(userDetails); 44 | if (userDetails) { 45 | return res.status(200).json({ data: userDetails }); // Return the details of the user 46 | } else { 47 | //console.log('User not found'); 48 | return res.status(404).json({ error: 'User not found' }); 49 | } 50 | } catch (err: any) { 51 | //console.log(err); 52 | return res.status(500).json({ error: 'Something went wrong' }); 53 | } 54 | }; 55 | export const updateAccountDetails = async (req: Request, res: Response) => { 56 | try { 57 | const userId = req?.auth?.payload?.sub; 58 | 59 | if (!userId) { 60 | return res.status(401).json({ error: 'Unauthorized' }); 61 | } 62 | 63 | if (userId !== req?.auth?.payload?.sub) { 64 | return res.status(403).json({ error: 'Forbidden' }); 65 | } 66 | 67 | const { firstName, lastName } = req.body; 68 | 69 | const accountDetails = await prisma.user.update({ 70 | where: { 71 | id: userId, 72 | }, 73 | data: { 74 | firstName, 75 | lastName, 76 | }, 77 | }); 78 | 79 | return res.status(200).json({ accountDetails }); 80 | } catch (err: any) { 81 | console.error(err); 82 | return res.status(500).json({ error: 'Something went wrong' }); 83 | } 84 | }; 85 | -------------------------------------------------------------------------------- /apps/core-admin/src/index.ts: -------------------------------------------------------------------------------- 1 | import express, { Express, Request, Response } from 'express'; 2 | import dotenv from 'dotenv'; 3 | import { validateUUID } from './middlewares/validateParams'; 4 | 5 | const bodyParser = require('body-parser'); 6 | const cors = require('cors'); 7 | 8 | dotenv.config(); 9 | 10 | const port: any = process.env.PORT; 11 | 12 | const app: Express = express(); 13 | 14 | const { auth } = require('express-oauth2-jwt-bearer'); 15 | 16 | const allowedOrigins = [ 17 | 'https://techno-event-management.vercel.app', 18 | 'http://localhost:3000', 19 | 'https://admin.eventsync.iedcmec.in', 20 | ]; 21 | 22 | app.use( 23 | cors({ 24 | origin: '*', 25 | methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], 26 | allowedHeaders: '*', // Allow all headers 27 | }), 28 | ); 29 | 30 | app.options('*', cors()); 31 | 32 | app.use( 33 | bodyParser.json({ 34 | limit: '50mb', 35 | }), 36 | ); 37 | 38 | const jwtCheck = auth({ 39 | audience: process.env.AUTH0_AUDIENCE, 40 | issuerBaseURL: process.env.AUTH0_ISSUER_BASE_URL, 41 | tokenSigningAlg: 'RS256', 42 | }); 43 | 44 | app.get('/', (req: Request, res: Response) => { 45 | return res.send('Techno Event Server'); 46 | }); 47 | 48 | app.get('/health', (req: Request, res: Response) => { 49 | const healthcheck: any = { 50 | resource: 'Techno Event Server', 51 | uptime: process.uptime(), 52 | responseTime: process.hrtime(), 53 | message: 'OK', 54 | timestamp: Date.now(), 55 | }; 56 | try { 57 | res.send(healthcheck); 58 | } catch (error) { 59 | healthcheck.message = error; 60 | res.status(503).send(); 61 | } 62 | }); 63 | 64 | app.param('eventId', validateUUID); 65 | 66 | import router from './routes'; 67 | import clientRouter from './unprotectedRoutes'; 68 | import { decodeUserInfo } from './middlewares/auth0'; 69 | 70 | app.use('/registration', clientRouter); 71 | 72 | app.use(jwtCheck); 73 | 74 | app.use('/core', decodeUserInfo, router); 75 | 76 | app.listen(port, () => { 77 | //console.log(`Server is running at http://localhost:${port}`); 78 | }); 79 | 80 | export default app; 81 | -------------------------------------------------------------------------------- /apps/core-admin/src/middlewares/auth0.ts: -------------------------------------------------------------------------------- 1 | import * as dotenv from 'dotenv'; 2 | import { NextFunction, Request, Response } from 'express'; 3 | import { auth, claimCheck, InsufficientScopeError } from 'express-oauth2-jwt-bearer'; 4 | 5 | dotenv.config(); 6 | 7 | export const validateAccessToken = auth({ 8 | issuerBaseURL: `https://${process.env.AUTH0_DOMAIN}`, 9 | audience: process.env.AUTH0_AUDIENCE, 10 | }); 11 | 12 | export const checkRequiredPermissions = (requiredPermissions: string[]) => { 13 | return (req: Request, res: Response, next: NextFunction) => { 14 | const permissionCheck = claimCheck((payload) => { 15 | const permissions = payload.permissions as string[]; 16 | 17 | const hasPermissions = requiredPermissions.every((requiredPermission) => 18 | permissions.includes(requiredPermission), 19 | ); 20 | 21 | if (!hasPermissions) { 22 | throw new InsufficientScopeError(); 23 | } 24 | 25 | return hasPermissions; 26 | }); 27 | 28 | permissionCheck(req, res, next); 29 | }; 30 | }; 31 | 32 | export const decodeUserInfo = (req: Request, res: Response, next: NextFunction) => { 33 | try { 34 | const userId = req?.auth?.payload?.sub; 35 | 36 | if (!userId) { 37 | console.error('User ID not found in the payload'); 38 | return res.status(500).send('Internal Server Error'); 39 | } 40 | 41 | next(); 42 | } catch (err) { 43 | console.error(err); 44 | return res.status(500).send('Internal Server Error'); 45 | } 46 | }; 47 | -------------------------------------------------------------------------------- /apps/core-admin/src/middlewares/authorization.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from '@prisma/client'; 2 | import { Request, Response, NextFunction } from 'express'; 3 | 4 | const prisma = new PrismaClient(); 5 | 6 | const validateUser = async (userId: any, organizationId: any) => { 7 | if (!userId) { 8 | console.error('User ID not found in the payload'); 9 | throw new Error('Internal Server Error'); 10 | } 11 | 12 | if (!organizationId) { 13 | console.error('Organization ID not found in the payload'); 14 | throw new Error('Internal Server Error'); 15 | } 16 | 17 | const organizationUser = await prisma.organizationUser.findFirst({ 18 | where: { 19 | userId: userId, 20 | organizationId: organizationId, 21 | }, 22 | }); 23 | 24 | if (!organizationUser) { 25 | throw new Error('Forbidden'); 26 | } 27 | 28 | return organizationUser; 29 | }; 30 | 31 | export const validateOrganizationUser = async (req: Request, res: Response, next: NextFunction) => { 32 | const userId = req?.auth?.payload?.sub; 33 | const orgId = req?.params?.orgId; 34 | try { 35 | await validateUser(userId, orgId); 36 | next(); 37 | } catch (err: any) { 38 | console.error(err); 39 | return res.status(403).json({ error: err.message }); 40 | } 41 | }; 42 | export const validateOrganizationAdmin = async ( 43 | req: Request, 44 | res: Response, 45 | next: NextFunction, 46 | ) => { 47 | const userId = req?.auth?.payload?.sub; 48 | const orgId = req?.params?.orgId; 49 | try { 50 | const organizationUser = await validateUser(userId, orgId); 51 | if (organizationUser.role !== 'ADMIN') { 52 | throw new Error('Forbidden'); 53 | } 54 | next(); 55 | } catch (err: any) { 56 | console.error(err); 57 | return res.status(403).json({ error: err.message }); 58 | } 59 | }; 60 | -------------------------------------------------------------------------------- /apps/core-admin/src/middlewares/validateParams.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response, NextFunction } from 'express'; 2 | 3 | const regex = 4 | /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/i; 5 | 6 | export function validateUUID(req: Request, res: Response, next: NextFunction, eventId: string) { 7 | if (!regex.test(eventId)) { 8 | return res.status(400).send('Invalid eventId: must be a valid UUID'); 9 | } 10 | 11 | next(); 12 | } 13 | -------------------------------------------------------------------------------- /apps/core-admin/src/unprotectedRoutes.ts: -------------------------------------------------------------------------------- 1 | import express, { Router } from 'express'; 2 | import { 3 | orgAndEventVerification, 4 | getFormAttributes, 5 | addFormResponse, 6 | } from './controllers/registration'; 7 | 8 | const router: Router = express.Router(); 9 | 10 | router.get('/', (req: any, res: any) => { 11 | try { 12 | return res.send('Hello World!'); 13 | } catch (err) { 14 | console.error(err); 15 | return res.status(500); 16 | } 17 | }); 18 | 19 | router.get('/:orgId/event/:eventId/verify', orgAndEventVerification); 20 | router.get('/:orgId/event/:eventId/attributes', getFormAttributes); 21 | router.post('/:orgId/event/:eventId/submit', addFormResponse); 22 | 23 | export default router; 24 | -------------------------------------------------------------------------------- /apps/core-admin/src/utils/database.ts: -------------------------------------------------------------------------------- 1 | const { PrismaClient } = require('database'); 2 | 3 | const prisma = new PrismaClient({ 4 | datasourceUrl: process.env.DATABASE_URL, 5 | }); 6 | 7 | export default prisma; 8 | -------------------------------------------------------------------------------- /apps/core-admin/src/utils/supabase.ts: -------------------------------------------------------------------------------- 1 | // supabaseClient.js 2 | 3 | import { createClient } from '@supabase/supabase-js'; 4 | import * as dotenv from 'dotenv'; 5 | import { Database } from './supabaseTypes'; 6 | dotenv.config(); 7 | 8 | const SUPABASE_URL = process.env.SUPABASE_URL; 9 | const SUPABASE_KEY = process.env.SUPABASE_KEY; 10 | 11 | const supabase = 12 | SUPABASE_URL !== undefined && 13 | SUPABASE_KEY !== undefined && 14 | createClient(SUPABASE_URL, SUPABASE_KEY); 15 | 16 | // module.exports = supabase; 17 | export default supabase; 18 | -------------------------------------------------------------------------------- /apps/core-admin/tests/routes/auth.route.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'mocha'; 2 | import chai, { expect } from 'chai'; 3 | import chaiHttp from 'chai-http'; 4 | import app from '../../src/index'; 5 | 6 | chai.use(chaiHttp); 7 | 8 | describe('Authentication API', () => { 9 | describe('POST /auth/signup', () => { 10 | it('should return 201 with a success message', (done) => { 11 | chai 12 | .request(app) 13 | .post('/auth/signup') 14 | .send({ 15 | firstName: 'AuthNewUserFirstName', 16 | lastName: 'AuthNewUserLastName', 17 | email: 'authnewuser@email.com', 18 | password: 'newpassword', 19 | }) 20 | .end((err, res) => { 21 | expect(res.status).to.equal(201); 22 | expect(res.body).to.have.property('message'); 23 | expect(res.body.message).to.equal('Successfully created new user'); 24 | done(); 25 | }); 26 | }); 27 | }); 28 | 29 | describe('POST /auth/signup with an existing email', () => { 30 | it('should return 409 with a success message', (done) => { 31 | chai 32 | .request(app) 33 | .post('/auth/signup') 34 | .send({ 35 | firstName: 'AuthNewUserFirstName', 36 | lastName: 'AuthNewUserLastName', 37 | email: 'authnewuser@email.com', 38 | password: 'newpassword', 39 | }) 40 | .end((err, res) => { 41 | expect(res.status).to.equal(409); 42 | expect(res.body).to.have.property('error'); 43 | expect(res.body.error).to.equal('User already exists'); 44 | done(); 45 | }); 46 | }); 47 | }); 48 | 49 | describe('POST /auth/login', () => { 50 | it('should return 200 with a success message and token', (done) => { 51 | chai 52 | .request(app) 53 | .post('/auth/login') 54 | .send({ 55 | email: 'authnewuser@email.com', 56 | password: 'newpassword', 57 | }) 58 | .end((err, res) => { 59 | expect(res.status).to.equal(200); 60 | expect(res.body).to.have.property('message'); 61 | expect(res.body.message).to.equal('Successfully logged in'); 62 | expect(res.body).to.have.property('token'); 63 | expect(res.body.token).to.be.a('string'); 64 | done(); 65 | }); 66 | }); 67 | }); 68 | }); 69 | -------------------------------------------------------------------------------- /apps/core-admin/tests/routes/onsite.route.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'mocha'; 2 | import chai, { expect } from 'chai'; 3 | import chaiHttp from 'chai-http'; 4 | import app from '../../src/index'; 5 | 6 | chai.use(chaiHttp); 7 | 8 | describe('Participants API', () => { 9 | describe('GET /onsite/participants', () => { 10 | it('should return a list of participants', (done) => { 11 | chai 12 | .request(app) 13 | .get('/onsite/participant') 14 | .end((err, res) => { 15 | expect(res.status).to.equal(200); 16 | expect(res.body).to.be.an('array'); 17 | expect(res.body.length).greaterThan(0); 18 | done(); 19 | }); 20 | }); 21 | }); 22 | 23 | // describe("GET /onsite/participants/:id", () => { 24 | // it("should return a participant", (done) => { 25 | // chai 26 | // .request(app) 27 | // .get("/onsite/participants/c599ed75-7300-446f-a1c5-2e52cb4ae41a") 28 | // .end((err, res) => { 29 | // expect(res.status).to.equal(200); 30 | // expect(res.body).to.be.an("object"); 31 | // done(); 32 | // }); 33 | // }); 34 | // }); 35 | }); 36 | -------------------------------------------------------------------------------- /apps/core-admin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./build/", 5 | "esModuleInterop": true 6 | }, 7 | "include": ["src/**/*"], 8 | "exclude": ["node_modules"] 9 | } 10 | -------------------------------------------------------------------------------- /apps/core-admin/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | target: 'node', 5 | entry: './src/index.ts', 6 | output: { 7 | path: path.resolve(__dirname, 'dist'), 8 | filename: 'app.js', 9 | }, 10 | mode: 'production', 11 | module: { 12 | rules: [ 13 | { 14 | test: /\.tsx?$/, 15 | use: 'ts-loader', 16 | exclude: /node_modules/, 17 | }, 18 | ], 19 | }, 20 | resolve: { 21 | extensions: ['.tsx', '.ts', '.js'], 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /apps/core-auth0-actions/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build -------------------------------------------------------------------------------- /apps/core-auth0-actions/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: ['custom'], 4 | }; 5 | -------------------------------------------------------------------------------- /apps/core-auth0-actions/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # core-auth0-actions 2 | 3 | ## 0.1.0 4 | 5 | ### Minor Changes 6 | 7 | - Initialise changeset 8 | 9 | ### Patch Changes 10 | 11 | - Updated dependencies 12 | - database@0.1.0 13 | -------------------------------------------------------------------------------- /apps/core-auth0-actions/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-alpine AS builder 2 | RUN apk add --no-cache libc6-compat 3 | RUN apk update 4 | 5 | WORKDIR /app 6 | 7 | RUN npm install -g pnpm turbo 8 | 9 | COPY ../../. . 10 | 11 | RUN apk add --no-cache \ 12 | build-base \ 13 | cairo-dev \ 14 | pango-dev \ 15 | jpeg-dev \ 16 | giflib-dev \ 17 | librsvg \ 18 | python3 19 | 20 | ENV PYTHON=/usr/bin/python3 21 | 22 | RUN pnpm install 23 | 24 | RUN pnpm run build --filter=core-auth0-actions... 25 | 26 | FROM node:18-alpine3.16 27 | 28 | RUN apk --no-cache add openssl 29 | 30 | COPY --from=builder /app/apps/core-auth0-actions/dist . 31 | COPY --from=builder /app/node_modules/.pnpm/@prisma+client*/node_modules/.prisma/client/*.node . 32 | 33 | ENV PORT=80 34 | 35 | CMD ["app.js"] 36 | -------------------------------------------------------------------------------- /apps/core-auth0-actions/README.md: -------------------------------------------------------------------------------- 1 | # core-auth0-actiions 2 | 3 | This is a collection of Auth0 actions that can be used to extend the functionality of Auth0. This project is not built 4 | and deployed as a standalone application. Instead, these actions have to be manually copied pasted into the Auth0 5 | dashboard under the appropriate action. Any changes to this project does not automatically reflect in the Auth0 6 | dashboard. The changes have to be manually copied over. 7 | -------------------------------------------------------------------------------- /apps/core-auth0-actions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "core-auth0-actions", 3 | "version": "0.1.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "npm run clean & webpack --config webpack.config.js", 8 | "clean": "rimraf dist & rimraf build", 9 | "dev": "concurrently \"npx tsc --watch\" \"nodemon -q build/index.js\"", 10 | "setup": "node -e \"require('fs').copyFile('.env.example', '.env', (err) => {if (err) {//console.log(err);} else {//console.log('Environment variables set up successfully');}});\"", 11 | "start": "node dist/app.js", 12 | "lint": "eslint --ext .ts src" 13 | }, 14 | "keywords": [], 15 | "author": "", 16 | "license": "ISC", 17 | "dependencies": { 18 | "@typescript-eslint/parser": "6.0.0", 19 | "body-parser": "^1.20.2", 20 | "cors": "^2.8.5", 21 | "database": "workspace:*", 22 | "dotenv": "^16.0.3", 23 | "express": "^4.18.2" 24 | }, 25 | "devDependencies": { 26 | "@types/chai": "^4.3.5", 27 | "@types/express": "^4.17.17", 28 | "@types/node": "^20.10.5", 29 | "@types/pg": "^8.10.1", 30 | "@typescript-eslint/eslint-plugin": "^6.7.3", 31 | "concurrently": "^8.0.1", 32 | "eslint": "^8.56.0", 33 | "eslint-config-custom": "*", 34 | "eslint-config-standard-with-typescript": "^43.0.0", 35 | "eslint-plugin-import": "^2.27.5", 36 | "eslint-plugin-n": "^16.5.0", 37 | "eslint-plugin-prettier": "^5.0.0", 38 | "eslint-plugin-promise": "^6.1.1", 39 | "nodemon": "^3.0.1", 40 | "prettier": "3.1.1", 41 | "rimraf": "^5.0.1", 42 | "ts-loader": "^9.5.1", 43 | "tsconfig": "workspace:*", 44 | "typescript": "^5.0.4", 45 | "webpack": "^5.89.0", 46 | "webpack-cli": "^5.1.4" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /apps/core-auth0-actions/src/controllers/newuser.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | 3 | import prisma from '../utils/database'; 4 | 5 | /** 6 | * Add new user to database on register. This is a must because Auth0 has no connection with our database. Not 7 | * performing this action will result in a mismatch between the user in Auth0 and the user in our database. 8 | * @param req 9 | * @param res 10 | */ 11 | export const addNewUserToDatabaseOnRegister = async (req: Request, res: Response) => { 12 | try { 13 | const { userId, email } = req.body; 14 | 15 | if (!userId || !email) { 16 | return res.status(400).send('Invalid Request'); 17 | } 18 | 19 | const newUser = await prisma.user.create({ 20 | data: { 21 | email: email, 22 | id: userId, 23 | }, 24 | }); 25 | 26 | if (!newUser) { 27 | return res.status(500).send('Error creating user'); 28 | } 29 | 30 | //console.log(`User ${userId} with email ${email} created`); 31 | 32 | return res.status(200).send(newUser); 33 | } catch (err) { 34 | console.error(err); 35 | return res.status(500); 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /apps/core-auth0-actions/src/controllers/user.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | 3 | import prisma from '../utils/database'; 4 | 5 | /** 6 | * Get user's permissions under an organization. This gets executed when the user logs in and Auth0 action for log in is 7 | * triggered. 8 | * @param req 9 | * @param res 10 | */ 11 | export const getUserPermissions = async (req: Request, res: Response) => { 12 | try { 13 | const { userId } = req.query; 14 | 15 | if (!userId) { 16 | return res.status(400).send('Invalid Request'); 17 | } 18 | 19 | const userPermissions = await prisma.user.findUnique({ 20 | where: { 21 | id: userId as string, 22 | }, 23 | select: { 24 | id: true, 25 | OrganizationUser: { 26 | select: { 27 | role: true, 28 | organizationId: true, 29 | }, 30 | }, 31 | }, 32 | }); 33 | 34 | if (!userPermissions) { 35 | return res.status(404).send('User not found'); 36 | } 37 | 38 | return res.status(200).json({ userPermissions }); 39 | } catch (err) { 40 | console.error(err); 41 | return res.status(500); 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /apps/core-auth0-actions/src/index.ts: -------------------------------------------------------------------------------- 1 | import express, { Request, Response } from 'express'; 2 | import dotenv from 'dotenv'; 3 | import { addNewUserToDatabaseOnRegister } from './controllers/newuser'; 4 | import { getUserPermissions } from './controllers/user'; 5 | import { authorize } from './middlewares/auth'; 6 | 7 | const bodyParser = require('body-parser'); 8 | const cors = require('cors'); 9 | dotenv.config(); 10 | 11 | const port = process.env.PORT || 80; 12 | 13 | const app = express(); 14 | app.use( 15 | cors({ 16 | origin: '*', 17 | }), 18 | ); 19 | app.use(bodyParser.json()); 20 | 21 | app.get('/health', (req: Request, res: Response) => { 22 | const healthcheck: any = { 23 | resource: 'Techno Auth0 Actions Server', 24 | uptime: process.uptime(), 25 | responseTime: process.hrtime(), 26 | message: 'OK', 27 | timestamp: Date.now(), 28 | }; 29 | try { 30 | res.send(healthcheck); 31 | } catch (error) { 32 | healthcheck.message = error; 33 | res.status(503).send(); 34 | } 35 | }); 36 | 37 | app.post('/api/auth/newuser', authorize, addNewUserToDatabaseOnRegister); 38 | app.get('/api/auth/get-user-permissions', authorize, getUserPermissions); 39 | 40 | app.listen(port, () => { 41 | //console.log(`Server is running at http://localhost:${port}`); 42 | }); 43 | -------------------------------------------------------------------------------- /apps/core-auth0-actions/src/middlewares/auth.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response, NextFunction } from 'express'; 2 | 3 | /** 4 | * Middleware to authorize requests reaching Auth0 actions. 5 | * @param req 6 | * @param res 7 | * @param next 8 | */ 9 | export const authorize = (req: Request, res: Response, next: NextFunction) => { 10 | const { authorization } = req.headers; 11 | 12 | if (!authorization) { 13 | return res.status(403).json({ 14 | message: 'No authorization header provided', 15 | }); 16 | } 17 | 18 | const token = authorization.split(' ')[1]; 19 | 20 | if (!token) { 21 | return res.status(403).json({ 22 | message: 'No token provided', 23 | }); 24 | } 25 | 26 | if (token !== process.env.AUTHORIZATION_SECRET) { 27 | return res.status(401).json({ 28 | message: 'Unauthorized', 29 | }); 30 | } 31 | 32 | next(); 33 | }; 34 | -------------------------------------------------------------------------------- /apps/core-auth0-actions/src/utils/database.ts: -------------------------------------------------------------------------------- 1 | const { PrismaClient } = require('database'); 2 | 3 | const prisma = new PrismaClient({ 4 | datasourceUrl: process.env.DATABASE_URL, 5 | }); 6 | 7 | export default prisma; 8 | -------------------------------------------------------------------------------- /apps/core-auth0-actions/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./build/", 5 | "esModuleInterop": true 6 | }, 7 | "include": ["src/**/*"], 8 | "exclude": ["node_modules"] 9 | } 10 | -------------------------------------------------------------------------------- /apps/core-auth0-actions/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | target: 'node', 5 | entry: './src/index.ts', 6 | output: { 7 | path: path.resolve(__dirname, 'dist'), 8 | filename: 'app.js', 9 | }, 10 | mode: 'production', 11 | module: { 12 | rules: [ 13 | { 14 | test: /\.tsx?$/, 15 | use: 'ts-loader', 16 | exclude: /node_modules/, 17 | }, 18 | ], 19 | }, 20 | resolve: { 21 | extensions: ['.tsx', '.ts', '.js'], 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /apps/core-mailer/.dockerignore: -------------------------------------------------------------------------------- 1 | .turbo 2 | dist 3 | build 4 | node_modules 5 | .env* -------------------------------------------------------------------------------- /apps/core-mailer/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: ['custom'], 4 | }; 5 | -------------------------------------------------------------------------------- /apps/core-mailer/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # core-mailer 2 | 3 | ## 0.1.0 4 | 5 | ### Minor Changes 6 | 7 | - Initialise changeset 8 | -------------------------------------------------------------------------------- /apps/core-mailer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16-alpine AS builder 2 | RUN apk update && apk add --no-cache libc6-compat 3 | RUN corepack enable && corepack prepare pnpm@8.15.1 --activate 4 | 5 | WORKDIR /app 6 | 7 | RUN npm install turbo 8 | 9 | COPY ../../. . 10 | 11 | RUN apk add --no-cache \ 12 | build-base \ 13 | cairo-dev \ 14 | pango-dev \ 15 | jpeg-dev \ 16 | giflib-dev \ 17 | librsvg \ 18 | python3 19 | 20 | ENV PYTHON=/usr/bin/python3 21 | 22 | RUN pnpm install 23 | 24 | RUN pnpm run build --filter=core-mailer... 25 | 26 | FROM node:18-alpine3.16 27 | 28 | RUN apk --no-cache add openssl 29 | 30 | COPY --from=builder /app/apps/core-mailer/dist . 31 | 32 | ENV PORT=80 33 | 34 | CMD ["app.js"] 35 | -------------------------------------------------------------------------------- /apps/core-mailer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "core-mailer", 3 | "version": "0.1.0", 4 | "description": "", 5 | "main": "build/index.js", 6 | "scripts": { 7 | "build": "npm run clean & webpack --config webpack.config.js", 8 | "clean": "rimraf dist & rimraf build & rimraf docs", 9 | "dev": "concurrently \"npx tsc --watch\" \"nodemon -q build/index.js\"", 10 | "start": "node dist/app.js", 11 | "lint": "eslint --ext .ts src" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "ISC", 16 | "dependencies": { 17 | "@typescript-eslint/parser": "6.0.0", 18 | "amqplib": "^0.10.4", 19 | "body-parser": "^1.20.2", 20 | "cors": "^2.8.5", 21 | "database": "workspace:eventsync-core-mailer-database", 22 | "dotenv": "^16.0.3", 23 | "express": "^4.18.2", 24 | "formidable": "^3.5.2", 25 | "ioredis": "^5.4.1", 26 | "multer": "1.4.5-lts.1", 27 | "nodemailer": "^6.9.9", 28 | "typedoc": "^0.26.11", 29 | "typescript": "5.6", 30 | "uuid": "^9.0.0" 31 | }, 32 | "devDependencies": { 33 | "@types/amqplib": "^0.10.5", 34 | "@types/express": "^4.17.17", 35 | "@types/formidable": "^3.4.5", 36 | "@types/multer": "^1.4.12", 37 | "@types/node": "^20.10.5", 38 | "@types/nodemailer": "^6.4.14", 39 | "@types/pg": "^8.10.1", 40 | "@types/uuid": "^10.0.0", 41 | "@typescript-eslint/eslint-plugin": "^6.7.3", 42 | "concurrently": "^8.0.1", 43 | "eslint": "^8.56.0", 44 | "eslint-config-custom": "*", 45 | "eslint-config-standard-with-typescript": "^43.0.0", 46 | "eslint-plugin-import": "^2.27.5", 47 | "eslint-plugin-n": "^16.5.0", 48 | "eslint-plugin-prettier": "^5.0.0", 49 | "eslint-plugin-promise": "^6.1.1", 50 | "nodemon": "^3.0.1", 51 | "prettier": "3.1.1", 52 | "rimraf": "^5.0.1", 53 | "ts-loader": "^9.5.1", 54 | "tsconfig": "workspace:*", 55 | "webpack": "^5.89.0", 56 | "webpack-cli": "^5.1.4" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /apps/core-mailer/src/config/NodeMailer.ts: -------------------------------------------------------------------------------- 1 | import Mail from 'nodemailer/lib/mailer'; 2 | 3 | import nodemailer from 'nodemailer'; 4 | 5 | export const createTransport = (): Mail => { 6 | return nodemailer.createTransport({ 7 | host: 'smtp.gmail.com', 8 | port: 587, 9 | secure: false, 10 | auth: { 11 | type: 'OAuth2', 12 | user: process.env.GMAIL_EMAIL_ADDRESS, 13 | clientId: process.env.GMAIL_API_CLIENT_ID, 14 | clientSecret: process.env.GMAIL_API_CLIENT_SECRET, 15 | refreshToken: process.env.GMAIL_API_REFRESH_TOKEN, 16 | }, 17 | }); 18 | }; 19 | -------------------------------------------------------------------------------- /apps/core-mailer/src/config/Redis.ts: -------------------------------------------------------------------------------- 1 | import Redis from 'ioredis'; 2 | 3 | // Right now the entries do not have a TTL, so they will remain in Redis indefinitely. 4 | // This is not a problem for this application, but it is something to keep in mind. 5 | // Ideally, we would want to set a TTL for each entry, so that they are automatically removed after a certain amount of time. 6 | // The application sending the email job should check whether the email has been sent or failed and update in its database. 7 | 8 | export const createRedisClient = (): Redis => { 9 | return new Redis(process.env.REDIS_URL as string, { 10 | maxRetriesPerRequest: null, 11 | keyPrefix: 'core-mailer:', 12 | }); 13 | }; 14 | -------------------------------------------------------------------------------- /apps/core-mailer/src/controllers/MailController.ts: -------------------------------------------------------------------------------- 1 | import { UUID } from 'node:crypto'; 2 | 3 | import { Mail } from '../models/Mail'; 4 | import { MailService } from '../services/MailService'; 5 | 6 | // Add email to the queue. This will be picked up by the worker and sent. 7 | export const sendEmailController: any = async ( 8 | name: string, 9 | to: string, 10 | subject: string, 11 | text: string, 12 | html: string, 13 | ) => { 14 | try { 15 | const mail: Mail = new Mail(process.env.EMAIL_ID as string, name, to, subject, text, html); 16 | 17 | const mailService: MailService = new MailService(); 18 | await mailService.initialize(); 19 | 20 | await mailService.publish(mail); 21 | 22 | return mail.jobId; 23 | } catch (error) { 24 | console.error(error); 25 | } 26 | }; 27 | 28 | // Check the status of an email job. 29 | export const fetchEmailJobStatus: any = async (jobId: UUID) => { 30 | try { 31 | const mailService: MailService = new MailService(); 32 | await mailService.initialize(); 33 | 34 | return await mailService.fetchJobStatus(jobId); 35 | } catch (error) { 36 | console.error(error); 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /apps/core-mailer/src/index.ts: -------------------------------------------------------------------------------- 1 | import dotenv from 'dotenv'; 2 | import { UUID } from 'node:crypto'; 3 | 4 | import express, { Express, Request, Response } from 'express'; 5 | 6 | import { fetchEmailJobStatus, sendEmailController } from './controllers/MailController'; 7 | import { MailService } from './services/MailService'; 8 | import { authorize } from './middlewares/auth'; 9 | import formidable from 'formidable'; 10 | 11 | // const bodyParser = require('body-parser'); 12 | const cors = require('cors'); 13 | 14 | dotenv.config(); 15 | 16 | const port = process.env.PORT || 80; 17 | 18 | const app: Express = express(); 19 | 20 | app.use(cors({ origin: '*' })); 21 | 22 | // app.use(bodyParser.json()); 23 | 24 | app.get('/health', (req: Request, res: Response) => { 25 | const healthcheck: any = { 26 | resource: 'Core Mailer', 27 | uptime: process.uptime(), 28 | responseTime: process.hrtime(), 29 | message: 'OK', 30 | timestamp: Date.now(), 31 | }; 32 | try { 33 | return res.send(healthcheck); 34 | } catch (error) { 35 | healthcheck.message = error; 36 | return res.status(503).send(); 37 | } 38 | }); 39 | 40 | // Add email to queue 41 | app.post('/mail', authorize, async (req: Request, res: Response) => { 42 | try { 43 | // const { name, to, subject, text, html } = req.body; 44 | const form = formidable({ multiples: true }); 45 | form.parse(req, async (err, fields, files) => { 46 | if (err) { 47 | console.error('Form parsing error:', err); 48 | return res.status(500).send({ message: 'Error parsing form data' }); 49 | } 50 | // Process fields and files as needed 51 | // Formidable returns the key value pairs as an array of strings. This will create issues further down the line 52 | const { name, to, subject, text, html } = fields; 53 | //console.log(name, to, subject, text, html); 54 | if (!name || !to || !subject || !text || !html) { 55 | return res.status(400).send({ message: 'Missing required fields' }); 56 | } 57 | 58 | // Only sending the first element of the array as the form data is being sent as an array of strings. 59 | // If this is not handled here, it will create further issues down the line. 60 | const jobId: UUID = await sendEmailController(name[0], to[0], subject[0], text[0], html[0]); 61 | 62 | return res.status(200).send({ 63 | jobId: jobId, 64 | }); 65 | }); 66 | // if (!name || !to || !subject || !text || !html) { 67 | // //console.log(req.body); 68 | 69 | // return res.status(400).send({ message: 'Missing required fields' }); 70 | // } 71 | 72 | // const jobId: UUID = await sendEmailController(name, to, subject, text, html); 73 | 74 | // return res.status(200).send({ 75 | // jobId: jobId, 76 | // }); 77 | } catch (error) { 78 | console.error(error); 79 | return res.status(500).send({ message: 'Internal Server Error' }); 80 | } 81 | }); 82 | 83 | // Fetch email status 84 | app.get('/mail', authorize, async (req: Request, res: Response) => { 85 | try { 86 | const { jobId } = req.query; 87 | 88 | if (!jobId) return res.status(400).send({ message: 'Missing required fields' }); 89 | 90 | const status = await fetchEmailJobStatus(jobId as UUID); 91 | 92 | return res.status(200).send({ 93 | status: status, 94 | }); 95 | } catch (error) { 96 | console.error(error); 97 | return res.status(500).send({ message: 'Internal Server Error' }); 98 | } 99 | }); 100 | 101 | app.listen(port, () => { 102 | //console.log(`Core Mailer is running on ${port}`); 103 | }); 104 | 105 | // Check queue for new emails and send them 106 | async function startMailSub() { 107 | try { 108 | const rmqInstance: MailService = new MailService(); 109 | await rmqInstance.initialize(); 110 | await rmqInstance.subscribe(); 111 | //console.log('Subscribed to RabbitMQ email queue'); 112 | } catch (error) { 113 | console.error('Failed to subscribe to RabbitMQ email queue: ', error); 114 | } 115 | } 116 | 117 | startMailSub(); 118 | -------------------------------------------------------------------------------- /apps/core-mailer/src/middlewares/auth.ts: -------------------------------------------------------------------------------- 1 | import { NextFunction, Request, Response } from 'express'; 2 | 3 | export const authorize = (req: Request, res: Response, next: NextFunction) => { 4 | try { 5 | const ACCESS_TOKEN = process.env.ACCESS_TOKEN; 6 | 7 | if (!ACCESS_TOKEN) { 8 | console.error('Access token not found on the server'); 9 | return res.status(500).json({ message: 'Internal Server Error' }); 10 | } 11 | 12 | const authHeader = req.headers['authorization']; 13 | const token = authHeader && authHeader.split(' ')[1]; 14 | 15 | if (!token) return res.status(401).json({ message: 'Unauthorized' }); 16 | 17 | if (token !== ACCESS_TOKEN) return res.status(403).json({ message: 'Unauthorized' }); 18 | 19 | next(); 20 | } catch (error) { 21 | console.error(error); 22 | return res.status(500).json({ message: 'Internal Server Error' }); 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /apps/core-mailer/src/models/Mail.ts: -------------------------------------------------------------------------------- 1 | import { randomUUID, UUID } from 'node:crypto'; 2 | 3 | export class Mail { 4 | readonly jobId: UUID; 5 | accessor from: string; 6 | accessor name: string; 7 | accessor to: string; 8 | accessor subject: string; 9 | accessor text: string; 10 | accessor html: string; 11 | 12 | constructor( 13 | from: string, 14 | name: string, 15 | to: string, 16 | subject: string, 17 | text: string, 18 | html: string, 19 | jobId: UUID = randomUUID(), 20 | ) { 21 | this.jobId = jobId; 22 | this.from = from; 23 | this.name = name; 24 | this.to = to; 25 | this.subject = subject; 26 | this.text = text; 27 | this.html = html; 28 | } 29 | 30 | toString(): string { 31 | return JSON.stringify({ 32 | jobId: this.jobId, 33 | from: this.from, 34 | name: this.name, 35 | to: this.to, 36 | subject: this.subject, 37 | text: this.text, 38 | html: this.html, 39 | }); 40 | } 41 | } 42 | 43 | export function parseMail(str: string): Mail { 44 | //console.log(str); 45 | const parsed = JSON.parse(str); 46 | return new Mail( 47 | parsed.from, 48 | parsed.name, 49 | parsed.to, 50 | parsed.subject, 51 | parsed.text, 52 | parsed.html, 53 | parsed.jobId, 54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /apps/core-mailer/src/services/MailService.ts: -------------------------------------------------------------------------------- 1 | import { UUID } from 'node:crypto'; 2 | 3 | import { connect } from 'amqplib'; 4 | import Redis from 'ioredis'; 5 | 6 | import { createTransport } from '../config/NodeMailer'; 7 | import { createRedisClient } from '../config/Redis'; 8 | 9 | import { Mail, parseMail } from '../models/Mail'; 10 | 11 | export class MailService { 12 | private connection: any; 13 | private channel: any; 14 | private readonly queueName: string; 15 | 16 | private redis: Redis; 17 | private transporter; 18 | 19 | constructor() { 20 | this.queueName = 'core-mailer'; 21 | this.redis = createRedisClient(); 22 | this.transporter = createTransport(); 23 | } 24 | 25 | async initialize() { 26 | this.connection = await connect(process.env.RABBITMQ_URL as string); 27 | this.channel = await this.connection.createChannel(); 28 | await this.channel.assertQueue(this.queueName, { 29 | durable: false, 30 | }); 31 | } 32 | 33 | async publish(mail: Mail) { 34 | try { 35 | await this.redis.set( 36 | mail.jobId, 37 | JSON.stringify({ 38 | status: 'PENDING', 39 | timestamp: Date.now(), 40 | }), 41 | ); 42 | 43 | this.channel.sendToQueue(this.queueName, Buffer.from(mail.toString())); 44 | } catch (error) { 45 | this.redis.set( 46 | mail.jobId, 47 | JSON.stringify({ 48 | status: 'FAILED', 49 | timestamp: Date.now(), 50 | }), 51 | ); 52 | console.error('Failed to push email to queue:', error); 53 | } 54 | } 55 | 56 | async subscribe() { 57 | try { 58 | await this.channel.consume( 59 | this.queueName, 60 | (msg: any) => { 61 | if (msg !== null) { 62 | const mail: Mail = parseMail(msg.content.toString()); 63 | 64 | this.transporter.sendMail( 65 | { 66 | from: { 67 | name: mail.name, 68 | address: mail.from, 69 | }, 70 | to: mail.to, 71 | subject: mail.subject, 72 | text: mail.text, 73 | html: mail.html, 74 | }, 75 | (error, info) => { 76 | if (error) { 77 | this.redis.set( 78 | mail.jobId, 79 | JSON.stringify({ 80 | status: 'FAILED', 81 | timestamp: Date.now(), 82 | }), 83 | ); 84 | console.error('Failed to send email:', error); 85 | } else { 86 | this.redis.set( 87 | mail.jobId, 88 | JSON.stringify({ 89 | status: 'SENT', 90 | timestamp: Date.now(), 91 | }), 92 | ); 93 | } 94 | }, 95 | ); 96 | this.channel.ack(msg); 97 | } 98 | }, 99 | { 100 | noAck: false, 101 | }, 102 | ); 103 | } catch (error) { 104 | console.error('Failed to subscribe to RabbitMQ email queue:', error); 105 | } 106 | } 107 | 108 | async fetchJobStatus(jobId: UUID) { 109 | try { 110 | const status = await this.redis.get(jobId); 111 | return JSON.parse(status as string); 112 | } catch (error) { 113 | console.error('Failed to fetch job status:', error); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /apps/core-mailer/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./build/", 5 | "esModuleInterop": true 6 | }, 7 | "include": ["src/**/*"], 8 | "exclude": ["node_modules"] 9 | } 10 | -------------------------------------------------------------------------------- /apps/core-mailer/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | target: 'node', 5 | entry: './src/index.ts', 6 | output: { 7 | path: path.resolve(__dirname, 'dist'), 8 | filename: 'app.js', 9 | }, 10 | mode: 'production', 11 | module: { 12 | rules: [ 13 | { 14 | test: /\.tsx?$/, 15 | use: 'ts-loader', 16 | exclude: /node_modules/, 17 | }, 18 | ], 19 | }, 20 | resolve: { 21 | extensions: ['.tsx', '.ts', '.js'], 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /apps/registration-admin/.dockerignore: -------------------------------------------------------------------------------- 1 | .turbo 2 | dist 3 | build 4 | node_modules 5 | .env* -------------------------------------------------------------------------------- /apps/registration-admin/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /apps/registration-admin/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-alpine AS builder 2 | RUN apk add --no-cache libc6-compat 3 | RUN apk update 4 | 5 | WORKDIR /app 6 | 7 | RUN npm install -g pnpm turbo 8 | 9 | COPY ../../. . 10 | 11 | RUN apk add --no-cache \ 12 | build-base \ 13 | cairo-dev \ 14 | pango-dev \ 15 | jpeg-dev \ 16 | giflib-dev \ 17 | librsvg \ 18 | python3 19 | 20 | ENV PYTHON=/usr/bin/python3 21 | 22 | RUN pnpm install 23 | 24 | RUN pnpm run build --filter=registration-admin... 25 | 26 | # FROM node:18-alpine 27 | 28 | # COPY --from=builder /app/apps/core-auth0-actions/dist . 29 | # COPY --from=builder /app/node_modules/.pnpm/@prisma+client*/node_modules/.prisma/client/*.node . 30 | 31 | # COPY --from=builder ../apps/registration-admin/dist /app/register-build/ 32 | # RUN pnpm install typescript 33 | 34 | # Use a lightweight web server to serve the static files 35 | EXPOSE 5173 36 | CMD ["pnpm", "run", "dev", "--filter=registration-admin"] -------------------------------------------------------------------------------- /apps/registration-admin/README.md: -------------------------------------------------------------------------------- 1 | # React + TypeScript + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | 10 | ## Expanding the ESLint configuration 11 | 12 | If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: 13 | 14 | - Configure the top-level `parserOptions` property like this: 15 | 16 | ```js 17 | export default tseslint.config({ 18 | languageOptions: { 19 | // other options... 20 | parserOptions: { 21 | project: ['./tsconfig.node.json', './tsconfig.app.json'], 22 | tsconfigRootDir: import.meta.dirname, 23 | }, 24 | }, 25 | }); 26 | ``` 27 | 28 | - Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked` 29 | - Optionally add `...tseslint.configs.stylisticTypeChecked` 30 | - Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config: 31 | 32 | ```js 33 | // eslint.config.js 34 | import react from 'eslint-plugin-react'; 35 | 36 | export default tseslint.config({ 37 | // Set the react version 38 | settings: { react: { version: '18.3' } }, 39 | plugins: { 40 | // Add the react plugin 41 | react, 42 | }, 43 | rules: { 44 | // other rules... 45 | // Enable its recommended rules 46 | ...react.configs.recommended.rules, 47 | ...react.configs['jsx-runtime'].rules, 48 | }, 49 | }); 50 | ``` 51 | -------------------------------------------------------------------------------- /apps/registration-admin/eslint.config.js: -------------------------------------------------------------------------------- 1 | import js from '@eslint/js'; 2 | import globals from 'globals'; 3 | import reactHooks from 'eslint-plugin-react-hooks'; 4 | import reactRefresh from 'eslint-plugin-react-refresh'; 5 | import tseslint from 'typescript-eslint'; 6 | 7 | export default tseslint.config( 8 | { ignores: ['dist'] }, 9 | { 10 | extends: [js.configs.recommended, ...tseslint.configs.recommended], 11 | files: ['**/*.{ts,tsx}'], 12 | languageOptions: { 13 | ecmaVersion: 2020, 14 | globals: globals.browser, 15 | }, 16 | plugins: { 17 | 'react-hooks': reactHooks, 18 | 'react-refresh': reactRefresh, 19 | }, 20 | rules: { 21 | ...reactHooks.configs.recommended.rules, 22 | 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }], 23 | }, 24 | }, 25 | ); 26 | -------------------------------------------------------------------------------- /apps/registration-admin/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/registration-admin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "registration-admin", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc -b && vite build", 9 | "lint": "eslint .", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@chakra-ui/react": "^2.8.2", 14 | "@emotion/react": "^11.13.3", 15 | "@emotion/styled": "^11.13.0", 16 | "axios": "^1.5.1", 17 | "framer-motion": "^11.5.4", 18 | "react": "^18.3.1", 19 | "react-dom": "^18.3.1", 20 | "react-router-dom": "^6.27.0" 21 | }, 22 | "devDependencies": { 23 | "@eslint/js": "^9.11.1", 24 | "@types/react": "^18.3.10", 25 | "@types/react-dom": "^18.3.0", 26 | "@vitejs/plugin-react": "^4.3.2", 27 | "eslint": "^9.11.1", 28 | "eslint-plugin-react-hooks": "^5.1.0-rc.0", 29 | "eslint-plugin-react-refresh": "^0.4.12", 30 | "globals": "^15.9.0", 31 | "typescript": "^5.5.3", 32 | "typescript-eslint": "^8.7.0", 33 | "vite": "^5.4.8" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /apps/registration-admin/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/registration-admin/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'; 2 | import { ChakraProvider } from '@chakra-ui/react'; 3 | import Form from './pages/Form'; 4 | import NotFound from './pages/NotFound'; 5 | import Registered from './pages/Registered'; 6 | import RegistrationClosed from './pages/RegistrationClosed'; 7 | 8 | const App = () => { 9 | return ( 10 | 11 | 12 | 13 | } /> 14 | } /> 15 | } /> 16 | } /> 17 | 18 | 19 | 20 | ); 21 | }; 22 | 23 | export default App; 24 | -------------------------------------------------------------------------------- /apps/registration-admin/src/hooks/useAlert.tsx: -------------------------------------------------------------------------------- 1 | import { useToast, ToastId } from '@chakra-ui/react'; 2 | 3 | interface AlertOptions { 4 | title: string; 5 | description?: string; 6 | status: 'info' | 'success' | 'warning' | 'error'; 7 | duration?: number; 8 | } 9 | 10 | export const useAlert = () => { 11 | const toast = useToast(); 12 | 13 | const showAlert = ({ title, description, status, duration = 3000 }: AlertOptions): ToastId => { 14 | return toast({ 15 | title, 16 | description, 17 | status, 18 | duration, 19 | isClosable: true, 20 | position: 'top-right', 21 | }); 22 | }; 23 | 24 | return showAlert; 25 | }; 26 | -------------------------------------------------------------------------------- /apps/registration-admin/src/hooks/useFetch.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import axios, { AxiosResponse } from 'axios'; 3 | 4 | export const useFetch = () => { 5 | const [loading, setLoading] = useState(false); 6 | 7 | const get = async (endpoint: string = ''): Promise<{ data: any; status: number } | null> => { 8 | setLoading(true); 9 | try { 10 | const { data, status }: AxiosResponse = await axios.get( 11 | `${import.meta.env.VITE_API_URL}${endpoint}`, 12 | ); 13 | 14 | setLoading(false); 15 | return { data, status }; 16 | } catch (err) { 17 | console.error(err); 18 | setLoading(false); 19 | if (axios.isAxiosError(err) && err.response) { 20 | console.error(`Error: ${err.response.status} - ${err.response.statusText}`); 21 | return { data: err.response.data, status: err.response.status }; 22 | } else { 23 | console.error('Error: Unable to process the request.'); 24 | return null; 25 | } 26 | } 27 | }; 28 | 29 | const post = async ( 30 | endpoint: string = '', 31 | body: Record = {}, 32 | ): Promise<{ data: any; status: number } | null> => { 33 | setLoading(true); 34 | try { 35 | const { data, status }: AxiosResponse = await axios.post( 36 | `${import.meta.env.VITE_API_URL}${endpoint}`, 37 | body, 38 | ); 39 | setLoading(false); 40 | return { data, status }; 41 | } catch (err) { 42 | console.error(err); 43 | setLoading(false); 44 | return null; 45 | } 46 | }; 47 | 48 | return { loading, get, post }; 49 | }; 50 | -------------------------------------------------------------------------------- /apps/registration-admin/src/index.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IEDCMEC/techno-event-management/b97f2f3cf8e9cf3ec8b4d8ac32167fc9e4f5393e/apps/registration-admin/src/index.css -------------------------------------------------------------------------------- /apps/registration-admin/src/main.tsx: -------------------------------------------------------------------------------- 1 | import { StrictMode } from 'react'; 2 | import { createRoot } from 'react-dom/client'; 3 | import App from './App.tsx'; 4 | import './index.css'; 5 | 6 | createRoot(document.getElementById('root')!).render( 7 | 8 | 9 | , 10 | ); 11 | -------------------------------------------------------------------------------- /apps/registration-admin/src/pages/Form.tsx: -------------------------------------------------------------------------------- 1 | import { FormControl, FormLabel, Input, Button } from '@chakra-ui/react'; 2 | import { useFetch } from '../hooks/useFetch'; 3 | import { useAlert } from '../hooks/useAlert'; 4 | import { useEffect, useState } from 'react'; 5 | import { useParams, useNavigate } from 'react-router-dom'; 6 | 7 | const Form = () => { 8 | const [attributes, setAttributes] = useState([ 9 | { id: 'firstName', name: 'First Name: ' }, 10 | { id: 'lastName', name: 'Last Name: ' }, 11 | { id: 'email', name: 'Email: ' }, 12 | { id: 'phone', name: 'Phone No:' }, 13 | ]); 14 | const [formData, setFormData] = useState<{ [key: string]: string }>({}); 15 | const { eventID, orgID } = useParams<{ eventID: string; orgID: string }>(); 16 | const { get, post } = useFetch(); 17 | const showAlert = useAlert(); 18 | const navigate = useNavigate(); 19 | 20 | useEffect(() => { 21 | const fetchEventAttributes = async () => { 22 | const checkResponse = await get(`/registration/${orgID}/event/${eventID}/verify`); 23 | if (checkResponse?.status === 200) { 24 | const response = await get(`/registration/${orgID}/event/${eventID}/attributes`); 25 | if (response?.status === 200) { 26 | setAttributes(response?.data.attributes || []); 27 | } else { 28 | showAlert({ 29 | title: 'Error', 30 | description: response?.data.error, 31 | status: 'error', 32 | }); 33 | } 34 | } else if (checkResponse?.status === 403) { 35 | navigate('/registrationclosed'); 36 | } else { 37 | navigate('/'); 38 | } 39 | }; 40 | 41 | const hasRegistered = document.cookie.includes(`registered_${eventID}=true`); 42 | if (hasRegistered) navigate('/already-registered'); 43 | 44 | fetchEventAttributes(); 45 | }, []); 46 | 47 | const handleInputChange = (event: React.ChangeEvent) => { 48 | const { id, value } = event.target; 49 | setFormData((prevData) => ({ ...prevData, [id]: value })); 50 | }; 51 | 52 | const handleSubmit = async (event: React.FormEvent) => { 53 | event.preventDefault(); 54 | //console.log(formData); 55 | const response = await post(`/registration/${orgID}/event/${eventID}/submit`, formData); 56 | if (response?.status === 200) { 57 | document.cookie = `registered_${eventID}=true; path=/; max-age=${60 * 60 * 24 * 30};`; 58 | showAlert({ 59 | title: 'Success', 60 | description: 'Form submitted successfully!', 61 | status: 'success', 62 | }); 63 | navigate('/already-registered'); 64 | } else { 65 | showAlert({ 66 | title: 'Error', 67 | description: response?.data.error || 'Submission failed.', 68 | status: 'error', 69 | }); 70 | } 71 | }; 72 | 73 | return ( 74 |
75 | {attributes.map((attr: any, index: number) => ( 76 | 77 | {attr.name} 78 | 85 | 86 | ))} 87 | 90 |
91 | ); 92 | }; 93 | 94 | export default Form; 95 | -------------------------------------------------------------------------------- /apps/registration-admin/src/pages/NotFound.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Box, Heading, Text, Center } from '@chakra-ui/react'; 3 | 4 | const NotFound: React.FC<{ msg?: String }> = ({ msg }) => { 5 | return ( 6 |
7 | 8 | 9 | 404 10 | 11 | 12 | {msg || 'Sorry, the page you are looking for does not exist.'} 13 | 14 | 15 |
16 | ); 17 | }; 18 | 19 | export default NotFound; 20 | -------------------------------------------------------------------------------- /apps/registration-admin/src/pages/Registered.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Box, Heading, Text, Center } from '@chakra-ui/react'; 3 | 4 | const Registered: React.FC = () => { 5 | return ( 6 |
7 | 8 | 9 | Sucessfully Registered 10 | 11 | 12 | Your registration have been completed 13 | 14 | 15 |
16 | ); 17 | }; 18 | 19 | export default Registered; 20 | -------------------------------------------------------------------------------- /apps/registration-admin/src/pages/RegistrationClosed.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Box, Heading, Text, Center } from '@chakra-ui/react'; 3 | 4 | const RegistrationClosed: React.FC = () => { 5 | return ( 6 |
7 | 8 | 9 | Registration Closed 10 | 11 | 12 | The registrations for this event is not currently active 13 | 14 | 15 |
16 | ); 17 | }; 18 | 19 | export default RegistrationClosed; 20 | -------------------------------------------------------------------------------- /apps/registration-admin/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /apps/registration-admin/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "isolatedModules": true, 13 | "moduleDetection": "force", 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true 22 | }, 23 | "include": ["src"] 24 | } 25 | -------------------------------------------------------------------------------- /apps/registration-admin/tsconfig.app.tsbuildinfo: -------------------------------------------------------------------------------- 1 | {"root":["./src/app.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/hooks/usealert.tsx","./src/hooks/usefetch.tsx","./src/pages/form.tsx","./src/pages/notfound.tsx","./src/pages/registered.tsx","./src/pages/registrationclosed.tsx"],"version":"5.7.2"} -------------------------------------------------------------------------------- /apps/registration-admin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [], 3 | "references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }] 4 | } 5 | -------------------------------------------------------------------------------- /apps/registration-admin/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "lib": ["ES2023"], 5 | "module": "ESNext", 6 | "skipLibCheck": true, 7 | 8 | /* Bundler mode */ 9 | "moduleResolution": "bundler", 10 | "allowImportingTsExtensions": true, 11 | "isolatedModules": true, 12 | "moduleDetection": "force", 13 | "noEmit": true, 14 | 15 | /* Linting */ 16 | "strict": true, 17 | "noUnusedLocals": true, 18 | "noUnusedParameters": true, 19 | "noFallthroughCasesInSwitch": true 20 | }, 21 | "include": ["vite.config.ts"] 22 | } 23 | -------------------------------------------------------------------------------- /apps/registration-admin/tsconfig.node.tsbuildinfo: -------------------------------------------------------------------------------- 1 | {"root":["./vite.config.ts"],"version":"5.7.2"} -------------------------------------------------------------------------------- /apps/registration-admin/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import react from '@vitejs/plugin-react'; 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | preview: { 8 | host: true, 9 | port: 3004, 10 | }, 11 | }); 12 | -------------------------------------------------------------------------------- /apps/registration-admin/vite.config.ts.timestamp-1729018167203-e533c06553425.mjs: -------------------------------------------------------------------------------- 1 | // vite.config.ts 2 | import { defineConfig } from 'file:///F:/Midhun/Programming/techno-event-management/node_modules/.pnpm/vite@5.4.9_@types+node@22.5.0_sass@1.78.0_terser@5.32.0/node_modules/vite/dist/node/index.js'; 3 | import react from 'file:///F:/Midhun/Programming/techno-event-management/node_modules/.pnpm/@vitejs+plugin-react@4.3.2_vite@5.4.9_@types+node@22.5.0_sass@1.78.0_terser@5.32.0_/node_modules/@vitejs/plugin-react/dist/index.mjs'; 4 | var vite_config_default = defineConfig({ 5 | plugins: [react()], 6 | }); 7 | export { vite_config_default as default }; 8 | //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJGOlxcXFxNaWRodW5cXFxcUHJvZ3JhbW1pbmdcXFxcdGVjaG5vLWV2ZW50LW1hbmFnZW1lbnRcXFxcYXBwc1xcXFxyZWdpc3RyYXRpb24tYWRtaW5cIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcIkY6XFxcXE1pZGh1blxcXFxQcm9ncmFtbWluZ1xcXFx0ZWNobm8tZXZlbnQtbWFuYWdlbWVudFxcXFxhcHBzXFxcXHJlZ2lzdHJhdGlvbi1hZG1pblxcXFx2aXRlLmNvbmZpZy50c1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vRjovTWlkaHVuL1Byb2dyYW1taW5nL3RlY2huby1ldmVudC1tYW5hZ2VtZW50L2FwcHMvcmVnaXN0cmF0aW9uLWFkbWluL3ZpdGUuY29uZmlnLnRzXCI7aW1wb3J0IHsgZGVmaW5lQ29uZmlnIH0gZnJvbSAndml0ZSdcclxuaW1wb3J0IHJlYWN0IGZyb20gJ0B2aXRlanMvcGx1Z2luLXJlYWN0J1xyXG5cclxuLy8gaHR0cHM6Ly92aXRlanMuZGV2L2NvbmZpZy9cclxuZXhwb3J0IGRlZmF1bHQgZGVmaW5lQ29uZmlnKHtcclxuICBwbHVnaW5zOiBbcmVhY3QoKV0sXHJcbn0pXHJcbiJdLAogICJtYXBwaW5ncyI6ICI7QUFBNlksU0FBUyxvQkFBb0I7QUFDMWEsT0FBTyxXQUFXO0FBR2xCLElBQU8sc0JBQVEsYUFBYTtBQUFBLEVBQzFCLFNBQVMsQ0FBQyxNQUFNLENBQUM7QUFDbkIsQ0FBQzsiLAogICJuYW1lcyI6IFtdCn0K 9 | -------------------------------------------------------------------------------- /apps/registration-admin/vite.config.ts.timestamp-1729269251856-984e362a25cee.mjs: -------------------------------------------------------------------------------- 1 | // vite.config.ts 2 | import { defineConfig } from 'file:///F:/Midhun/Programming/techno-event-management/node_modules/.pnpm/vite@5.4.9_@types+node@22.5.0_sass@1.78.0_terser@5.32.0/node_modules/vite/dist/node/index.js'; 3 | import react from 'file:///F:/Midhun/Programming/techno-event-management/node_modules/.pnpm/@vitejs+plugin-react@4.3.2_vite@5.4.9_@types+node@22.5.0_sass@1.78.0_terser@5.32.0_/node_modules/@vitejs/plugin-react/dist/index.mjs'; 4 | var vite_config_default = defineConfig({ 5 | plugins: [react()], 6 | }); 7 | export { vite_config_default as default }; 8 | //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJGOlxcXFxNaWRodW5cXFxcUHJvZ3JhbW1pbmdcXFxcdGVjaG5vLWV2ZW50LW1hbmFnZW1lbnRcXFxcYXBwc1xcXFxyZWdpc3RyYXRpb24tYWRtaW5cIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcIkY6XFxcXE1pZGh1blxcXFxQcm9ncmFtbWluZ1xcXFx0ZWNobm8tZXZlbnQtbWFuYWdlbWVudFxcXFxhcHBzXFxcXHJlZ2lzdHJhdGlvbi1hZG1pblxcXFx2aXRlLmNvbmZpZy50c1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vRjovTWlkaHVuL1Byb2dyYW1taW5nL3RlY2huby1ldmVudC1tYW5hZ2VtZW50L2FwcHMvcmVnaXN0cmF0aW9uLWFkbWluL3ZpdGUuY29uZmlnLnRzXCI7aW1wb3J0IHsgZGVmaW5lQ29uZmlnIH0gZnJvbSAndml0ZSdcclxuaW1wb3J0IHJlYWN0IGZyb20gJ0B2aXRlanMvcGx1Z2luLXJlYWN0J1xyXG5cclxuLy8gaHR0cHM6Ly92aXRlanMuZGV2L2NvbmZpZy9cclxuZXhwb3J0IGRlZmF1bHQgZGVmaW5lQ29uZmlnKHtcclxuICBwbHVnaW5zOiBbcmVhY3QoKV0sXHJcbn0pXHJcbiJdLAogICJtYXBwaW5ncyI6ICI7QUFBNlksU0FBUyxvQkFBb0I7QUFDMWEsT0FBTyxXQUFXO0FBR2xCLElBQU8sc0JBQVEsYUFBYTtBQUFBLEVBQzFCLFNBQVMsQ0FBQyxNQUFNLENBQUM7QUFDbkIsQ0FBQzsiLAogICJuYW1lcyI6IFtdCn0K 9 | -------------------------------------------------------------------------------- /apps/registration-admin/vite.config.ts.timestamp-1734261834064-a590afbf0f3ab.mjs: -------------------------------------------------------------------------------- 1 | // vite.config.ts 2 | import { defineConfig } from 'file:///F:/Midhun/Programming/techno-event-management/node_modules/.pnpm/vite@5.4.11_@types+node@22.5.0_sass@1.81.0_terser@5.36.0/node_modules/vite/dist/node/index.js'; 3 | import react from 'file:///F:/Midhun/Programming/techno-event-management/node_modules/.pnpm/@vitejs+plugin-react@4.3.3_vite@5.4.11_@types+node@22.5.0_sass@1.81.0_terser@5.36.0_/node_modules/@vitejs/plugin-react/dist/index.mjs'; 4 | var vite_config_default = defineConfig({ 5 | plugins: [react()], 6 | preview: { 7 | host: true, 8 | port: 3004, 9 | }, 10 | }); 11 | export { vite_config_default as default }; 12 | //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJGOlxcXFxNaWRodW5cXFxcUHJvZ3JhbW1pbmdcXFxcdGVjaG5vLWV2ZW50LW1hbmFnZW1lbnRcXFxcYXBwc1xcXFxyZWdpc3RyYXRpb24tYWRtaW5cIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcIkY6XFxcXE1pZGh1blxcXFxQcm9ncmFtbWluZ1xcXFx0ZWNobm8tZXZlbnQtbWFuYWdlbWVudFxcXFxhcHBzXFxcXHJlZ2lzdHJhdGlvbi1hZG1pblxcXFx2aXRlLmNvbmZpZy50c1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vRjovTWlkaHVuL1Byb2dyYW1taW5nL3RlY2huby1ldmVudC1tYW5hZ2VtZW50L2FwcHMvcmVnaXN0cmF0aW9uLWFkbWluL3ZpdGUuY29uZmlnLnRzXCI7aW1wb3J0IHsgZGVmaW5lQ29uZmlnIH0gZnJvbSAndml0ZSc7XHJcbmltcG9ydCByZWFjdCBmcm9tICdAdml0ZWpzL3BsdWdpbi1yZWFjdCc7XHJcblxyXG4vLyBodHRwczovL3ZpdGVqcy5kZXYvY29uZmlnL1xyXG5leHBvcnQgZGVmYXVsdCBkZWZpbmVDb25maWcoe1xyXG4gIHBsdWdpbnM6IFtyZWFjdCgpXSxcclxuICBwcmV2aWV3OiB7XHJcbiAgICBob3N0OiB0cnVlLFxyXG4gICAgcG9ydDogMzAwNCxcclxuICB9LFxyXG59KTtcclxuIl0sCiAgIm1hcHBpbmdzIjogIjtBQUE2WSxTQUFTLG9CQUFvQjtBQUMxYSxPQUFPLFdBQVc7QUFHbEIsSUFBTyxzQkFBUSxhQUFhO0FBQUEsRUFDMUIsU0FBUyxDQUFDLE1BQU0sQ0FBQztBQUFBLEVBQ2pCLFNBQVM7QUFBQSxJQUNQLE1BQU07QUFBQSxJQUNOLE1BQU07QUFBQSxFQUNSO0FBQ0YsQ0FBQzsiLAogICJuYW1lcyI6IFtdCn0K 13 | -------------------------------------------------------------------------------- /apps/web-admin/.dockerignore: -------------------------------------------------------------------------------- 1 | .turbo 2 | dist 3 | build 4 | .next 5 | node_modules 6 | .env* -------------------------------------------------------------------------------- /apps/web-admin/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /apps/web-admin/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env*.local 29 | 30 | # vercel 31 | .vercel 32 | 33 | # typescript 34 | *.tsbuildinfo 35 | next-env.d.ts 36 | -------------------------------------------------------------------------------- /apps/web-admin/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # techno-event-web-admin 2 | 3 | ## 0.1.0 4 | 5 | ### Minor Changes 6 | 7 | - Initialise changeset 8 | 9 | ### Patch Changes 10 | 11 | - Updated dependencies 12 | - database@0.1.0 13 | -------------------------------------------------------------------------------- /apps/web-admin/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-alpine AS builder 2 | RUN apk add --no-cache libc6-compat 3 | RUN apk update 4 | 5 | WORKDIR /app 6 | 7 | RUN npm install -g pnpm turbo 8 | 9 | COPY ../../. . 10 | 11 | RUN apk add --no-cache \ 12 | build-base \ 13 | cairo-dev \ 14 | pango-dev \ 15 | jpeg-dev \ 16 | giflib-dev \ 17 | librsvg \ 18 | python3 19 | 20 | ENV PYTHON=/usr/bin/python3 21 | 22 | RUN pnpm install 23 | 24 | RUN pnpm run build --filter=techno-event-web-admin 25 | 26 | EXPOSE 3000 27 | CMD ["pnpm", "run", "dev", "--filter=techno-event-web-admin"] -------------------------------------------------------------------------------- /apps/web-admin/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | # or 12 | pnpm dev 13 | ``` 14 | 15 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 16 | 17 | You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file. 18 | 19 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`. 20 | 21 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. 22 | 23 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. 24 | 25 | ## Learn More 26 | 27 | To learn more about Next.js, take a look at the following resources: 28 | 29 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 30 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 31 | 32 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 33 | 34 | ## Deploy on Vercel 35 | 36 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 37 | 38 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 39 | -------------------------------------------------------------------------------- /apps/web-admin/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": false, 5 | "tsx": false, 6 | "tailwind": { 7 | "config": "tailwind.config.js", 8 | "css": "src/styles/globals.css", 9 | "baseColor": "neutral", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /apps/web-admin/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "paths": { 4 | "@/*": ["./src/*"] 5 | }, 6 | "jsx": "react-jsx" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /apps/web-admin/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const removeImports = require('next-remove-imports')(); 3 | const nextConfig = { 4 | reactStrictMode: true, 5 | experimental: { esmExternals: 'loose' }, 6 | images: { 7 | domains: ['s.gravatar.com', 'cdn.auth0.com'], // Add all allowed domains here 8 | }, 9 | }; 10 | 11 | module.exports = removeImports(nextConfig); 12 | -------------------------------------------------------------------------------- /apps/web-admin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "techno-event-web-admin", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "setup": "node -e \"require('fs').copyFile('.env.example', '.env', (err) => {if (err) {//console.log(err);} else {//console.log('Environment variables set up successfully');}});\"", 10 | "lint": "next lint" 11 | }, 12 | "dependencies": { 13 | "@auth0/auth0-react": "^2.2.4", 14 | "@auth0/nextjs-auth0": "^3.5.0", 15 | "@chakra-ui/button": "^2.1.0", 16 | "@chakra-ui/icons": "^2.1.1", 17 | "@chakra-ui/input": "^2.1.2", 18 | "@chakra-ui/react": "^2.8.2", 19 | "@chakra-ui/tag": "^3.1.1", 20 | "@emotion/react": "^11.13.3", 21 | "@emotion/styled": "^11.13.0", 22 | "@hookform/resolvers": "^3.3.1", 23 | "@next/bundle-analyzer": "^15.0.3", 24 | "@next/font": "^14.2.13", 25 | "@radix-ui/react-slot": "^1.1.0", 26 | "@uiw/react-markdown-preview": "^5.1.3", 27 | "@uiw/react-md-editor": "^4.0.4", 28 | "autoprefixer": "10.4.16", 29 | "axios": "^1.5.1", 30 | "bundle-analyzer": "^0.0.6", 31 | "canvas": "^2.11.2", 32 | "chakra-ui-autocomplete": "^1.4.5", 33 | "class-variance-authority": "^0.7.0", 34 | "clsx": "^2.0.0", 35 | "database": "workspace:*", 36 | "date-fns": "3.0.0", 37 | "dompurify": "^3.2.0", 38 | "easymde": "^2.18.0", 39 | "eslint": "8.56.0", 40 | "eslint-config-custom": "workspace:*", 41 | "eslint-config-next": "14.0.4", 42 | "framer-motion": "^11.5.4", 43 | "konva": "^9.3.15", 44 | "lucide-react": "^0.441.0", 45 | "marked": "^15.0.0", 46 | "next": "14.0.4", 47 | "next-remove-imports": "^1.0.12", 48 | "papaparse": "^5.4.1", 49 | "prop-types": "^15.8.1", 50 | "react": "^18.3.1", 51 | "react-color": "^2.19.3", 52 | "react-cookies": "^0.1.1", 53 | "react-csv": "^2.2.2", 54 | "react-day-picker": "9.4.4", 55 | "react-dom": "18.2.0", 56 | "react-hook-form": "^7.49.0", 57 | "react-icons": "^5.4.0", 58 | "react-konva": "^18.2.10", 59 | "react-markdown": "^9.0.1", 60 | "react-query": "^3.39.3", 61 | "react-remove-scroll": "^2.6.0", 62 | "react-simplemde-editor": "^5.2.0", 63 | "react-textarea-autosize": "^8.5.5", 64 | "react-zxing": "^2.0.2", 65 | "sass": "^1.69.1", 66 | "tailwind-merge": "^2.5.2", 67 | "tailwindcss-animate": "^1.0.7", 68 | "zod": "^3.23.8" 69 | }, 70 | "devDependencies": { 71 | "@types/node": "22.5.0", 72 | "@types/react": "18.3.4", 73 | "autoprefixer": "10.4.16", 74 | "postcss": "8.4.31", 75 | "tailwindcss": "^3.4.12", 76 | "typescript": "^5.0.4", 77 | "webpack": "^5", 78 | "webpack-cli": "^4" 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /apps/web-admin/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /apps/web-admin/public/QrTemplate.txt: -------------------------------------------------------------------------------- 1 | ### Greetings {{name}}! 2 | 3 |
4 | 5 | Thank you for your interest in participating in **< Event Name >** led by **< Speaker 1>**, < designation >, and **< Speaker 2 >**, < designation > 6 |

7 | 8 | #### Following are the details of the event: 9 | 10 |
11 | 12 | - **Date of Event**: < Date of event > 13 | - **Time**: < Time > 14 | - **Venue**: < Venue details > 15 | 16 |
17 | 18 | Your **QR Code** for the event is attached with this email. Please use it to confirm your attendance at the registration desk tomorrow before **< Time of registration >**. 19 | 20 |
21 | 22 | **Entry to the venue will not be entertained after < Time of entry >** 23 | 24 |
25 | 26 | Your QR code is your unique entry pass for the event. Please present it to our volunteers to be scanned and checked in. **Only those checked in with the QR code will receive certificates**. The same QR code can also be used as a **food coupon** to avail of your **lunch and snacks**. 27 | 28 |
29 | 30 | #### General Guidelines: 31 | 32 |
33 | 34 | - < guideline 1 > 35 | - < guideline 2 > 36 | 37 |
38 | 39 | In case of any questions or inquiries, please contact: 40 | **< POC 1 >** - < contact no: > 41 | **< POC 2 >** - < contact no: > 42 | 43 |

44 | Looking forward to seeing you at the event tomorrow! 45 |

46 | 47 | Kind regards, 48 | < Organization name > 49 | 50 | 61 | 62 | 63 | 113 | 114 | 115 |
64 |

76 | Verify Your Identity 77 |

78 |

89 | Produce this email to the volunteers at the registration desk 90 |

91 |

92 |
93 | Ticker QR 94 |
95 |

96 | {{payload}} 97 |

98 |

108 | Contact 109 | iedcmec@mec.ac.in 110 | if you face any issues 111 |

112 |
-------------------------------------------------------------------------------- /apps/web-admin/public/ThisIsAnORG.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IEDCMEC/techno-event-management/b97f2f3cf8e9cf3ec8b4d8ac32167fc9e4f5393e/apps/web-admin/public/ThisIsAnORG.png -------------------------------------------------------------------------------- /apps/web-admin/public/assets/events/Adduser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IEDCMEC/techno-event-management/b97f2f3cf8e9cf3ec8b4d8ac32167fc9e4f5393e/apps/web-admin/public/assets/events/Adduser.png -------------------------------------------------------------------------------- /apps/web-admin/public/assets/events/Box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IEDCMEC/techno-event-management/b97f2f3cf8e9cf3ec8b4d8ac32167fc9e4f5393e/apps/web-admin/public/assets/events/Box.png -------------------------------------------------------------------------------- /apps/web-admin/public/assets/events/Heartbeat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IEDCMEC/techno-event-management/b97f2f3cf8e9cf3ec8b4d8ac32167fc9e4f5393e/apps/web-admin/public/assets/events/Heartbeat.png -------------------------------------------------------------------------------- /apps/web-admin/public/assets/events/ProgressBar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IEDCMEC/techno-event-management/b97f2f3cf8e9cf3ec8b4d8ac32167fc9e4f5393e/apps/web-admin/public/assets/events/ProgressBar.png -------------------------------------------------------------------------------- /apps/web-admin/public/assets/events/Users.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IEDCMEC/techno-event-management/b97f2f3cf8e9cf3ec8b4d8ac32167fc9e4f5393e/apps/web-admin/public/assets/events/Users.png -------------------------------------------------------------------------------- /apps/web-admin/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/web-admin/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/web-admin/src/assets/Dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IEDCMEC/techno-event-management/b97f2f3cf8e9cf3ec8b4d8ac32167fc9e4f5393e/apps/web-admin/src/assets/Dark.png -------------------------------------------------------------------------------- /apps/web-admin/src/assets/Light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IEDCMEC/techno-event-management/b97f2f3cf8e9cf3ec8b4d8ac32167fc9e4f5393e/apps/web-admin/src/assets/Light.png -------------------------------------------------------------------------------- /apps/web-admin/src/components/AddParticipant.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | Button, 3 | Modal, 4 | ModalOverlay, 5 | ModalContent, 6 | ModalHeader, 7 | ModalFooter, 8 | ModalBody, 9 | ModalCloseButton, 10 | FormControl, 11 | FormLabel, 12 | Input, 13 | useDisclosure, 14 | useColorMode, 15 | } from '@chakra-ui/react'; 16 | import { StyledText } from '@/components/ui/StyledComponents'; 17 | import { inter } from '@/components/ui/fonts'; 18 | 19 | const AddParticipant = ({ isOpen, onClose, formData, handleInputChange, handleSubmit }) => { 20 | const { colorMode } = useColorMode(); 21 | return ( 22 | 23 | 24 | 25 | 32 | Add Participant 33 | 34 | 35 | 36 | 37 | First Name 38 | 45 | 46 | 47 | Last Name 48 | 55 | 56 | 57 | Email 58 | 65 | 66 | 67 | Phone 68 | 75 | 76 | 77 | Check In Key 78 | 85 | 86 | 87 | 92 | 102 | 110 | 111 | 112 | 113 | ); 114 | }; 115 | 116 | export default AddParticipant; 117 | -------------------------------------------------------------------------------- /apps/web-admin/src/components/ComingSoon.jsx: -------------------------------------------------------------------------------- 1 | import { Heading } from '@chakra-ui/react'; 2 | import { Highlight } from '@chakra-ui/react'; 3 | import { color } from 'framer-motion'; 4 | import React from 'react'; 5 | 6 | const ComingSoon = () => { 7 | return ( 8 |
18 | 19 | 29 | Coming Soon ! 30 | 31 | 32 | 33 | Check back soon for updates. 34 | 35 |
36 | ); 37 | }; 38 | 39 | export default ComingSoon; 40 | -------------------------------------------------------------------------------- /apps/web-admin/src/components/DataDisplayNew.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | ChakraProvider, 3 | Box, 4 | Spinner, 5 | Table, 6 | Thead, 7 | Tbody, 8 | Tr, 9 | Th, 10 | Td, 11 | TableContainer, 12 | Checkbox, 13 | } from '@chakra-ui/react'; 14 | import { useState } from 'react'; 15 | import { extendTheme } from '@chakra-ui/react'; 16 | // import { useEffect } from 'react'; 17 | // import useWrapper from '@/hooks/useWrapper'; 18 | // import { useContext } from 'react'; 19 | // import { account } from '@/contexts/MyContext'; 20 | const chakraTheme = extendTheme({ 21 | colors: { 22 | primary: { 23 | 500: '#319795', 24 | }, 25 | }, 26 | }); 27 | export default function DataDisplayNew({ 28 | loading, 29 | rows, 30 | columns, 31 | onRowClick, 32 | overflowY = 'auto', 33 | height = 'auto', 34 | state = null, 35 | setState = null, 36 | }) { 37 | // const {useGetQuery, usePostMutation} = useWrapper(); 38 | // const {accountDetails} = useContext(account); 39 | 40 | //const [selectedRows, setSelectedRows] = useState([]); 41 | // //console.log(state); 42 | 43 | // ////console.log(Object.keys(rows[0])); 44 | 45 | // useEffect(() => { 46 | // ////console.log(selectedRows); 47 | // }, [selectedRows]); 48 | return ( 49 | 50 | 51 | {loading ? ( 52 | 53 | ) : ( 54 | 55 | 56 | 57 | 58 | {columns.map((column) => ( 59 | 60 | ))} 61 | 62 | 63 | 64 | {rows.map((row) => ( 65 | 66 | {columns.map((column) => ( 67 | 68 | ))} 69 | 70 | ))} 71 | 72 |
{column.headerName}
{row[column.field]}
73 |
74 | )} 75 |
76 |
77 | ); 78 | } 79 | -------------------------------------------------------------------------------- /apps/web-admin/src/components/EventsDisplay.jsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | // import { useFetch } from '@/hooks/useFetch'; 3 | import { useAlert } from '@/hooks/useAlert'; 4 | import { inter } from './ui/fonts'; 5 | import { 6 | Box, 7 | UnorderedList, 8 | ListItem, 9 | Link, 10 | Text, 11 | Accordion, 12 | AccordionItem, 13 | AccordionButton, 14 | AccordionPanel, 15 | AccordionIcon, 16 | SkeletonText, 17 | } from '@chakra-ui/react'; 18 | import useWrapper from '@/hooks/useWrapper'; 19 | import { StyledText } from './ui/StyledComponents'; 20 | import NextLink from 'next/link'; 21 | import { MdOutlineEvent } from 'react-icons/md'; 22 | import { useRouter } from 'next/router'; 23 | 24 | const EventsDisplay = () => { 25 | const [events, setEvents] = useState([]); 26 | const { useGetQuery } = useWrapper(); 27 | 28 | const router = useRouter(); 29 | const { orgId } = router.query; 30 | 31 | const showAlert = useAlert(); 32 | // const [orgId, setOrgId] = useState(null) 33 | const { 34 | data, 35 | status, 36 | error, 37 | isFetching: loading, 38 | } = useGetQuery( 39 | `/core/organizations/${orgId}/events`, 40 | `/core/organizations/${orgId}/events`, 41 | {}, // headers 42 | {}, // options 43 | (data) => { 44 | if (data.data.events.lenth !== events.length) { 45 | setEvents(data.data.events || []); 46 | } 47 | }, 48 | ); 49 | 50 | // if (events.length === 0) { 51 | // return
No events found for this organization.
; 52 | // } 53 | 54 | return ( 55 | 56 | 57 | 67 | 68 | 69 | 70 | Events 71 | 72 | 73 | 74 | 75 | {(!orgId || loading || events.length === 0) && ( 76 |
77 | 78 |
79 | )} 80 | {events.length !== 0 ? ( 81 | events.map((event) => ( 82 | 83 | 84 | {event.name} 85 | 86 | 87 | )) 88 | ) : ( 89 |
No events found for this organization.
90 | )} 91 |
92 |
93 |
94 |
95 | ); 96 | }; 97 | 98 | export default EventsDisplay; 99 | -------------------------------------------------------------------------------- /apps/web-admin/src/components/ItemCard.jsx: -------------------------------------------------------------------------------- 1 | import { Card, CardBody, Image, Text, Box } from '@chakra-ui/react'; 2 | import { useRouter } from 'next/router'; 3 | const ItemCard = (props) => { 4 | const router = useRouter(); 5 | 6 | if (props.logo == '') { 7 | var logo = '/ThisIsAnORG.png'; 8 | } else { 9 | var logo = props.logo; 10 | } 11 | return ( 12 | 23 | logo of org 32 | 33 | {props.name} 34 | 35 | 36 | ); 37 | }; 38 | 39 | export default ItemCard; 40 | -------------------------------------------------------------------------------- /apps/web-admin/src/components/MarkdownEditor.jsx: -------------------------------------------------------------------------------- 1 | import dynamic from 'next/dynamic'; 2 | import { useEffect, useRef } from 'react'; 3 | import { Textarea } from '@chakra-ui/react'; 4 | 5 | // Dynamically import EasyMDE to avoid SSR issues 6 | const EasyMDE = dynamic(() => import('easymde').then((mod) => mod.default), { ssr: false }); 7 | 8 | const MarkdownEditor = ({ value, onChange }) => { 9 | const editorRef = useRef(null); 10 | 11 | useEffect(() => { 12 | if (!editorRef.current) { 13 | const textarea = document.getElementById('editor'); 14 | editorRef.current = new EasyMDE({ 15 | element: textarea, 16 | initialValue: value, 17 | toolbar: ['bold', 'italic', 'heading', '|', 'preview'], 18 | spellChecker: false, 19 | }); 20 | 21 | // Handle content changes 22 | editorRef.current.codemirror.on('change', () => { 23 | onChange(editorRef.current.value()); 24 | }); 25 | } 26 | 27 | return () => { 28 | if (editorRef.current) { 29 | editorRef.current.toTextArea(); // Cleanup the editor 30 | editorRef.current = null; 31 | } 32 | }; 33 | }, [value, onChange]); 34 | 35 | return ( 36 |