├── .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 |
9 |
10 |
11 |
12 |
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 |
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 |
64 |
76 | Verify Your Identity
77 |
78 |
89 | Produce this email to the volunteers at the registration desk
90 |
91 |
92 |
93 |
94 |
95 |
96 | {{payload}}
97 |
98 |
108 | Contact
109 | iedcmec@mec.ac.in
110 | if you face any issues
111 |
112 |
113 |
114 |
115 |
--------------------------------------------------------------------------------
/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 |
100 | Submit
101 |
102 |
108 | Cancel
109 |
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 | {column.headerName}
60 | ))}
61 |
62 |
63 |
64 | {rows.map((row) => (
65 |
66 | {columns.map((column) => (
67 | {row[column.field]}
68 | ))}
69 |
70 | ))}
71 |
72 |
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 |
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 |
41 | );
42 | };
43 |
44 | export default MarkdownEditor;
45 |
--------------------------------------------------------------------------------
/apps/web-admin/src/components/NewCalendar.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect, useRef, useCallback } from 'react';
2 | import { format } from 'date-fns';
3 | import { DayPicker } from 'react-day-picker';
4 | import { FaCaretDown } from 'react-icons/fa';
5 | import { StyledText } from '../components/ui/StyledComponents';
6 |
7 | const weekdays = ['S', 'M', 'T', 'W', 'T', 'F', 'S'];
8 | const currentYear = new Date().getFullYear();
9 | const startYear = currentYear - 7;
10 | const endYear = currentYear + 7;
11 | const years = Array.from({ length: endYear - startYear + 1 }, (_, i) => startYear + i);
12 |
13 | const CustomYearDropdown = ({ className, value, onChange, isOpen, setIsOpen, ...props }) => {
14 | const [selectedYear, setSelectedYear] = useState(value);
15 | const dropdownRef = useRef(null);
16 |
17 | const toggleDropdown = useCallback(() => {
18 | setIsOpen(!isOpen);
19 | }, [isOpen, setIsOpen]);
20 |
21 | useEffect(() => {
22 | setSelectedYear(value);
23 | }, [value]);
24 |
25 | const handleYearClick = useCallback(
26 | (year) => {
27 | setSelectedYear(year);
28 | setIsOpen(false);
29 | onChange({ target: { value: year } });
30 | },
31 | [onChange, setIsOpen],
32 | );
33 |
34 | const handleClickOutside = useCallback(
35 | (event) => {
36 | if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
37 | setIsOpen(false);
38 | }
39 | },
40 | [setIsOpen],
41 | );
42 |
43 | useEffect(() => {
44 | document.addEventListener('mousedown', handleClickOutside);
45 | return () => {
46 | document.removeEventListener('mousedown', handleClickOutside);
47 | };
48 | }, [handleClickOutside]);
49 |
50 | return (
51 |
52 |
53 |
54 | {selectedYear}
55 |
56 |
57 |
58 | {isOpen && (
59 |
60 | {years.map((year) => (
61 | handleYearClick(year)}
66 | >
67 |
68 | {year}
69 |
70 |
71 | ))}
72 |
73 | )}
74 |
75 | );
76 | };
77 |
78 | const NewCalendar = () => {
79 | const [selected, setSelected] = useState(new Date());
80 | const [isOpen, setIsOpen] = useState(false);
81 |
82 | const handleDateSelect = useCallback(
83 | (date) => {
84 | if (date) {
85 | if (!selected || format(date, 'yyyy-MM-dd') !== format(selected, 'yyyy-MM-dd')) {
86 | setSelected(date);
87 | }
88 | }
89 | },
90 | [selected, setSelected],
91 | );
92 |
93 | return (
94 |
95 |
96 | {selected instanceof Date ? format(selected, 'EEEE, MMM d') : ''}
97 |
98 |
99 | (
109 | {weekdays[day.getDay()]}
110 | ),
111 | }}
112 | components={{
113 | Dropdown: (props) => (
114 |
115 | ),
116 | DayButton: ({ day, className, children, ...props }) => (
117 |
118 | {children}
119 |
120 | ),
121 | MonthCaption: ({ day, className, children, ...props }) => (
122 |
123 | {children}
124 |
125 | ),
126 | }}
127 | modifiers={{
128 | hidden: isOpen,
129 | }}
130 | />
131 |
132 | );
133 | };
134 |
135 | export default NewCalendar;
136 |
--------------------------------------------------------------------------------
/apps/web-admin/src/components/Scanner.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { Box } from '@chakra-ui/react';
3 | import { useZxing } from 'react-zxing';
4 |
5 | const Scanner = ({ result, setResult }) => {
6 | const handleScan = (result) => {
7 | if (result) {
8 | setResult(result?.text);
9 | }
10 | };
11 |
12 | const handleError = (err) => {
13 | console.error(err);
14 | };
15 | const { ref } = useZxing({
16 | onDecodeResult(result) {
17 | setResult(result.getText());
18 | },
19 | });
20 | return (
21 | //
28 |
29 | );
30 | };
31 |
32 | export default Scanner;
33 |
--------------------------------------------------------------------------------
/apps/web-admin/src/components/landing/assets/images/dash_frame.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IEDCMEC/techno-event-management/b97f2f3cf8e9cf3ec8b4d8ac32167fc9e4f5393e/apps/web-admin/src/components/landing/assets/images/dash_frame.png
--------------------------------------------------------------------------------
/apps/web-admin/src/components/landing/assets/index.js:
--------------------------------------------------------------------------------
1 | import logo from './logo/logo.svg';
2 | import logo_text from './logo/logo+text.svg';
3 |
4 | import dash_frame from './images/dash_frame.png';
5 |
6 | export { logo, logo_text, dash_frame };
7 |
--------------------------------------------------------------------------------
/apps/web-admin/src/components/landing/assets/logo/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/apps/web-admin/src/components/landing/components/calltoaction.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Heading, Text, Button, Stack, Flex } from '@chakra-ui/react';
3 | import { HiMiniArrowUpRight } from 'react-icons/hi2';
4 | import { IoIosPlayCircle } from 'react-icons/io';
5 |
6 | const CallToAction = () => {
7 | return (
8 |
9 |
33 |
34 | New Update:
35 |
36 | AI Integration{' '}
37 |
38 |
39 |
52 | Simplified event planning awaits
53 |
54 |
66 | From concept to completion, manage every detail with ease. Transform your events and impress
67 | attendees.
68 |
69 |
80 |
87 | Start for free
88 |
89 |
98 |
99 | Watch Demo
100 |
101 |
102 |
103 | );
104 | };
105 |
106 | export default CallToAction;
107 |
--------------------------------------------------------------------------------
/apps/web-admin/src/components/landing/components/footer.jsx:
--------------------------------------------------------------------------------
1 | import { Box, Button, Flex, Text } from '@chakra-ui/react';
2 | import React from 'react';
3 | import { FaXTwitter, FaGithub, FaLinkedin } from 'react-icons/fa6';
4 |
5 | const Footer = () => {
6 | return (
7 |
19 |
30 |
39 | Copyright © 2024 EventSync. Engineered by{' '}
40 |
41 | IEDC
42 | {' '}
43 | MEC.
44 |
45 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | );
68 | };
69 |
70 | export default Footer;
71 |
--------------------------------------------------------------------------------
/apps/web-admin/src/components/landing/index.jsx:
--------------------------------------------------------------------------------
1 | 'use-client';
2 | import React from 'react';
3 | import { Box, ChakraProvider, Flex } from '@chakra-ui/react';
4 | import theme, { Fonts } from './theme';
5 | import Navbar from './components/navbar';
6 | import CallToAction from '@/components/landing/components/calltoaction';
7 | import Image from 'next/image';
8 | import { dash_frame } from '@/components/landing/assets';
9 | import Footer from '@/components/landing/components/footer';
10 |
11 | const Landing = () => {
12 | return (
13 |
14 |
15 |
16 |
17 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | );
34 | };
35 |
36 | export default Landing;
37 |
--------------------------------------------------------------------------------
/apps/web-admin/src/components/landing/theme.js:
--------------------------------------------------------------------------------
1 | import { extendTheme } from '@chakra-ui/react';
2 | import { Global } from '@emotion/react';
3 |
4 | const Fonts = () => (
5 |
10 | );
11 |
12 | const theme = extendTheme({
13 | colors: {
14 | brand: {
15 | light_green: '#D9E4F9',
16 | light_blue: '#ADC5F2',
17 | white: '#FFF',
18 | black: '#000',
19 |
20 | black_v1: '#111111',
21 | black_v1_border: 'rgba(17, 17, 17, 0.1)',
22 |
23 | nav_white: 'rgba(255, 255, 255, 0.8)',
24 | footer_white: 'rgba(255, 255, 255, 0.75)',
25 |
26 | black_v2: '#2A2A2A',
27 |
28 | socials_bg: 'rgba(217, 228, 249, 0.8)',
29 | },
30 | },
31 | fonts: {
32 | heading: 'mluvka, sans-serif',
33 | body: 'mluvka, sans-serif',
34 | },
35 | styles: {
36 | global: {
37 | body: {
38 | bg: 'brand.light_green',
39 | },
40 | },
41 | },
42 | components: {
43 | Button: {
44 | baseStyle: {
45 | fontWeight: '500',
46 | borderRadius: '12px',
47 | transition: 'all 0.5s ease',
48 | },
49 | sizes: {
50 | md: {
51 | fontSize: '16px',
52 | py: '12px',
53 | px: '15px',
54 | },
55 | },
56 | variants: {
57 | socials: {
58 | display: 'inline-flex',
59 | justifyContent: 'center',
60 | alignItems: 'center',
61 | bg: 'rgba(217, 228, 249, 0.35)',
62 | _hover: {
63 | bg: 'brand.socials_bg',
64 | },
65 | },
66 | trans: {
67 | bg: 'transparent',
68 | border: '1px',
69 | borderColor: 'brand.black_v1_border',
70 | outline: 'none',
71 | height: '48px',
72 | width: '124px',
73 | color: 'brand.black_v1',
74 | _hover: {
75 | bg: 'brand.black_v1_border',
76 | },
77 | },
78 | black: {
79 | display: 'inline-flex',
80 | justifyContent: 'center',
81 | alignItems: 'center',
82 | bg: 'brand.black_v1',
83 | height: '48px',
84 | width: '124px',
85 | color: 'brand.white',
86 | _hover: {
87 | opacity: '0.97',
88 | },
89 | },
90 | },
91 | },
92 | },
93 | });
94 |
95 | export default theme;
96 | export { Fonts };
97 |
--------------------------------------------------------------------------------
/apps/web-admin/src/components/modals/AddParticipantModal/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 |
114 |
115 |
116 | );
117 | };
118 |
119 | export default AddParticipant;
120 |
--------------------------------------------------------------------------------
/apps/web-admin/src/components/ui/button.jsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { Slot } from '@radix-ui/react-slot';
3 | import { cva } from 'class-variance-authority';
4 |
5 | import { cn } from '@/lib/utils';
6 |
7 | const buttonVariants = cva(
8 | 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
9 | {
10 | variants: {
11 | variant: {
12 | default: 'bg-primary text-primary-foreground hover:bg-primary/90',
13 | destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
14 | outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
15 | secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
16 | ghost: 'hover:bg-accent hover:text-accent-foreground',
17 | link: 'text-primary underline-offset-4 hover:underline',
18 | },
19 | size: {
20 | default: 'h-10 px-4 py-2',
21 | sm: 'h-9 rounded-md px-3',
22 | lg: 'h-11 rounded-md px-8',
23 | icon: 'h-10 w-10',
24 | },
25 | },
26 | defaultVariants: {
27 | variant: 'default',
28 | size: 'default',
29 | },
30 | },
31 | );
32 |
33 | const Button = React.forwardRef(({ className, variant, size, asChild = false, ...props }, ref) => {
34 | const Comp = asChild ? Slot : 'button';
35 | return ;
36 | });
37 | Button.displayName = 'Button';
38 |
39 | export { Button, buttonVariants };
40 |
--------------------------------------------------------------------------------
/apps/web-admin/src/components/ui/fonts.jsx:
--------------------------------------------------------------------------------
1 | // Define the fonts here
2 | import { Inter } from 'next/font/google';
3 |
4 | const inter = Inter({
5 | subsets: ['latin'], // Subset options: 'latin', 'latin-ext', etc.
6 | weight: ['400', '600'], // Specify weights like 400 (regular), 700 (bold), etc.
7 | variable: '--font-inter', // Define a CSS variable for easier usage
8 | });
9 |
10 | export { inter };
11 |
--------------------------------------------------------------------------------
/apps/web-admin/src/contexts/MyContext.jsx:
--------------------------------------------------------------------------------
1 | import { React, createContext, useState, useEffect } from 'react';
2 | import { useFetch } from '@/hooks/useFetch';
3 | import { useAlert } from '@/hooks/useAlert';
4 | import { useAuth0 } from '@auth0/auth0-react';
5 | import { useColorMode } from '@chakra-ui/react';
6 | export const account = createContext();
7 | const MyContext = ({ children }) => {
8 | const [accountDetails, setAccountDetails] = useState({
9 | firstName: '',
10 | lastName: '',
11 | });
12 | const [emailProjects, setEmailProjects] = useState([]);
13 | const { user, isAuthenticated, isLoading, loginWithRedirect } = useAuth0();
14 | const [participants, setParticipants] = useState([]);
15 | const [userDetails, setUserDetails] = useState([]);
16 | const [eventDetails, setEventDetails] = useState({});
17 |
18 | const { loading, get, put } = useFetch();
19 | const showAlert = useAlert();
20 | useEffect(() => {
21 | console.log(accountDetails);
22 | }, [accountDetails]);
23 | // useEffect(() => {
24 | // const fetchAccountDetails = async () => {
25 | // if (isAuthenticated) {
26 | // const { data, status } = await get('/core/users/me');
27 | // const response = await get('/core/users/mycreds');
28 | // // //console.log(response, data);
29 | // setAccountDetails((preValue) => ({ ...preValue, ...(data.accountDetails || {}) }));
30 | // }
31 | // };
32 | // fetchAccountDetails();
33 | // // //console.log('trigger');
34 | // }, [isAuthenticated]);
35 | // //console.log(accountDetails);
36 | const updateAccountDetails = async () => {
37 | const { data, status } = await put('/core/users/me', {}, accountDetails);
38 | // //console.log(data);
39 | if (status === 200) {
40 | showAlert({
41 | title: 'Success',
42 | description: 'Account details updated successfully.',
43 | status: 'success',
44 | });
45 | //console.log(data);
46 | setUserDetails((prev) => {
47 | return {
48 | ...prev,
49 | ...(data.accountDetails || {}),
50 | };
51 | });
52 | } else {
53 | showAlert({
54 | title: 'Error',
55 | description: data.error,
56 | status: 'error',
57 | });
58 | }
59 | };
60 | const [activeTab, setActiveTab] = useState('Participants');
61 | const [allAccounts, setAllAccounts] = useState([]);
62 | const { colorMode, toggleColorMode } = useColorMode();
63 | return (
64 |
65 |
84 | {children}
85 |
86 |
87 | );
88 | };
89 |
90 | export default MyContext;
91 |
--------------------------------------------------------------------------------
/apps/web-admin/src/hooks/useAccessToken.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react';
2 |
3 | import { useUser } from '@auth0/nextjs-auth0/client';
4 |
5 | import axios from 'axios';
6 |
7 | export const useAccessToken = () => {
8 | const { user, isLoading } = useUser();
9 |
10 | const [accessToken, setAccessToken] = useState(null);
11 |
12 | useEffect(() => {
13 | if (isLoading) return;
14 |
15 | if (!user) return;
16 |
17 | const fetchToken = async () => {
18 | const { data, status } = await axios.get('/api/auth/access-token', {
19 | validateStatus: () => true,
20 | });
21 | if (status === 200) {
22 | setAccessToken(data.accessToken);
23 | } else {
24 | setAccessToken(null);
25 | }
26 | };
27 | fetchToken();
28 | }, [user, isLoading]);
29 |
30 | return { accessToken };
31 | };
32 |
--------------------------------------------------------------------------------
/apps/web-admin/src/hooks/useAlert.jsx:
--------------------------------------------------------------------------------
1 | import { useToast } from '@chakra-ui/react';
2 |
3 | export const useAlert = () => {
4 | const toast = useToast();
5 |
6 | const showAlert = ({ title, description, status, duration = 3000 }) => {
7 | toast({
8 | title,
9 | description,
10 | status,
11 | duration,
12 | isClosable: true,
13 | position: 'top-right',
14 | });
15 | };
16 |
17 | return showAlert;
18 | };
19 |
--------------------------------------------------------------------------------
/apps/web-admin/src/hooks/useDebounce.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react';
2 | // import useTimeout from "../2-useTimeout/useTimeout"
3 | import useTimeout from './useTimeout';
4 |
5 | export default function useDebounce(callback, delay, dependencies) {
6 | const { reset, clear } = useTimeout(callback, delay);
7 | useEffect(reset, [...dependencies, reset]);
8 | useEffect(clear, []);
9 | }
10 |
--------------------------------------------------------------------------------
/apps/web-admin/src/hooks/useFetch.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 |
3 | import axios from 'axios';
4 |
5 | import { useAuth0 } from '@auth0/auth0-react';
6 |
7 | export const useFetch = () => {
8 | const { getAccessTokenSilently } = useAuth0();
9 |
10 | const [loading, setLoading] = useState(false);
11 |
12 | const get = async (endpoint = '', headers = {}) => {
13 | setLoading(true);
14 | try {
15 | const response = await axios.get(process.env.NEXT_PUBLIC_API_URL + endpoint, {
16 | headers: {
17 | ...headers,
18 | 'Content-Type': 'application/json',
19 | Authorization:
20 | 'Bearer ' +
21 | (await getAccessTokenSilently({
22 | authorizationParams: {
23 | audience: process.env.NEXT_PUBLIC_AUTH0_AUDIENCE,
24 | },
25 | })),
26 | },
27 | validateStatus: () => true,
28 | });
29 |
30 | setLoading(false);
31 |
32 | return response;
33 | } catch (err) {
34 | console.error(err);
35 | setLoading(false);
36 | return null;
37 | }
38 | };
39 |
40 | const post = async (endpoint = '', headers = {}, body = {}, contentType = 'application/json') => {
41 | setLoading(true);
42 | try {
43 | const { data, status } = await axios.post(process.env.NEXT_PUBLIC_API_URL + endpoint, body, {
44 | headers: {
45 | ...headers,
46 | 'Content-Type': contentType,
47 | Authorization:
48 | 'Bearer ' +
49 | (await getAccessTokenSilently({
50 | authorizationParams: {
51 | audience: process.env.NEXT_PUBLIC_AUTH0_AUDIENCE,
52 | },
53 | })),
54 | },
55 | validateStatus: () => true,
56 | });
57 |
58 | setLoading(false);
59 |
60 | return { data, status };
61 | } catch (err) {
62 | console.error(err);
63 | setLoading(false);
64 | return null;
65 | }
66 | };
67 |
68 | const put = async (endpoint = '', headers = {}, body = {}) => {
69 | setLoading(true);
70 | try {
71 | const { data, status } = await axios.put(process.env.NEXT_PUBLIC_API_URL + endpoint, body, {
72 | headers: {
73 | ...headers,
74 | 'Content-Type': 'application/json',
75 | Authorization:
76 | 'Bearer ' +
77 | (await getAccessTokenSilently({
78 | authorizationParams: {
79 | audience: process.env.NEXT_PUBLIC_AUTH0_AUDIENCE,
80 | },
81 | })),
82 | },
83 | validateStatus: () => true,
84 | });
85 |
86 | setLoading(false);
87 |
88 | return { data, status };
89 | } catch (err) {
90 | console.error(err);
91 | setLoading(false);
92 | return null;
93 | }
94 | };
95 |
96 | const del = async (endpoint = '', headers = {}) => {
97 | setLoading(true);
98 | try {
99 | const { data, status } = await axios.delete(process.env.NEXT_PUBLIC_API_URL + endpoint, {
100 | headers: {
101 | ...headers,
102 | 'Content-Type': 'application/json',
103 | Authorization:
104 | 'Bearer ' +
105 | (await getAccessTokenSilently({
106 | authorizationParams: {
107 | audience: process.env.NEXT_PUBLIC_AUTH0_AUDIENCE,
108 | },
109 | })),
110 | },
111 | validateStatus: () => true,
112 | });
113 |
114 | setLoading(false);
115 |
116 | return { data, status };
117 | } catch (err) {
118 | console.error(err);
119 | setLoading(false);
120 | return null;
121 | }
122 | };
123 |
124 | return { loading, get, post, put, del };
125 | };
126 |
--------------------------------------------------------------------------------
/apps/web-admin/src/hooks/useLocalStorage.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 |
3 | export const useLocalStorage = (keyName, defaultValue) => {
4 | const [storedValue, setStoredValue] = useState(() => {
5 | try {
6 | const value = window.localStorage.getItem(keyName);
7 | if (value) {
8 | return JSON.parse(value);
9 | } else {
10 | window.localStorage.setItem(keyName, JSON.stringify(defaultValue));
11 | return defaultValue;
12 | }
13 | } catch (err) {
14 | return defaultValue;
15 | }
16 | });
17 | const setValue = (newValue) => {
18 | try {
19 | window.localStorage.setItem(keyName, JSON.stringify(newValue));
20 | } catch (err) {}
21 | setStoredValue(newValue);
22 | };
23 | return [storedValue, setValue];
24 | };
25 |
--------------------------------------------------------------------------------
/apps/web-admin/src/hooks/useRequests.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 | // import { useQueryClient } from 'react-query';
3 | import axios from 'axios';
4 |
5 | import { useAuth0 } from '@auth0/auth0-react';
6 |
7 | export const useRequests = () => {
8 | const { getAccessTokenSilently } = useAuth0();
9 |
10 | const [loading, setLoading] = useState(false);
11 |
12 | const get = async (endpoint = '', headers = {}) => {
13 | setLoading(true);
14 | try {
15 | const response = await axios.get(process.env.NEXT_PUBLIC_API_URL + endpoint, {
16 | headers: {
17 | ...headers,
18 | 'Content-Type': 'application/json',
19 | Authorization:
20 | 'Bearer ' +
21 | (await getAccessTokenSilently({
22 | authorizationParams: {
23 | audience: process.env.NEXT_PUBLIC_AUTH0_AUDIENCE,
24 | },
25 | })),
26 | },
27 | validateStatus: () => true,
28 | });
29 |
30 | // Update React Query cache
31 | // queryClient.setQueryData(endpoint, response.data);
32 |
33 | setLoading(false);
34 |
35 | return response;
36 | } catch (err) {
37 | console.error(err);
38 | setLoading(false);
39 | return null;
40 | }
41 | };
42 |
43 | const post = async (endpoint = '', headers = {}, body = {}, contentType = 'application/json') => {
44 | // const queryClient = useQueryClient(); // React Query client instance
45 | setLoading(true);
46 | try {
47 | const { data, status } = await axios.post(process.env.NEXT_PUBLIC_API_URL + endpoint, body, {
48 | headers: {
49 | ...headers,
50 | 'Content-Type': contentType,
51 | Authorization:
52 | 'Bearer ' +
53 | (await getAccessTokenSilently({
54 | authorizationParams: {
55 | audience: process.env.NEXT_PUBLIC_AUTH0_AUDIENCE,
56 | },
57 | })),
58 | },
59 | validateStatus: () => true,
60 | });
61 |
62 | // Invalidate queries to refetch updated data
63 | // queryClient.invalidateQueries(endpoint);
64 |
65 | setLoading(false);
66 |
67 | return { data, status };
68 | } catch (err) {
69 | console.error(err);
70 | setLoading(false);
71 | return null;
72 | }
73 | };
74 |
75 | const put = async (endpoint = '', headers = {}, body = {}) => {
76 | // const queryClient = useQueryClient(); // React Query client instance
77 | setLoading(true);
78 | try {
79 | const { data, status } = await axios.put(process.env.NEXT_PUBLIC_API_URL + endpoint, body, {
80 | headers: {
81 | ...headers,
82 | 'Content-Type': 'application/json',
83 | Authorization:
84 | 'Bearer ' +
85 | (await getAccessTokenSilently({
86 | authorizationParams: {
87 | audience: process.env.NEXT_PUBLIC_AUTH0_AUDIENCE,
88 | },
89 | })),
90 | },
91 | validateStatus: () => true,
92 | });
93 |
94 | // Invalidate queries to refetch updated data
95 |
96 | setLoading(false);
97 |
98 | return { data, status };
99 | } catch (err) {
100 | console.error(err);
101 | setLoading(false);
102 | return null;
103 | }
104 | };
105 |
106 | const del = async (endpoint = '', headers = {}) => {
107 | // const queryClient = useQueryClient(); // React Query client instance
108 | setLoading(true);
109 | try {
110 | const { data, status } = await axios.delete(process.env.NEXT_PUBLIC_API_URL + endpoint, {
111 | headers: {
112 | ...headers,
113 | 'Content-Type': 'application/json',
114 | Authorization:
115 | 'Bearer ' +
116 | (await getAccessTokenSilently({
117 | authorizationParams: {
118 | audience: process.env.NEXT_PUBLIC_AUTH0_AUDIENCE,
119 | },
120 | })),
121 | },
122 | validateStatus: () => true,
123 | });
124 |
125 | // Invalidate queries to refetch updated data
126 | // queryClient.invalidateQueries(endpoint);
127 |
128 | setLoading(false);
129 |
130 | return { data, status };
131 | } catch (err) {
132 | console.error(err);
133 | setLoading(false);
134 | return null;
135 | }
136 | };
137 |
138 | return { loading, get, post, put, del };
139 | };
140 |
--------------------------------------------------------------------------------
/apps/web-admin/src/hooks/useTimeout.jsx:
--------------------------------------------------------------------------------
1 | import { useCallback, useEffect, useRef } from 'react';
2 |
3 | export default function useTimeout(callback, delay) {
4 | const callbackRef = useRef(callback);
5 | const timeoutRef = useRef();
6 |
7 | useEffect(() => {
8 | callbackRef.current = callback;
9 | }, [callback]);
10 |
11 | const set = useCallback(() => {
12 | timeoutRef.current = setTimeout(() => callbackRef.current(), delay);
13 | }, [delay]);
14 |
15 | const clear = useCallback(() => {
16 | timeoutRef.current && clearTimeout(timeoutRef.current);
17 | }, []);
18 |
19 | useEffect(() => {
20 | set();
21 | return clear;
22 | }, [delay, set, clear]);
23 |
24 | const reset = useCallback(() => {
25 | clear();
26 | set();
27 | }, [clear, set]);
28 |
29 | return { reset, clear };
30 | }
31 |
--------------------------------------------------------------------------------
/apps/web-admin/src/hooks/useWrapper.jsx:
--------------------------------------------------------------------------------
1 | import { useQuery } from 'react-query';
2 | // import useRequests from './useRequests'; // Import your custom useRequests hook
3 | import { useRequests } from './useRequests';
4 | import { useMutation, useQueryClient } from 'react-query';
5 | const useWrapper = () => {
6 | const queryClient = useQueryClient(); // React Query client instance
7 | const useGetQuery = (key, endpoint, headers = {}, options = {}, setState = (value) => {}) => {
8 | const { get } = useRequests();
9 | return useQuery(
10 | key, // Unique query key
11 | () => get(endpoint, headers), // Function to fetch data
12 | // options, // React Query options like staleTime, refetchOnWindowFocus, etc.
13 | {
14 | ...options,
15 | // staleTime: 2 * 60 * 1000, // fresh for 2 minutes unless manually invalidated
16 | onSuccess: (response) => {
17 | if (response && response.status == 200) {
18 | setState(response);
19 | queryClient.setQueryData(endpoint, response);
20 | }
21 | },
22 | },
23 | );
24 | };
25 |
26 | const useMutationWrapper = (
27 | method,
28 | endpoint,
29 | headers = {},
30 | options = {},
31 | myFunction = (value) => {},
32 | ) => {
33 | // const queryClient = useQueryClient();
34 | const { post, put, del } = useRequests();
35 |
36 | const mutationFn = {
37 | post,
38 | put,
39 | delete: del,
40 | }[method];
41 | // //console.log(options?.invalidateKeys);
42 | // //console.log(method);
43 |
44 | return useMutation((body = {}) => mutationFn(endpoint, headers, body), {
45 | ...options,
46 | onSuccess: (data, variables, context) => {
47 | myFunction({ data, variables, context });
48 | const keysToInvalidate = options?.invalidateKeys || [];
49 |
50 | if (options?.invalidateDelay) {
51 | setTimeout(() => {
52 | keysToInvalidate.forEach((key) => queryClient.invalidateQueries(key));
53 | }, options.invalidateDelay);
54 | } else {
55 | keysToInvalidate.forEach((key) => queryClient.invalidateQueries(key));
56 | }
57 |
58 | if (options?.onSuccess) {
59 | options.onSuccess(data, variables, context);
60 | }
61 | },
62 | });
63 | };
64 |
65 | const usePostMutation = (endpoint, headers = {}, options = {}, myFunction = (value) => {}) => {
66 | return useMutationWrapper('post', endpoint, headers, options, myFunction);
67 | };
68 |
69 | const usePutMutation = (endpoint, headers = {}, options = {}, myFunction = (value) => {}) => {
70 | return useMutationWrapper('put', endpoint, headers, options, myFunction);
71 | };
72 |
73 | const useDeleteMutation = (endpoint, headers = {}, options = {}, myFunction = (value) => {}) => {
74 | return useMutationWrapper('delete', endpoint, headers, options, myFunction);
75 | };
76 |
77 | return { useGetQuery, usePostMutation, usePutMutation, useDeleteMutation };
78 | };
79 |
80 | export default useWrapper;
81 |
--------------------------------------------------------------------------------
/apps/web-admin/src/lib/utils.js:
--------------------------------------------------------------------------------
1 | import { clsx } from 'clsx';
2 | import { twMerge } from 'tailwind-merge';
3 |
4 | export function cn(...inputs) {
5 | return twMerge(clsx(inputs));
6 | }
7 |
--------------------------------------------------------------------------------
/apps/web-admin/src/pages/404.jsx:
--------------------------------------------------------------------------------
1 | // pages/404.tsx
2 | import { Box, Heading, Text, Button } from '@chakra-ui/react';
3 | import Link from 'next/link';
4 | // import { GetServerSideProps } from 'next';
5 |
6 | const Custom404 = () => {
7 | return (
8 |
18 |
25 | 404
26 |
27 |
28 | Page Not Found
29 |
30 |
31 | The page you are looking for does not seem to exist.
32 |
33 |
34 |
35 |
41 | Go to Home
42 |
43 |
44 |
45 | );
46 | };
47 |
48 | /*export const getServerSideProps = async (context) => {
49 | context.res.statusCode = 404;
50 | return {
51 | props: {},
52 | };
53 | };*/
54 |
55 | export default Custom404;
56 |
--------------------------------------------------------------------------------
/apps/web-admin/src/pages/[orgId]/emailer/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useRouter } from 'next/router';
3 | import DashboardLayout from '@/layouts/DashboardLayout';
4 | import ComingSoon from '@/components/ComingSoon';
5 |
6 | const EmailSettings = () => {
7 | const router = useRouter();
8 | const { orgId } = router.query;
9 | return (
10 |
11 |
12 |
13 | );
14 | };
15 |
16 | export default EmailSettings;
17 |
--------------------------------------------------------------------------------
/apps/web-admin/src/pages/[orgId]/events/[eventId]/attributes/[attributeId]/index.jsx:
--------------------------------------------------------------------------------
1 | import { useRouter } from 'next/router';
2 | import { useEffect, useState } from 'react';
3 |
4 | import { Button, Flex } from '@chakra-ui/react';
5 | import { StyledBox, StyledText } from '@/components/ui/StyledComponents';
6 |
7 | import DashboardLayout from '@/layouts/DashboardLayout';
8 | import DataDisplay from '@/components/DataDisplay';
9 |
10 | // import { useFetch } from '@/hooks/useFetch';
11 | import { useAlert } from '@/hooks/useAlert';
12 | import useWrapper from '@/hooks/useWrapper';
13 | const columns = [
14 | { field: 'id', headerName: 'ID', width: 200 },
15 | { field: 'value', headerName: 'Value', width: 200 },
16 | { field: 'firstName', headerName: 'First Name', width: 200 },
17 | { field: 'lastName', headerName: 'Last Name', width: 200 },
18 | { field: 'email', headerName: 'Email', width: 200 },
19 | { field: 'phone', headerName: 'Phone', width: 200 },
20 | { field: 'checkInKey', headerName: 'Check In Key', width: 200 },
21 | { field: 'addedAt', headerName: 'Added At', width: 200 },
22 | ];
23 |
24 | export default function AttributeById() {
25 | const router = useRouter();
26 | const { orgId, eventId, attributeId } = router.query;
27 | const showAlert = useAlert();
28 |
29 | // const { loading, get } = useFetch();
30 |
31 | const [attribute, setAttribute] = useState({});
32 | const [attributeDetails, setAttributeDetails] = useState([]);
33 | const { useGetQuery } = useWrapper();
34 | const { isFetching: loading } = useGetQuery(
35 | `/core/organizations/${orgId}/events/${eventId}/attributes/${attributeId}`,
36 | `/core/organizations/${orgId}/events/${eventId}/attributes/${attributeId}`,
37 | {},
38 | {
39 | onError: (error) => {
40 | showAlert({
41 | title: 'Error',
42 | description: data.error,
43 | status: 'error',
44 | });
45 | },
46 | },
47 | (response) => {
48 | // //console.log(response.data);
49 | setAttribute(response.data.attribute || []);
50 | setAttributeDetails(response.data.attribute?.participantAttributeDetails || []);
51 | },
52 | );
53 | // //console.log(loading);
54 | // useEffect(() => {
55 | // const fetchAttribute = async () => {
56 | // const { data, status } = await get(
57 | // `/core/organizations/${orgId}/events/${eventId}/attributes/${attributeId}`,
58 | // );
59 | // if (status === 200) {
60 | // setAttribute(data.attribute || {});
61 | // setAttributeDetails(data.attribute?.participantAttributeDetails || []);
62 | // } else {
63 | // showAlert({
64 | // title: 'Error',
65 | // description: data.error,
66 | // status: 'error',
67 | // });
68 | // }
69 | // };
70 | // fetchAttribute();
71 | // }, []);
72 |
73 | return (
74 |
79 | {
81 | router.push(
82 | `/organizations/${orgId}/events/${eventId}/attributes/${attributeId}/settings`,
83 | );
84 | }}
85 | isLoading={loading}
86 | disabled="true"
87 | >
88 | Attribute Settings
89 |
90 | >
91 | }
92 | debugInfo={JSON.stringify(attribute)}
93 | >
94 | {
99 | router.push(`/${orgId}/events/${eventId}/participants/${row.id}`);
100 | }}
101 | />
102 | {!loading && attributeDetails.length === 0 ? (
103 |
104 |
105 | No participants assigned
106 |
107 |
108 | Assign participants to see details
109 |
110 |
111 | ) : (
112 | <>>
113 | )}
114 |
115 | );
116 | }
117 |
--------------------------------------------------------------------------------
/apps/web-admin/src/pages/[orgId]/events/[eventId]/attributes/index.jsx:
--------------------------------------------------------------------------------
1 | import { useRouter } from 'next/router';
2 | import { useEffect, useState } from 'react';
3 | import {
4 | Button,
5 | Modal,
6 | ModalOverlay,
7 | ModalContent,
8 | ModalHeader,
9 | ModalBody,
10 | ModalCloseButton,
11 | useDisclosure,
12 | } from '@chakra-ui/react';
13 | import DashboardLayout from '@/layouts/DashboardLayout';
14 | // import { useFetch } from '@/hooks/useFetch';
15 | import { useAlert } from '@/hooks/useAlert';
16 | import DataDisplay from '@/components/DataDisplay';
17 | import NewAttributeForm from './new';
18 | import { StyledBox, StyledButton, StyledText } from '@/components/ui/StyledComponents';
19 | import useWrapper from '@/hooks/useWrapper';
20 | import NavigationMenu from '../navigationmenu';
21 | import {
22 | ChevronLeftIcon,
23 | ChevronDownIcon,
24 | Menu,
25 | MenuButton,
26 | MenuList,
27 | MenuItem,
28 | useColorMode,
29 | } from '@chakra-ui/icons';
30 | import CustomStyledBox from '@/pages/CustomStyledBox';
31 |
32 | const columns = [
33 | { field: 'name', headerName: 'Name', width: 200 },
34 | {
35 | field: 'numberOfParticipantsWithAttributeAssigned',
36 | headerName: 'No of Participants Assigned',
37 | width: 200,
38 | },
39 | ];
40 |
41 | export default function Attributes() {
42 | const { colorMode } = useColorMode();
43 | const router = useRouter();
44 | const { orgId, eventId } = router.query;
45 | const showAlert = useAlert();
46 | const { isOpen, onOpen, onClose } = useDisclosure();
47 | // const { loading, get } = useFetch();
48 |
49 | const [attributes, setAttributes] = useState([]);
50 | const { useGetQuery } = useWrapper();
51 | const { isFetching: loading } = useGetQuery(
52 | `/core/organizations/${orgId}/events/${eventId}/attributes`,
53 | `/core/organizations/${orgId}/events/${eventId}/attributes`,
54 | {},
55 | {
56 | onError: (error) => {
57 | showAlert({
58 | title: 'Error',
59 | description: data.error,
60 | status: 'error',
61 | });
62 | },
63 | },
64 | (response) => {
65 | setAttributes(response.data.attributes || []);
66 | },
67 | );
68 |
69 | return (
70 |
75 |
80 |
81 | Add Attribute
82 |
83 |
84 | }
85 | />
86 |
87 | {/* */}
88 |
89 | {
94 | router.push(`/${orgId}/events/${eventId}/attributes/${row.id}`);
95 | }}
96 | />
97 | {!loading && attributes.length === 0 ? (
98 |
99 |
100 | No attributes created
101 |
102 |
103 | Add attributes and assign participants to see details
104 |
105 |
106 | ) : (
107 | <>>
108 | )}
109 |
110 |
111 |
112 |
113 |
120 | Add Attribute
121 |
122 |
123 |
127 |
128 |
129 |
130 |
131 |
132 | );
133 | }
134 |
--------------------------------------------------------------------------------
/apps/web-admin/src/pages/[orgId]/events/[eventId]/extras/[extraId]/check-in/index.jsx:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react';
2 | import { useRouter } from 'next/router';
3 |
4 | import { Button, FormControl, FormLabel, Select } from '@chakra-ui/react';
5 |
6 | import DashboardLayout from '@/layouts/DashboardLayout';
7 |
8 | import { useAlert } from '@/hooks/useAlert';
9 | import useWrapper from '@/hooks/useWrapper';
10 |
11 | export default function CheckInExtra() {
12 | const showAlert = useAlert();
13 |
14 | const router = useRouter();
15 | const { orgId, eventId, extraId } = router.query;
16 |
17 | const [participantId, setParticipantId] = useState(null);
18 | const [participants, setParticipants] = useState([]);
19 | const { usePostMutation, useGetQuery } = useWrapper();
20 | const { mutate: handleCheckInMutation } = usePostMutation(
21 | `/core/organizations/${orgId}/events/${eventId}/extras/${extraId}/check-in`,
22 | {},
23 | {
24 | onSuccess: (response) => {
25 | showAlert({
26 | title: 'Success',
27 | description: 'Extra for participant has been checked in successfully.',
28 | status: 'success',
29 | });
30 | router.push(`/${orgId}/events/${eventId}/extras/${extraId}`);
31 | },
32 | onError: (error) => {
33 | showAlert({
34 | title: 'Error',
35 | description: data.error,
36 | status: 'error',
37 | });
38 | },
39 | },
40 | );
41 | const { isFetching: loading } = useGetQuery(
42 | `/core/organizations/${orgId}/events/${eventId}/participants`,
43 | `/core/organizations/${orgId}/events/${eventId}/participants`,
44 | {},
45 | {
46 | onError: (error) => {
47 | showAlert({
48 | title: 'Error',
49 | description: data.error,
50 | status: 'error',
51 | });
52 | },
53 | },
54 | (response) => {
55 | setParticipants(response.data.participants);
56 | },
57 | );
58 | const handleSubmit = async (e) => {
59 | e.preventDefault();
60 | handleCheckInMutation({
61 | participantId,
62 | checkedInAt: new Date().toISOString(),
63 | });
64 | };
65 |
66 | return (
67 |
72 |
125 |
126 | );
127 | }
128 |
--------------------------------------------------------------------------------
/apps/web-admin/src/pages/[orgId]/events/[eventId]/extras/[extraId]/index.jsx:
--------------------------------------------------------------------------------
1 | import { useRouter } from 'next/router';
2 | import { useEffect, useState } from 'react';
3 |
4 | import { Button, Flex } from '@chakra-ui/react';
5 | import { StyledBox, StyledText } from '@/components/ui/StyledComponents';
6 |
7 | import DashboardLayout from '@/layouts/DashboardLayout';
8 | import DataDisplay from '@/components/DataDisplay';
9 |
10 | // import { useFetch } from '@/hooks/useFetch';
11 | import { useAlert } from '@/hooks/useAlert';
12 | import useWrapper from '@/hooks/useWrapper';
13 |
14 | const columns = [
15 | { field: 'firstName', headerName: 'First Name', width: 200 },
16 | { field: 'lastName', headerName: 'Last Name', width: 200 },
17 | { field: 'email', headerName: 'Email', width: 200 },
18 | { field: 'phone', headerName: 'Phone', width: 200 },
19 | { field: 'checkInKey', headerName: 'Check In Key', width: 200 },
20 | { field: 'addedAt', headerName: 'Added At', width: 200 },
21 | {
22 | field: 'status',
23 | headerName: 'Status',
24 | width: 200,
25 | valueGetter: (params) => params.row?.checkedIn?.status,
26 | },
27 | {
28 | field: 'at',
29 | headerName: 'Checked In At',
30 | width: 200,
31 | valueGetter: (params) => params.row?.checkedIn?.at,
32 | },
33 | {
34 | field: 'email',
35 | headerName: 'Checked In By Email',
36 | width: 200,
37 | valueGetter: (params) => params.row?.checkedIn?.by?.email,
38 | },
39 | ];
40 |
41 | export default function ExtraById() {
42 | const router = useRouter();
43 | const { orgId, eventId, extraId } = router.query;
44 | const showAlert = useAlert();
45 |
46 | // const { loading, get } = useFetch();
47 |
48 | const [extra, setExtra] = useState({});
49 | const [extraDetails, setExtraDetails] = useState([]);
50 | const { useGetQuery } = useWrapper();
51 | const { isFetching: loading } = useGetQuery(
52 | `/core/organizations/${orgId}/events/${eventId}/extras/${extraId}`,
53 | `/core/organizations/${orgId}/events/${eventId}/extras/${extraId}`,
54 | {},
55 | {
56 | onError: (error) => {
57 | showAlert({
58 | title: 'Error',
59 | description: error,
60 | status: 'error',
61 | });
62 | },
63 | },
64 | (response) => {
65 | setExtraDetails(response.data.extra?.participantExtraDetails || []);
66 | },
67 | );
68 |
69 | return (
70 |
75 | {
77 | router.push(`/${orgId}/events/${eventId}/extras/${extraId}/check-in`);
78 | }}
79 | isLoading={loading}
80 | disabled="true"
81 | >
82 | Check In
83 |
84 | {
86 | router.push(
87 | `/organizations/${orgId}/events/${eventId}/extras/${extraId}/check-in/scanner`,
88 | );
89 | }}
90 | isLoading={loading}
91 | disabled="true"
92 | >
93 | Scanner
94 |
95 | {
97 | router.push(`/${orgId}/events/${eventId}/extras/${extraId}/settings`);
98 | }}
99 | isLoading={loading}
100 | disabled="true"
101 | >
102 | Extras Settings
103 |
104 | >
105 | }
106 | debugInfo={extra}
107 | >
108 | {
113 | router.push(`/${orgId}/events/${eventId}/participants/${row.id}`);
114 | }}
115 | />
116 | {!loading && extraDetails.length === 0 ? (
117 |
118 |
119 | No participants assigned
120 |
121 |
122 | Assign participants for extras to see details
123 |
124 |
125 | ) : (
126 | <>>
127 | )}
128 |
129 | );
130 | }
131 |
--------------------------------------------------------------------------------
/apps/web-admin/src/pages/[orgId]/events/[eventId]/extras/index.jsx:
--------------------------------------------------------------------------------
1 | import { useRouter } from 'next/router';
2 | import { useEffect, useState } from 'react';
3 | import {
4 | Button,
5 | Modal,
6 | ModalOverlay,
7 | ModalContent,
8 | ModalHeader,
9 | ModalBody,
10 | ModalCloseButton,
11 | useDisclosure,
12 | useColorMode,
13 | } from '@chakra-ui/react';
14 | import { StyledBox, StyledButton, StyledText } from '@/components/ui/StyledComponents';
15 | import DashboardLayout from '@/layouts/DashboardLayout';
16 | // import { useFetch } from '@/hooks/useFetch';
17 | import { useAlert } from '@/hooks/useAlert';
18 | import DataDisplay from '@/components/DataDisplay';
19 | import NewExtraForm from './new'; // Import the form component
20 | import useWrapper from '@/hooks/useWrapper';
21 | import NavigationMenu from '../navigationmenu';
22 | import {
23 | ChevronLeftIcon,
24 | ChevronDownIcon,
25 | Menu,
26 | MenuButton,
27 | MenuList,
28 | MenuItem,
29 | } from '@chakra-ui/icons';
30 | import CustomStyledBox from '@/pages/CustomStyledBox';
31 |
32 | const columns = [
33 | { field: 'name', headerName: 'Name', width: 200 },
34 | {
35 | field: 'numberOfParticipantsWithExtrasAssigned',
36 | headerName: 'No of Participants Assigned',
37 | width: 200,
38 | },
39 | {
40 | field: 'numberOfParticipantsWithExtrasCheckedIn',
41 | headerName: 'No of Participants Checked In',
42 | width: 200,
43 | },
44 | ];
45 |
46 | export default function Extras() {
47 | const { colorMode } = useColorMode();
48 | const router = useRouter();
49 | const { orgId, eventId } = router.query;
50 | const showAlert = useAlert();
51 | const { isOpen, onOpen, onClose } = useDisclosure(); // Chakra UI hook for modal control
52 | // const { loading, get } = useFetch();
53 |
54 | const [extras, setExtras] = useState([]);
55 | const { useGetQuery } = useWrapper();
56 | const { isFetching: loading } = useGetQuery(
57 | `/core/organizations/${orgId}/events/${eventId}/extras`,
58 | `/core/organizations/${orgId}/events/${eventId}/extras`,
59 | {},
60 | {
61 | onError: (error) => {
62 | showAlert({
63 | title: 'Error',
64 | description: error,
65 | status: 'error',
66 | });
67 | },
68 | },
69 | (response) => {
70 | setExtras(response.data.extras || []);
71 | },
72 | );
73 |
74 | return (
75 |
80 |
85 |
86 | Add Extras
87 |
88 |
89 | }
90 | />
91 |
92 | {/* */}
93 |
94 | {
99 | router.push(`/${orgId}/events/${eventId}/extras/${row.id}`);
100 | }}
101 | />
102 | {!loading && extras.length === 0 ? (
103 |
104 |
105 | No extras created
106 |
107 |
108 | Add extras assigned and checked in to see details
109 |
110 |
111 | ) : (
112 | <>>
113 | )}
114 |
115 | {/* Modal for creating a new extra */}
116 |
117 |
118 |
119 |
126 | Add Extra
127 |
128 |
129 |
133 | {/* Render the form from new/index.js */}
134 |
135 |
136 |
137 |
138 |
139 | );
140 | }
141 |
--------------------------------------------------------------------------------
/apps/web-admin/src/pages/[orgId]/events/[eventId]/extras/new/index.jsx:
--------------------------------------------------------------------------------
1 | /*import { useState } from 'react';
2 | import { useRouter } from 'next/router';
3 | import { Button, FormControl, FormLabel, Input } from '@chakra-ui/react';
4 | import { useAlert } from '@/hooks/useAlert';
5 | import { useFetch } from '@/hooks/useFetch';
6 |
7 | export default function NewExtraForm({ onClose }) {
8 | // Accept onClose to close the modal
9 | const { loading, post } = useFetch();
10 | const showAlert = useAlert();
11 | const router = useRouter();
12 | const { orgId, eventId } = router.query;
13 |
14 | const [name, setName] = useState('');
15 |
16 | const handleSubmit = async (e) => {
17 | e.preventDefault();
18 | const { data, status } = await post(
19 | `/core/organizations/${orgId}/events/${eventId}/extras`,
20 | {},
21 | { name },
22 | );
23 | if (status === 200) {
24 | showAlert({
25 | title: 'Success',
26 | description: 'Extra has been added successfully.',
27 | status: 'success',
28 | });
29 | onClose(); // Close the modal after success
30 | router.push(`/${orgId}/events/${eventId}/extras`); // Redirect to extras list
31 | } else {
32 | showAlert({
33 | title: 'Error',
34 | description: data.error,
35 | status: 'error',
36 | });
37 | }
38 | };
39 |
40 | return (
41 |
57 | );
58 | }
59 |
60 | */
61 | import { useState } from 'react';
62 | import { useRouter } from 'next/router';
63 | import { Button, FormControl, FormLabel, Input, useColorMode } from '@chakra-ui/react';
64 | import { useAlert } from '@/hooks/useAlert';
65 | import { useFetch } from '@/hooks/useFetch';
66 | import useWrapper from '@/hooks/useWrapper';
67 | import { StyledText } from '@/components/ui/StyledComponents';
68 |
69 | export default function NewExtraForm({ onClose }) {
70 | const { colorMode } = useColorMode();
71 | // Accept onClose to close the modal
72 | const { loading, post } = useFetch();
73 | const showAlert = useAlert();
74 | const router = useRouter();
75 | const { orgId, eventId } = router.query;
76 |
77 | const [name, setName] = useState('');
78 | const { useGetQuery, usePostMutation } = useWrapper();
79 | const { mutate: handleExtraMutation } = usePostMutation(
80 | `/core/organizations/${orgId}/events/${eventId}/extras`,
81 | {},
82 | {
83 | onSuccess: (response) => {
84 | showAlert({
85 | title: 'Success',
86 | description: 'Extra has been added successfully.',
87 | status: 'success',
88 | });
89 | onClose(); // Close the modal after success
90 | router.push(`/${orgId}/events/${eventId}/extras`); // Redirect to extras list
91 | },
92 | onError: (error) => {
93 | showAlert({
94 | title: 'Error',
95 | description: error,
96 | status: 'error',
97 | });
98 | },
99 | invalidateKeys: [`/core/organizations/${orgId}/events/${eventId}/extras`],
100 | },
101 | );
102 | const handleSubmit = async (e) => {
103 | e.preventDefault();
104 | handleExtraMutation({
105 | name,
106 | });
107 | };
108 |
109 | return (
110 |
137 | );
138 | }
139 |
--------------------------------------------------------------------------------
/apps/web-admin/src/pages/[orgId]/events/[eventId]/participants/[participantId]/attributes/index.jsx:
--------------------------------------------------------------------------------
1 | import { useRouter } from 'next/router';
2 |
3 | import {
4 | Box,
5 | Flex,
6 | Table,
7 | TableCaption,
8 | Tbody,
9 | Td,
10 | Tfoot,
11 | Th,
12 | Thead,
13 | Tr,
14 | TableContainer,
15 | Text,
16 | } from '@chakra-ui/react';
17 |
18 | // import { useFetch } from '@/hooks/useFetch';
19 | import useWrapper from '@/hooks/useWrapper';
20 | import DashboardLayout from '@/layouts/DashboardLayout';
21 | import { useEffect, useState } from 'react';
22 |
23 | export default function Events() {
24 | const router = useRouter();
25 |
26 | const { orgId, eventId, participantId } = router.query;
27 |
28 | // const { loading, get } = useFetch();
29 |
30 | const [participantAttributes, setParticipantAttributes] = useState([]);
31 | const { useGetQuery } = useWrapper();
32 |
33 | const { isFetching: loading } = useGetQuery(
34 | `/core/organizations/${orgId}/events/${eventId}/participants/${participantId}/attributes`,
35 | `/core/organizations/${orgId}/events/${eventId}/participants/${participantId}/attributes`,
36 | {},
37 | {
38 | onSuccess: (response) => {
39 | setParticipantAttributes(response.data.participantAttributes || []);
40 | //console.log(data);
41 | },
42 | onError: (error) => {
43 | showAlert({
44 | title: 'Error',
45 | description: error,
46 | status: 'error',
47 | });
48 | },
49 | },
50 | );
51 | // useEffect(() => {
52 | // const fetchParticipantAttributes = async () => {
53 | // const { data, status } = await get(
54 | // `/core/organizations/${orgId}/events/${eventId}/participants/${participantId}/attributes`,
55 | // );
56 | // setParticipantAttributes(data.participantAttributes || []);
57 | // //console.log(data);
58 | // };
59 | // fetchParticipantAttributes();
60 | // }, [orgId, eventId, participantId]);
61 |
62 | return (
63 |
64 |
72 |
73 |
74 | Participant Attributes
75 |
76 |
77 |
78 | {JSON.stringify(participantAttributes)}
79 |
80 |
81 |
82 | );
83 | }
84 |
--------------------------------------------------------------------------------
/apps/web-admin/src/pages/[orgId]/events/[eventId]/participants/[participantId]/index.jsx:
--------------------------------------------------------------------------------
1 | import { useRouter } from 'next/router';
2 | import { useEffect, useState } from 'react';
3 |
4 | import { Text, Flex } from '@chakra-ui/react';
5 | import { StyledBox, StyledText } from '@/components/ui/StyledComponents';
6 |
7 | import DashboardLayout from '@/layouts/DashboardLayout';
8 |
9 | import { useFetch } from '@/hooks/useFetch';
10 | import { useAlert } from '@/hooks/useAlert';
11 | import useWrapper from '@/hooks/useWrapper';
12 |
13 | import DataDisplay from '@/components/DataDisplay';
14 |
15 | const attributeColumns = [
16 | { field: 'name', headerName: 'Name', width: 200 },
17 | { field: 'value', headerName: 'Value', width: 200 },
18 | ];
19 |
20 | const extraColumns = [
21 | { field: 'name', headerName: 'Name', width: 200 },
22 | { field: 'assigned', headerName: 'Assigned', width: 200 },
23 | {
24 | field: 'status',
25 | headerName: 'Checked In',
26 | width: 200,
27 | valueGetter: (params) => (params.row?.checkIn?.status ? 'true' : 'false'),
28 | },
29 | {
30 | field: 'at',
31 | headerName: 'Checked In At',
32 | width: 200,
33 | valueGetter: (params) => params.row?.checkIn?.at,
34 | },
35 | {
36 | field: 'by',
37 | headerName: 'Checked In By',
38 | width: 200,
39 | valueGetter: (params) => params.row?.checkIn?.by?.email,
40 | },
41 | ];
42 |
43 | export default function ParticipantById() {
44 | const router = useRouter();
45 | const { orgId, eventId, participantId } = router.query;
46 | const showAlert = useAlert();
47 |
48 | const { loading, get } = useFetch();
49 |
50 | const { useGetQuery } = useWrapper();
51 |
52 | const [participant, setParticipant] = useState([]);
53 | const [participantAttributes, setParticipantAttributes] = useState([]);
54 | const [participantExtras, setParticipantExtras] = useState([]);
55 | const [participantCheckIn, setParticipantCheckIn] = useState({});
56 |
57 | const { data, status, error } = useGetQuery(
58 | `/core/organizations/${orgId}/events/${eventId}/participants/${participantId}`,
59 | `/core/organizations/${orgId}/events/${eventId}/participants/${participantId}`,
60 | {},
61 | {},
62 | (data) => {
63 | setParticipant(data.data.participant || []);
64 | setParticipantAttributes(data.data.participant.attributes || []);
65 | setParticipantExtras(data.data.participant.extras || []);
66 | setParticipantCheckIn(data.data.participant.checkIn || {});
67 | },
68 | );
69 |
70 | return (
71 |
76 |
77 |
78 | ID: {participant.id}
79 | Fist Name: {participant.firstName}
80 | Last Name: {participant.lastName}
81 | Email: {participant.email}
82 | Phone: {participant.phone}
83 | Check In Key: {participant.checkInKey}
84 |
85 | Check In
86 |
87 | Status: {participantCheckIn.status ? 'true' : 'false'}
88 | Checked In At: {participantCheckIn.checkedInAt}
89 |
90 | Checked In By: {participantCheckIn.checkedInByEmail}
91 |
92 |
93 | Attributes - {participant.numberOfAttributesAssigned} assigned
94 |
95 | Extras - {participant.numberOfExtrasAssigned} assigned
96 | Extras - {participant.numberOfExtrasCheckedIn} checked in
97 |
98 |
99 | {!loading && participant.length === 0 ? (
100 |
101 |
102 | No participants created
103 |
104 |
105 | Add participants and assignment to see details
106 |
107 |
108 | ) : (
109 | <>>
110 | )}
111 |
112 | );
113 | }
114 |
--------------------------------------------------------------------------------
/apps/web-admin/src/pages/[orgId]/index.jsx:
--------------------------------------------------------------------------------
1 | import { useRouter } from 'next/router';
2 | import { useEffect, useState } from 'react';
3 |
4 | import { Button, Flex } from '@chakra-ui/react';
5 |
6 | import DashboardLayout from '@/layouts/DashboardLayout';
7 |
8 | // import { useFetch } from '@/hooks/useFetch';
9 | import { useAlert } from '@/hooks/useAlert';
10 | import { useAuth0 } from '@auth0/auth0-react';
11 | import { useContext } from 'react';
12 | import { account } from '@/contexts/MyContext';
13 | import { useMemo } from 'react';
14 | import useWrapper from '@/hooks/useWrapper';
15 |
16 | export default function OrganizationById() {
17 | const router = useRouter();
18 | const { orgId } = router.query;
19 | const { user, isAuthenticated } = useAuth0();
20 | const { useGetQuery } = useWrapper();
21 | // //console.log(user);
22 | const showAlert = useAlert();
23 |
24 | // const { loading, get } = useFetch();
25 | const { accountDetails, setAccountDetails } = useContext(account);
26 | // //console.log(accountDetails.orgId);
27 | // //console.log(orgId);
28 | // //console.log(
29 | // orgId !== undefined , orgId !== accountDetails?.orgId , accountDetails?.orgId === undefined,
30 | // );
31 | // //console.log(orgId, accountDetails.orgId);
32 |
33 | const {
34 | data,
35 | status,
36 | error,
37 | isFetching: loading,
38 | } = useGetQuery(
39 | `/core/organizations/${orgId}`,
40 | `/core/organizations/${orgId}`,
41 | {}, // headers
42 | {}, // options
43 | (data) => {
44 | setAccountDetails((prev) => ({
45 | ...prev,
46 | name: data.data.organization.name || '',
47 | nEvents: data.data.organization.numberOfEvents || 0,
48 | nMembers: data.data.organization.numberOfMembers || 0,
49 | }));
50 | },
51 | );
52 |
53 | // Use useEffect to respond to the result of the hook
54 | // Only trigger when data, status, or error changes
55 | // //console.log(accountDetails?.orgId);
56 | return (
57 |
62 |
63 | {
65 | router.push(`/${orgId}/events`);
66 | }}
67 | isLoading={loading}
68 | >
69 | Events
70 |
71 | {
73 | router.push(`/${orgId}/members`);
74 | }}
75 | isLoading={loading}
76 | >
77 | Members
78 |
79 |
80 |
81 | );
82 | }
83 |
--------------------------------------------------------------------------------
/apps/web-admin/src/pages/[orgId]/members/new/index.jsx:
--------------------------------------------------------------------------------
1 | /*import { useState } from 'react';
2 | import { useRouter } from 'next/router';
3 | import { Button, FormControl, FormLabel, Input, Select } from '@chakra-ui/react';
4 | import { useAlert } from '@/hooks/useAlert';
5 | import { useFetch } from '@/hooks/useFetch';
6 |
7 | export default function NewMemberForm({ onClose }) {
8 | const { loading, post } = useFetch();
9 | const showAlert = useAlert();
10 | const router = useRouter();
11 | const { orgId } = router.query;
12 |
13 | const [email, setEmail] = useState('');
14 | const [role, setRole] = useState('USER');
15 |
16 | const handleSubmit = async (e) => {
17 | e.preventDefault();
18 | const { data, status } = await post(
19 | `/core/organizations/${orgId}/members`,
20 | {},
21 | {
22 | email,
23 | role,
24 | },
25 | );
26 | if (status === 200) {
27 | showAlert({
28 | title: 'Success',
29 | description: 'Member added successfully',
30 | status: 'success',
31 | });
32 | onClose();
33 | router.push(`/${orgId}/members`);
34 | } else {
35 | showAlert({
36 | title: 'Error',
37 | description: data.error,
38 | status: 'error',
39 | });
40 | }
41 | };
42 |
43 | return (
44 |
67 | );
68 | }
69 | */
70 | import { useState } from 'react';
71 | import { useRouter } from 'next/router';
72 | import { Button, FormControl, FormLabel, Input, Select } from '@chakra-ui/react';
73 | import { useAlert } from '@/hooks/useAlert';
74 | import { useFetch } from '@/hooks/useFetch';
75 |
76 | export default function NewMemberForm({ onClose }) {
77 | const { loading, post } = useFetch();
78 | const showAlert = useAlert();
79 | const router = useRouter();
80 | const { orgId } = router.query;
81 |
82 | const [email, setEmail] = useState('');
83 | const [role, setRole] = useState('USER');
84 |
85 | const handleSubmit = async (e) => {
86 | e.preventDefault();
87 | const { data, status } = await post(
88 | `/core/organizations/${orgId}/members`,
89 | {},
90 | {
91 | email,
92 | role,
93 | },
94 | );
95 | if (status === 200) {
96 | showAlert({
97 | title: 'Success',
98 | description: 'Member added successfully',
99 | status: 'success',
100 | });
101 | onClose();
102 | router.push(`/${orgId}/members`);
103 | } else {
104 | showAlert({
105 | title: 'Error',
106 | description: data.error,
107 | status: 'error',
108 | });
109 | }
110 | };
111 |
112 | return (
113 |
136 | );
137 | }
138 |
--------------------------------------------------------------------------------
/apps/web-admin/src/pages/[orgId]/mycertificates/index.jsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 | import { useRouter } from 'next/router';
3 | import DashboardLayout from '@/layouts/DashboardLayout';
4 | // import CertifcateUploadBox from '@/components/CertificateUploadBox';
5 | // import ComingSoon from '@/components/ComingSoon';
6 | import dynamic from 'next/dynamic';
7 |
8 | const CertifcateUploadBox = dynamic(() => import('@/components/CertificateUploadBox'), {
9 | ssr: false,
10 | });
11 |
12 | export default function MyCertificates() {
13 | const router = useRouter();
14 | const { orgId } = router.query;
15 | return (
16 |
17 | {/* */}
18 |
19 |
20 | );
21 | }
22 |
23 | // export default MyCertificates;
24 |
--------------------------------------------------------------------------------
/apps/web-admin/src/pages/[orgId]/profile/index.jsx:
--------------------------------------------------------------------------------
1 | const MyProfile = () => {
2 | // unleash your creativity here
3 | return <>>;
4 | };
5 | export default MyProfile;
6 |
--------------------------------------------------------------------------------
/apps/web-admin/src/pages/_app.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import MyContext from '@/contexts/MyContext';
3 | import { extendTheme, ChakraProvider, withDefaultColorScheme } from '@chakra-ui/react';
4 | import { Auth0Provider } from '@auth0/auth0-react';
5 | import { ProtectedRoute } from '@/components/ProtectedRoute';
6 | import { ColorModeScript } from '@chakra-ui/icons';
7 | import 'react-day-picker/dist/style.css';
8 | import '../styles/globals.css';
9 | // import '@uiw/react-md-editor/markdown-editor.css';
10 | import { QueryClient, QueryClientProvider } from 'react-query';
11 | const queryClient = new QueryClient();
12 | // defaultOptions: {
13 | // queries: {
14 | // refetchOnWindowFocus: true, // Disable refetch on window focus globally
15 | // refetchOnReconnect: true, // Prevent refetch on network reconnect
16 | // staleTime: 60 * 1000, // Set data as fresh for 1 minute
17 | // },
18 | // },
19 |
20 | const theme = extendTheme({
21 | ...withDefaultColorScheme({
22 | colorScheme: 'teal',
23 | }),
24 | components: {
25 | Button: {
26 | _hover: {
27 | bg: 'auto',
28 | },
29 | },
30 | },
31 | });
32 |
33 | export default function App({ Component, pageProps }) {
34 | return (
35 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | );
57 | }
58 |
--------------------------------------------------------------------------------
/apps/web-admin/src/pages/_document.jsx:
--------------------------------------------------------------------------------
1 | import { Html, Head, Main, NextScript } from 'next/document';
2 |
3 | const Document = () => {
4 | return (
5 |
6 |
7 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | );
18 | };
19 |
20 | export default Document;
21 |
--------------------------------------------------------------------------------
/apps/web-admin/src/pages/index.jsx:
--------------------------------------------------------------------------------
1 | 'use-client';
2 |
3 | import { useEffect } from 'react';
4 | import { useRouter } from 'next/router';
5 | import { useFetch } from '@/hooks/useFetch';
6 |
7 | import DashboardLayout from '@/layouts/DashboardLayout';
8 | import Landing from '@/components/landing';
9 | import { useAuth0 } from '@auth0/auth0-react';
10 | import { useContext } from 'react';
11 | import { account } from '@/contexts/MyContext';
12 |
13 | function Dashboard() {
14 | const router = useRouter();
15 | const { user, isAuthenticated, isLoading, loginWithRedirect } = useAuth0();
16 | const { accountDetails } = useContext(account);
17 | useEffect(() => {
18 | console.log(accountDetails);
19 | if (
20 | accountDetails &&
21 | accountDetails.orgId &&
22 | router.asPath !== `/${accountDetails.orgId}/events`
23 | ) {
24 | // // //console.log('route')
25 | router.replace(`/${accountDetails.orgId}/events`);
26 | }
27 | }, [isAuthenticated, accountDetails]);
28 |
29 | return {!isAuthenticated && } ;
30 | }
31 |
32 | export default Dashboard;
33 |
--------------------------------------------------------------------------------
/apps/web-admin/src/pages/onboarding/verify-email/index.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react';
2 |
3 | import { useAuth0 } from '@auth0/auth0-react';
4 | import { useRouter } from 'next/router';
5 |
6 | import { Text, Flex } from '@chakra-ui/react';
7 |
8 | import DashboardLayout from '@/layouts/DashboardLayout';
9 | import { StyledText } from '@/components/ui/StyledComponents';
10 |
11 | export default function VerifyEmail() {
12 | const { user, isAuthenticated, isLoading } = useAuth0();
13 |
14 | const router = useRouter();
15 |
16 | useEffect(() => {
17 | if (isAuthenticated && user.email_verified) router.push('/');
18 | }, [user, isAuthenticated, isLoading]);
19 |
20 | return (
21 |
22 |
23 |
24 | Please verify your email to continue using the application. Do not forget to check the
25 | spam folder too.
26 |
27 |
28 |
29 | );
30 | }
31 |
--------------------------------------------------------------------------------
/apps/web-admin/src/utils/Rectangle 1.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/apps/web-admin/src/utils/prisma.js:
--------------------------------------------------------------------------------
1 | import { PrismaClient } from 'database';
2 |
3 | const prisma = new PrismaClient();
4 | export default prisma;
5 |
--------------------------------------------------------------------------------
/apps/web-admin/src/utils/rectangle.svg:
--------------------------------------------------------------------------------
1 |
9 |
10 |
17 | ;
18 |
--------------------------------------------------------------------------------
/apps/web-admin/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | darkMode: ['class'],
4 | content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'],
5 | theme: {
6 | extend: {
7 | borderRadius: {
8 | lg: 'var(--radius)',
9 | md: 'calc(var(--radius) - 2px)',
10 | sm: 'calc(var(--radius) - 4px)',
11 | },
12 | colors: {
13 | teal: {
14 | DEFAULT: '#0d9488',
15 | },
16 | background: 'hsl(var(--background))',
17 | foreground: 'hsl(var(--foreground))',
18 | card: {
19 | DEFAULT: 'hsl(var(--card))',
20 | foreground: 'hsl(var(--card-foreground))',
21 | },
22 | popover: {
23 | DEFAULT: 'hsl(var(--popover))',
24 | foreground: 'hsl(var(--popover-foreground))',
25 | },
26 | primary: {
27 | DEFAULT: 'hsl(var(--primary))',
28 | foreground: 'hsl(var(--primary-foreground))',
29 | },
30 | secondary: {
31 | DEFAULT: 'hsl(var(--secondary))',
32 | foreground: 'hsl(var(--secondary-foreground))',
33 | },
34 | muted: {
35 | DEFAULT: 'hsl(var(--muted))',
36 | foreground: 'hsl(var(--muted-foreground))',
37 | },
38 | accent: {
39 | DEFAULT: 'hsl(var(--accent))',
40 | foreground: 'hsl(var(--accent-foreground))',
41 | },
42 | destructive: {
43 | DEFAULT: 'hsl(var(--destructive))',
44 | foreground: 'hsl(var(--destructive-foreground))',
45 | },
46 | border: 'hsl(var(--border))',
47 | input: 'hsl(var(--input))',
48 | ring: 'hsl(var(--ring))',
49 | chart: {
50 | 1: 'hsl(var(--chart-1))',
51 | 2: 'hsl(var(--chart-2))',
52 | 3: 'hsl(var(--chart-3))',
53 | 4: 'hsl(var(--chart-4))',
54 | 5: 'hsl(var(--chart-5))',
55 | },
56 | },
57 | },
58 | },
59 | plugins: [require('tailwindcss-animate')],
60 | };
61 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "scripts": {
4 | "build": "turbo run build",
5 | "clean": "turbo run clean",
6 | "dev": "turbo run dev",
7 | "lint": "turbo run lint",
8 | "format": "prettier --write \"**/*.{ts,tsx,md}\"",
9 | "test": "turbo run test",
10 | "prepare": "husky install",
11 | "setup": "turbo run setup"
12 | },
13 | "devDependencies": {
14 | "@changesets/cli": "^2.27.10",
15 | "@turbo/gen": "^2.3.1",
16 | "eslint": "^9.15.0",
17 | "eslint-config-custom": "workspace:*",
18 | "husky": "^9.1.7",
19 | "lint-staged": "^15.2.10",
20 | "prettier": "^3.3.3",
21 | "turbo": "^2.3.1"
22 | },
23 | "packageManager": "pnpm@7.15.0",
24 | "name": "techno-event-management",
25 | "version": "0.0.0",
26 | "dependencies": {
27 | "@chakra-ui/form-control": "^2.2.0",
28 | "@chakra-ui/layout": "^2.3.1",
29 | "@chakra-ui/react": "^2.8.2",
30 | "@emotion/react": "^11.13.3",
31 | "@emotion/styled": "^11.13.0",
32 | "chakra-ui-autocomplete": "^1.4.5",
33 | "framer-motion": "^11.5.4",
34 | "react-dom": "18.3.1"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/packages/auth0-flows/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # auth0-flows
2 |
3 | ## 0.1.0
4 |
5 | ### Minor Changes
6 |
7 | - Initialise changeset
8 |
--------------------------------------------------------------------------------
/packages/auth0-flows/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "auth0-flows",
3 | "version": "0.1.0",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "auth0-flows",
9 | "version": "0.1.0",
10 | "license": "ISC"
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/packages/auth0-flows/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "auth0-flows",
3 | "version": "0.1.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC"
11 | }
12 |
--------------------------------------------------------------------------------
/packages/auth0-flows/src/add-user-to-database/index.js:
--------------------------------------------------------------------------------
1 | const axios = require('axios');
2 |
3 | exports.onExecutePostUserRegistration = async (event, api) => {
4 | try {
5 | const { user } = event;
6 |
7 | const userId = user.user_id;
8 | const email = user.email;
9 |
10 | const apiUrl = event.secrets.CORE_AUTH0_ACTIONS_API + '/api/auth/newuser';
11 |
12 | const { data, status } = await axios.post(
13 | apiUrl,
14 | {
15 | userId,
16 | email,
17 | },
18 | {
19 | headers: {
20 | 'Content-Type': 'application/json',
21 | Authorization: 'Bearer ' + event.secrets.AUTHORIZATION_SECRET,
22 | },
23 | validateStatus: () => true,
24 | },
25 | );
26 |
27 | if (status === 200) console.log(`Status: ${status} - Successfully added ${userId} to database`);
28 | else console.log(`Status: ${status} - Failed to add ${userId} to database`);
29 | } catch (error) {
30 | console.error('Error calling API:', error.message);
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/packages/database/.env.example:
--------------------------------------------------------------------------------
1 | DATABASE_URL="postgres://postgres.qdydcjeonvpvifdfcntz:QEvX7r28HZeoWMXr@aws-0-ap-south-1.pooler.supabase.com:5432/postgres"
2 |
--------------------------------------------------------------------------------
/packages/database/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | # Keep environment variables out of version control
3 | .env
4 |
--------------------------------------------------------------------------------
/packages/database/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # database
2 |
3 | ## 0.1.0
4 |
5 | ### Minor Changes
6 |
7 | - Initialise changeset
8 |
--------------------------------------------------------------------------------
/packages/database/index.ts:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | ...require('@prisma/client/index'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/database/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "database",
3 | "version": "0.1.0",
4 | "main": "./index.ts",
5 | "types": "./index.d.ts",
6 | "scripts": {
7 | "db:generate": "prisma generate",
8 | "db:push": "prisma db push --skip-generate",
9 | "setup": "node -e \"require('fs').copyFile('.env.example', '.env', (err) => {if (err) {//console.log(err);} else {//console.log('Prisma client generated and environment variables set up successfully');}});\" && prisma generate"
10 | },
11 | "dependencies": {
12 | "@prisma/client": "^5.7.1"
13 | },
14 | "devDependencies": {
15 | "@types/node": "^20.10.6",
16 | "prisma": "^5.22.0"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/packages/database/prisma/migrations/20240229114739_/migration.sql:
--------------------------------------------------------------------------------
1 | /*
2 | Warnings:
3 |
4 | - A unique constraint covering the columns `[email,eventId]` on the table `Participant` will be added. If there are existing duplicate values, this will fail.
5 | - Made the column `email` on table `Participant` required. This step will fail if there are existing NULL values in that column.
6 |
7 | */
8 | -- AlterTable
9 | ALTER TABLE "Participant" ALTER COLUMN "email" SET NOT NULL;
10 |
11 | -- CreateIndex
12 | CREATE UNIQUE INDEX "Participant_email_eventId_key" ON "Participant"("email", "eventId");
13 |
--------------------------------------------------------------------------------
/packages/database/prisma/migrations/20241025092424_registration/migration.sql:
--------------------------------------------------------------------------------
1 | -- CreateTable
2 | CREATE TABLE "Registrant" (
3 | "id" UUID NOT NULL DEFAULT gen_random_uuid(),
4 | "firstName" TEXT NOT NULL,
5 | "lastName" TEXT,
6 | "email" TEXT NOT NULL,
7 | "phone" TEXT,
8 | "eventId" UUID NOT NULL,
9 | "organizationId" TEXT NOT NULL,
10 |
11 | CONSTRAINT "Registrant_pkey" PRIMARY KEY ("id")
12 | );
13 |
14 | -- CreateTable
15 | CREATE TABLE "RegistrantAttributes" (
16 | "id" UUID NOT NULL DEFAULT gen_random_uuid(),
17 | "value" TEXT NOT NULL,
18 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
19 | "updatedAt" TIMESTAMP(3) NOT NULL,
20 | "registrantId" UUID NOT NULL,
21 | "attributeId" UUID NOT NULL,
22 |
23 | CONSTRAINT "RegistrantAttributes_pkey" PRIMARY KEY ("id")
24 | );
25 |
26 | -- CreateTable
27 | CREATE TABLE "RegistrantExtras" (
28 | "id" UUID NOT NULL DEFAULT gen_random_uuid(),
29 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
30 | "updatedAt" TIMESTAMP(3) NOT NULL,
31 | "registrantId" UUID NOT NULL,
32 | "extraId" UUID NOT NULL,
33 |
34 | CONSTRAINT "RegistrantExtras_pkey" PRIMARY KEY ("id")
35 | );
36 |
37 | -- AddForeignKey
38 | ALTER TABLE "Registrant" ADD CONSTRAINT "Registrant_eventId_fkey" FOREIGN KEY ("eventId") REFERENCES "Event"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
39 |
40 | -- AddForeignKey
41 | ALTER TABLE "Registrant" ADD CONSTRAINT "Registrant_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
42 |
43 | -- AddForeignKey
44 | ALTER TABLE "RegistrantAttributes" ADD CONSTRAINT "RegistrantAttributes_registrantId_fkey" FOREIGN KEY ("registrantId") REFERENCES "Registrant"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
45 |
46 | -- AddForeignKey
47 | ALTER TABLE "RegistrantAttributes" ADD CONSTRAINT "RegistrantAttributes_attributeId_fkey" FOREIGN KEY ("attributeId") REFERENCES "Attributes"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
48 |
49 | -- AddForeignKey
50 | ALTER TABLE "RegistrantExtras" ADD CONSTRAINT "RegistrantExtras_registrantId_fkey" FOREIGN KEY ("registrantId") REFERENCES "Registrant"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
51 |
52 | -- AddForeignKey
53 | ALTER TABLE "RegistrantExtras" ADD CONSTRAINT "RegistrantExtras_extraId_fkey" FOREIGN KEY ("extraId") REFERENCES "Extras"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
54 |
--------------------------------------------------------------------------------
/packages/database/prisma/migrations/migration_lock.toml:
--------------------------------------------------------------------------------
1 | # Please do not edit this file manually
2 | # It should be added in your version-control system (i.e. Git)
3 | provider = "postgresql"
--------------------------------------------------------------------------------
/packages/eslint-config-custom/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | commonjs: true,
5 | es6: true,
6 | node: true,
7 | },
8 | parser: '@typescript-eslint/parser',
9 | settings: {
10 | react: {
11 | version: 'detect',
12 | },
13 | },
14 | plugins: ['react', '@typescript-eslint', 'prettier'],
15 | extends: ['plugin:react/recommended', 'prettier'],
16 | rules: {
17 | 'react/jsx-props-no-spreading': 'off',
18 | 'react/react-in-jsx-scope': 'off',
19 | 'import/prefer-default-export': 'off',
20 | 'prettier/prettier': ['error'],
21 | 'class-methods-use-this': 'off',
22 | 'import/no-extraneous-dependencies': 'off',
23 | 'react/require-default-props': 'off',
24 | 'comma-dangle': 'off',
25 | 'no-return-await': 'off',
26 | '@typescript-eslint/return-await': 'off',
27 | },
28 | };
29 |
--------------------------------------------------------------------------------
/packages/eslint-config-custom/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "eslint-config-custom",
3 | "main": "index.js",
4 | "license": "MIT",
5 | "dependencies": {
6 | "eslint": "^8.56.0",
7 | "eslint-config-next": "^13.5.4",
8 | "eslint-config-prettier": "^9.0.0",
9 | "eslint-config-turbo": "^1.11.2",
10 | "eslint-plugin-react": "7.33.2"
11 | },
12 | "devDependencies": {
13 | "tsconfig": "workspace:*"
14 | },
15 | "publishConfig": {
16 | "access": "public"
17 | },
18 | "scripts": {
19 | "lint": "eslint"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/packages/eslint-config-custom/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "tsconfig/tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "./build/"
5 | },
6 | "include": ["src/**/*"],
7 | "exclude": ["node_modules"]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/tsconfig/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tsconfig",
3 | "lockfileVersion": 3,
4 | "requires": true,
5 | "packages": {
6 | "": {
7 | "name": "tsconfig"
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/packages/tsconfig/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tsconfig"
3 | }
4 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - 'apps/*'
3 | - 'packages/*'
4 | - 'apps/web-admin/*'
5 |
--------------------------------------------------------------------------------
/turbo.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://turbo.build/schema.json",
3 | "globalDependencies": ["**/.env.*local"],
4 | "tasks": {
5 | "build": {
6 | "dependsOn": ["^build", "^db:generate"],
7 | "outputs": [".next/**", "!.next/cache/**"]
8 | },
9 | "clean": {},
10 | "start": {},
11 | "lint": {},
12 | "dev": {
13 | "dependsOn": ["^db:generate"],
14 | "cache": false,
15 | "persistent": true
16 | },
17 | "setup": {},
18 | "test": {
19 | "dependsOn": ["^build"],
20 | "inputs": ["src/**/*.tsx", "src/**/*.ts", "test/**/*.ts", "test/**/*.tsx"]
21 | },
22 | "db:generate": {
23 | "cache": false
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------