├── .DS_Store
├── .gitmodules
├── README.md
├── client
├── .gitignore
├── README.md
├── babel-plugin-macros.config.js
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
│ ├── assets
│ │ ├── Argentina.png
│ │ ├── Australia.png
│ │ ├── Belgium.png
│ │ ├── Brazil.png
│ │ ├── Cameroon.png
│ │ ├── Canada.png
│ │ ├── CostaRica.png
│ │ ├── Croatia.png
│ │ ├── Denmark.png
│ │ ├── Ecuador.png
│ │ ├── England.png
│ │ ├── France.png
│ │ ├── Germany.png
│ │ ├── Ghana.png
│ │ ├── Iran.png
│ │ ├── Japan.png
│ │ ├── Korea.png
│ │ ├── Mexico.png
│ │ ├── Morocco.png
│ │ ├── Netherlands.png
│ │ ├── Poland.png
│ │ ├── Portugal.png
│ │ ├── Qatar.png
│ │ ├── SaudiArabia.png
│ │ ├── Senegal.png
│ │ ├── Serbia.png
│ │ ├── Spain.png
│ │ ├── Switzerland.png
│ │ ├── Tunisia.png
│ │ ├── USA.png
│ │ ├── Uruguay.png
│ │ ├── Wales.png
│ │ ├── abdo.jpeg
│ │ ├── banner.jpg
│ │ ├── banner.webp
│ │ ├── bg-gre.jpg
│ │ ├── bg-grey.jpg
│ │ ├── calendar.svg
│ │ ├── customer-service.png
│ │ ├── customer1.jpeg
│ │ ├── customer2.jpeg
│ │ ├── emptyab.png
│ │ ├── flash.png
│ │ ├── logo.png
│ │ ├── maro.jpeg
│ │ ├── omar.jpeg
│ │ ├── refund.png
│ │ ├── ribbon.png
│ │ ├── soldout.png
│ │ ├── stadium.svg
│ │ └── tba.png
│ ├── favicon.ico
│ ├── index.html
│ ├── logo.svg
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
├── src
│ ├── App.js
│ ├── ScrollToTop.js
│ ├── components
│ │ ├── Form.js
│ │ ├── MatchLogo.js
│ │ ├── UserCard.js
│ │ ├── authButton.js
│ │ ├── ballLoading
│ │ │ ├── BallLoading.js
│ │ │ └── ball.css
│ │ ├── cards
│ │ │ ├── ProfileThreeColGrid.js
│ │ │ └── TabCardGrid.js
│ │ ├── checkout
│ │ │ ├── Card.js
│ │ │ ├── Styles.js
│ │ │ ├── cardUtils.js
│ │ │ ├── index.js
│ │ │ ├── reportWebVitals.js
│ │ │ └── setupTests.js
│ │ ├── contact
│ │ │ ├── SingleCol.js
│ │ │ └── TwoColContactUsWithIllustrationFullForm.js
│ │ ├── features
│ │ │ ├── DashedBorderSixFeatures.js
│ │ │ ├── ThreeColCenteredStatsPrimaryBackground.js
│ │ │ ├── ThreeColSimple.js
│ │ │ ├── ThreeColWithSideImage.js
│ │ │ ├── ThreeColWithSideImageWithPrimaryBackground.js
│ │ │ ├── TwoColSingleFeatureWithStats.js
│ │ │ ├── TwoColSingleFeatureWithStats2.js
│ │ │ ├── TwoColWithButton.js
│ │ │ ├── TwoColWithSteps.js
│ │ │ ├── TwoColWithTwoFeaturesAndButtons.js
│ │ │ ├── TwoColWithTwoHorizontalFeaturesAndButton.js
│ │ │ └── VerticalWithAlternateImageAndText.js
│ │ ├── footers
│ │ │ └── MiniCenteredFooter.js
│ │ ├── form.css
│ │ ├── headers
│ │ │ └── light.js
│ │ ├── hero
│ │ │ ├── TwoColumnWithVideo.js
│ │ │ └── hero.css
│ │ ├── matches.js
│ │ ├── misc
│ │ │ ├── Buttons.js
│ │ │ ├── Headings.js
│ │ │ ├── Layouts.js
│ │ │ ├── Links.js
│ │ │ └── Typography.js
│ │ ├── myTickets
│ │ │ ├── MyTickets.js
│ │ │ ├── Ticket.css
│ │ │ └── Ticket.js
│ │ ├── paymentSF
│ │ │ ├── PaymentFail.js
│ │ │ ├── PaymentSuccess.js
│ │ │ └── payment.scss
│ │ ├── pricing
│ │ │ ├── ThreePlans.js
│ │ │ ├── ThreePlansWithHalfPrimaryBackground.js
│ │ │ └── TwoPlansWithDurationSwitcher.js
│ │ ├── select.js
│ │ ├── socialMedia
│ │ │ ├── SocialMedia.jsx
│ │ │ └── socialMedia.css
│ │ └── testimonials
│ │ │ └── ThreeColumnWithProfileImage.js
│ ├── context
│ │ └── Total.js
│ ├── firebase
│ │ └── config.js
│ ├── helpers
│ │ ├── AnimationRevealPage.js
│ │ ├── useAnimatedNavToggler.js
│ │ └── useInView.js
│ ├── images
│ │ ├── arrow-left-2-icon.svg
│ │ ├── arrow-left-3-icon.svg
│ │ ├── arrow-right-2-icon.svg
│ │ ├── arrow-right-3-icon.svg
│ │ ├── arrow-right-icon.svg
│ │ ├── celebration-icon.svg
│ │ ├── checkbox-circle.svg
│ │ ├── chef-icon.svg
│ │ ├── contact-us-illustration.svg
│ │ ├── customer-support-illustration.svg
│ │ ├── customers-logo-strip.png
│ │ ├── customize-icon.svg
│ │ ├── design-illustration-2.svg
│ │ ├── design-illustration.svg
│ │ ├── dot-pattern.svg
│ │ ├── email-illustration.svg
│ │ ├── email-newsletter-icon.svg
│ │ ├── facebook-icon.svg
│ │ ├── fast-icon.svg
│ │ ├── girl-illustration.svg
│ │ ├── github-icon.svg
│ │ ├── google-icon.png
│ │ ├── google-play-icon.png
│ │ ├── handle-icon.svg
│ │ ├── handshake.jpg
│ │ ├── happy-illustration.svg
│ │ ├── hero-screenshot-1.png
│ │ ├── hero-screenshot-2.png
│ │ ├── linkedin-icon.svg
│ │ ├── loc-icon.png
│ │ ├── login-illustration.svg
│ │ ├── logo-full.png
│ │ ├── logo-light.svg
│ │ ├── logo.svg
│ │ ├── love-illustration.svg
│ │ ├── professional-illustration.svg
│ │ ├── prototype-illustration.svg
│ │ ├── quotes-l.svg
│ │ ├── quotes-r.svg
│ │ ├── reliable-icon.svg
│ │ ├── server-illustration-2.svg
│ │ ├── server-illustration.svg
│ │ ├── server-redundancy-illustration.svg
│ │ ├── server-secure-illustration.svg
│ │ ├── shield-icon.svg
│ │ ├── shop-icon.svg
│ │ ├── signup-illustration.svg
│ │ ├── simple-icon.svg
│ │ ├── stadium-empty.png
│ │ ├── stadium-left.png
│ │ ├── stadium-right.png
│ │ ├── star-icon.svg
│ │ ├── stats-illustration.svg
│ │ ├── support-icon.svg
│ │ ├── svg-decorator-blob-1.svg
│ │ ├── svg-decorator-blob-10.svg
│ │ ├── svg-decorator-blob-2.svg
│ │ ├── svg-decorator-blob-3.svg
│ │ ├── svg-decorator-blob-4.svg
│ │ ├── svg-decorator-blob-5.svg
│ │ ├── svg-decorator-blob-6.svg
│ │ ├── svg-decorator-blob-7.svg
│ │ ├── svg-decorator-blob-8.svg
│ │ ├── svg-decorator-blob-9.svg
│ │ ├── team-illustration-2.svg
│ │ ├── team-illustration-3.svg
│ │ ├── team-illustration.svg
│ │ ├── twitter-icon.png
│ │ ├── twitter-icon.svg
│ │ └── youtube-icon.svg
│ ├── index.js
│ ├── pages
│ │ ├── About.js
│ │ ├── Book.js
│ │ ├── Checkout.js
│ │ ├── Contact.js
│ │ └── Home.js
│ ├── services
│ │ ├── payment.js
│ │ ├── reservation.js
│ │ └── shop.js
│ ├── styles
│ │ └── GlobalStyles.js
│ └── tailwind.config.js
└── tailwind.config.js
├── payment
├── .DS_Store
├── .gitignore
├── app.js
├── controller
│ └── payment.js
├── package-lock.json
├── package.json
├── routes
│ └── api.route.js
└── vercel.json
├── security
├── .gitignore
├── blacklist.txt
├── index.js
├── middlewares
│ ├── IPBlocking.js
│ └── cors.js
├── package-lock.json
├── package.json
├── suspicious.log
└── vercel.json
├── seed
├── package-lock.json
├── package.json
└── seeder
│ ├── MockData
│ ├── customer.js
│ ├── data generator
│ │ ├── matches
│ │ │ ├── genMatch.js
│ │ │ ├── whichCon.js
│ │ │ └── world_cup_matches.json
│ │ └── tickets
│ │ │ ├── formate.js
│ │ │ ├── output.json
│ │ │ └── tickets.json
│ ├── fillDB.js
│ ├── matches.js
│ └── tickets.js
│ ├── migrations
│ ├── 20221203141326_init
│ │ └── migration.sql
│ ├── 20221203142455_update_match
│ │ └── migration.sql
│ ├── 20221204124438_
│ │ └── migration.sql
│ ├── 20221204132848_init
│ │ └── migration.sql
│ ├── 20221204133223_int
│ │ └── migration.sql
│ ├── 20221204133312_ll
│ │ └── migration.sql
│ ├── 20230108024909_fix
│ │ └── migration.sql
│ └── migration_lock.toml
│ └── schema.prisma
├── shop
├── .DS_Store
├── .babelrc
├── .gitignore
├── .vscode
│ └── settings.json
├── README.md
├── __tests__
│ └── shop.test.js
├── app.js
├── controller
│ └── shopController.js
├── package-lock.json
├── package.json
├── prisma.js
├── prisma
│ └── schema.prisma
├── routes
│ └── shopRouter.js
└── vercel.json
└── user
├── .gitignore
├── app.js
├── controllers
└── userController.js
├── package-lock.json
├── package.json
├── prisma
└── schema.prisma
├── routes
└── userRoutes.js
├── user.test.js
└── vercel.json
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/.DS_Store
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 |
2 | [submodule "consumer"]
3 | path = consumer
4 | url = https://github.com/AlHassanHK/consumer
5 | [submodule "producer"]
6 | path = producer
7 | url = https://github.com/AlHassanHK/producer.git
8 | [submodule "reservation"]
9 | path = reservation
10 | url = https://github.com/AlHassanHK/reservation.git
11 |
12 |
--------------------------------------------------------------------------------
/client/.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 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/client/README.md:
--------------------------------------------------------------------------------
1 | # Ticktaka-client
--------------------------------------------------------------------------------
/client/babel-plugin-macros.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | twin: {
3 | styled: "styled-components",
4 | config: "./src/tailwind.config.js",
5 | format: "auto"
6 | }
7 | };
8 |
--------------------------------------------------------------------------------
/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "world-cup",
3 | "version": "0.1.0",
4 | "dependencies": {
5 | "@algolia/autocomplete-js": "^1.7.4",
6 | "@emotion/react": "^11.10.5",
7 | "@emotion/styled": "^11.10.5",
8 | "@hcaptcha/react-hcaptcha": "^1.4.4",
9 | "@material-tailwind/react": "^1.2.4",
10 | "@mui/material": "^5.11.0",
11 | "@ramonak/react-progress-bar": "^5.0.3",
12 | "@testing-library/jest-dom": "^5.11.4",
13 | "@testing-library/react": "^11.1.0",
14 | "@testing-library/user-event": "^12.1.10",
15 | "algoliasearch": "^4.14.3",
16 | "axios": "^0.24.0",
17 | "bootstrap": "^3.4.1",
18 | "bootstrap-css": "^4.0.0-alpha.5",
19 | "feather-icons": "^4.29.0",
20 | "firebase": "^8.10.1",
21 | "framer-motion": "^7.3.2",
22 | "gsap": "^3.11.4",
23 | "instantsearch.css": "^7.4.5",
24 | "node-sass": "^8.0.0",
25 | "payment": "^2.4.6",
26 | "qs": "^6.11.0",
27 | "react": "^18.2.0",
28 | "react-anchor-link-smooth-scroll": "^1.0.12",
29 | "react-async-hook": "^4.0.0",
30 | "react-avatar": "^5.0.3",
31 | "react-bootstrap": "^2.7.0",
32 | "react-countdown": "^2.3.5",
33 | "react-countdown-circle-timer": "^3.1.0",
34 | "react-credit-cards-2": "^0.9.0",
35 | "react-dom": "^18.0.0",
36 | "react-final-form": "^6.5.9",
37 | "react-github-btn": "^1.4.0",
38 | "react-helmet": "^6.1.0",
39 | "react-icons": "^4.7.1",
40 | "react-instantsearch-dom": "^6.38.1",
41 | "react-instantsearch-hooks-web": "^6.38.1",
42 | "react-loading": "^2.0.3",
43 | "react-loading-skeleton": "^3.1.0",
44 | "react-modal": "^3.16.1",
45 | "react-responsive": "^9.0.2",
46 | "react-rnd": "^10.3.7",
47 | "react-router-dom": "^6.4.5",
48 | "react-scripts": "5.0.1",
49 | "react-slick": "^0.29.0",
50 | "react-toastify": "^9.1.1",
51 | "reactstrap": "^9.1.5",
52 | "slick-carousel": "^1.8.1",
53 | "stripe": "^11.5.0",
54 | "styled-components": "^5.3.6",
55 | "tailwindcss": "^3.0.0",
56 | "twin.macro": "^3.1.0",
57 | "web-vitals": "^2.1.4"
58 | },
59 | "scripts": {
60 | "start": "react-scripts start",
61 | "build": "react-scripts build",
62 | "eject": "react-scripts eject"
63 | },
64 | "browserslist": {
65 | "production": [
66 | ">0.2%",
67 | "not dead",
68 | "not op_mini all"
69 | ],
70 | "development": [
71 | "last 1 chrome version",
72 | "last 1 firefox version",
73 | "last 1 safari version"
74 | ]
75 | },
76 | "devDependencies": {
77 | "autoprefixer": "^10.4.13",
78 | "postcss": "^8.4.20",
79 | "tailwindcss": "^3.2.4"
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/client/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/client/public/assets/Argentina.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/Argentina.png
--------------------------------------------------------------------------------
/client/public/assets/Australia.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/Australia.png
--------------------------------------------------------------------------------
/client/public/assets/Belgium.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/Belgium.png
--------------------------------------------------------------------------------
/client/public/assets/Brazil.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/Brazil.png
--------------------------------------------------------------------------------
/client/public/assets/Cameroon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/Cameroon.png
--------------------------------------------------------------------------------
/client/public/assets/Canada.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/Canada.png
--------------------------------------------------------------------------------
/client/public/assets/CostaRica.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/CostaRica.png
--------------------------------------------------------------------------------
/client/public/assets/Croatia.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/Croatia.png
--------------------------------------------------------------------------------
/client/public/assets/Denmark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/Denmark.png
--------------------------------------------------------------------------------
/client/public/assets/Ecuador.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/Ecuador.png
--------------------------------------------------------------------------------
/client/public/assets/England.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/England.png
--------------------------------------------------------------------------------
/client/public/assets/France.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/France.png
--------------------------------------------------------------------------------
/client/public/assets/Germany.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/Germany.png
--------------------------------------------------------------------------------
/client/public/assets/Ghana.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/Ghana.png
--------------------------------------------------------------------------------
/client/public/assets/Iran.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/Iran.png
--------------------------------------------------------------------------------
/client/public/assets/Japan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/Japan.png
--------------------------------------------------------------------------------
/client/public/assets/Korea.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/Korea.png
--------------------------------------------------------------------------------
/client/public/assets/Mexico.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/Mexico.png
--------------------------------------------------------------------------------
/client/public/assets/Morocco.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/Morocco.png
--------------------------------------------------------------------------------
/client/public/assets/Netherlands.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/Netherlands.png
--------------------------------------------------------------------------------
/client/public/assets/Poland.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/Poland.png
--------------------------------------------------------------------------------
/client/public/assets/Portugal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/Portugal.png
--------------------------------------------------------------------------------
/client/public/assets/Qatar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/Qatar.png
--------------------------------------------------------------------------------
/client/public/assets/SaudiArabia.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/SaudiArabia.png
--------------------------------------------------------------------------------
/client/public/assets/Senegal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/Senegal.png
--------------------------------------------------------------------------------
/client/public/assets/Serbia.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/Serbia.png
--------------------------------------------------------------------------------
/client/public/assets/Spain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/Spain.png
--------------------------------------------------------------------------------
/client/public/assets/Switzerland.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/Switzerland.png
--------------------------------------------------------------------------------
/client/public/assets/Tunisia.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/Tunisia.png
--------------------------------------------------------------------------------
/client/public/assets/USA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/USA.png
--------------------------------------------------------------------------------
/client/public/assets/Uruguay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/Uruguay.png
--------------------------------------------------------------------------------
/client/public/assets/Wales.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/Wales.png
--------------------------------------------------------------------------------
/client/public/assets/abdo.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/abdo.jpeg
--------------------------------------------------------------------------------
/client/public/assets/banner.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/banner.jpg
--------------------------------------------------------------------------------
/client/public/assets/banner.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/banner.webp
--------------------------------------------------------------------------------
/client/public/assets/bg-gre.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/bg-gre.jpg
--------------------------------------------------------------------------------
/client/public/assets/bg-grey.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/bg-grey.jpg
--------------------------------------------------------------------------------
/client/public/assets/calendar.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
8 |
9 |
10 |
16 |
18 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/client/public/assets/customer-service.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/customer-service.png
--------------------------------------------------------------------------------
/client/public/assets/customer1.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/customer1.jpeg
--------------------------------------------------------------------------------
/client/public/assets/customer2.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/customer2.jpeg
--------------------------------------------------------------------------------
/client/public/assets/emptyab.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/emptyab.png
--------------------------------------------------------------------------------
/client/public/assets/flash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/flash.png
--------------------------------------------------------------------------------
/client/public/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/logo.png
--------------------------------------------------------------------------------
/client/public/assets/maro.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/maro.jpeg
--------------------------------------------------------------------------------
/client/public/assets/omar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/omar.jpeg
--------------------------------------------------------------------------------
/client/public/assets/refund.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/refund.png
--------------------------------------------------------------------------------
/client/public/assets/ribbon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/ribbon.png
--------------------------------------------------------------------------------
/client/public/assets/soldout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/soldout.png
--------------------------------------------------------------------------------
/client/public/assets/stadium.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/public/assets/tba.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/assets/tba.png
--------------------------------------------------------------------------------
/client/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/favicon.ico
--------------------------------------------------------------------------------
/client/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
24 | TICKETaka
25 |
26 |
27 |
28 | You need to enable JavaScript to run this app.
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/client/public/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/client/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/logo192.png
--------------------------------------------------------------------------------
/client/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/public/logo512.png
--------------------------------------------------------------------------------
/client/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/client/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/client/src/App.js:
--------------------------------------------------------------------------------
1 | import { BrowserRouter, Route, Routes } from "react-router-dom";
2 | import GlobalStyles from "./styles/GlobalStyles";
3 | import { css } from "styled-components/macro"; //eslint-disable-line
4 | import Home from "./pages/Home.js";
5 | import { Book } from "./pages/Book.js";
6 | import React from "react";
7 | import { AppContextProvider } from "./context/Total.js";
8 | import Checkout from "./pages/Checkout";
9 | import ScrollToTop from "./ScrollToTop";
10 | import BallLoading from "./components/ballLoading/BallLoading";
11 | import PaymentSuccess from "./components/paymentSF/PaymentSuccess";
12 | import PaymentFail from "./components/paymentSF/PaymentFail";
13 | import MyTickets from "./components/myTickets/MyTickets";
14 | import About from "./pages/About";
15 | import Contact from "./pages/Contact";
16 |
17 | function App() {
18 | return (
19 |
20 |
21 |
22 |
23 |
24 | } />
25 |
26 |
30 |
31 |
32 | }
33 | />
34 |
35 | } />
36 |
37 | } />
38 | } />
39 | } />
40 | } />
41 |
42 | } />
43 |
44 |
45 |
46 |
47 | );
48 | }
49 |
50 | export default App;
51 |
--------------------------------------------------------------------------------
/client/src/ScrollToTop.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useEffect } from "react";
3 | import { useLocation } from "react-router";
4 |
5 | const ScrollToTop = (props) => {
6 | const location = useLocation();
7 | useEffect(() => {
8 | window.scrollTo(0, 0);
9 | }, [location]);
10 |
11 | return <>{props.children}>;
12 | };
13 |
14 | export default ScrollToTop;
15 |
--------------------------------------------------------------------------------
/client/src/components/MatchLogo.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import left from "../images/stadium-left.png";
3 | import right from "../images/stadium-right.png";
4 | import empty from "../images/stadium-empty.png";
5 |
6 | const pos = { left, right, empty };
7 | const MatchLogo = ({
8 | myRef,
9 | user,
10 | setProgress,
11 | progress,
12 | choices,
13 | setChoices,
14 | }) => {
15 | const [selected, setSelected] = useState(pos.empty);
16 |
17 | const handleClick = (e) => {
18 | document.getElementsByClassName("type1")[0].style.display = "block";
19 | myRef.current.scrollIntoView({ behavior: "smooth" });
20 | const img1 = document.getElementsByClassName("pic1");
21 | const img2 = document.getElementsByClassName("pic2");
22 | if (progress < 25) {
23 | setProgress((prev) => prev + 25);
24 | }
25 |
26 | if (e.target.src.includes(user.homeTeam)) {
27 | setChoices({ ...choices, team: user.homeTeam, side: "home" });
28 | setSelected(pos.left);
29 | img2[0].classList.remove("my-border");
30 | e.target.classList.add("my-border");
31 | } else {
32 | setChoices({ ...choices, team: user.awayTeam, side: "away" });
33 | setSelected(pos.right);
34 | img1[0].classList.remove("my-border");
35 | e.target.classList.add("my-border");
36 | }
37 | };
38 |
39 | return (
40 |
48 |
56 |
64 |
{user.homeTeam}
65 |
66 |
67 |
77 |
78 |
79 |
87 |
95 |
{user.awayTeam}
96 |
97 |
98 | );
99 | };
100 |
101 | export default MatchLogo;
102 |
--------------------------------------------------------------------------------
/client/src/components/UserCard.js:
--------------------------------------------------------------------------------
1 | import { useNavigate } from "react-router-dom";
2 | import React from "react";
3 |
4 | const UserCard = ({ user }) => {
5 | const navigate = useNavigate();
6 | return (
7 |
8 |
9 |
14 |
15 |
20 |
21 |
22 |
23 |
24 | {user.homeTeam} vs {user.awayTeam}
25 |
26 |
27 |
28 |
29 |
34 |
35 |
36 |
37 |
{user.Date}
38 |
39 |
40 |
41 |
46 |
47 |
48 |
49 |
{user.location}
50 |
51 |
52 | {/*
53 | Score: {user.homeTeamScore} - {user.awayTeamScore}
54 |
*/}
55 |
56 | navigate(`/book/${user.id}`, { state: { ...user } })}
59 | >
60 | Book Ticket
61 |
62 |
63 |
64 | );
65 | };
66 | export default UserCard;
67 |
--------------------------------------------------------------------------------
/client/src/components/authButton.js:
--------------------------------------------------------------------------------
1 | import SocialMedia from './socialMedia/SocialMedia';
2 | import {
3 | Menu,
4 | MenuHandler,
5 | MenuList,
6 | MenuItem,
7 | Button,
8 | } from "@material-tailwind/react";
9 |
10 |
11 | export default function AuthButton() {
12 | return (
13 |
19 |
20 | LogIn
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | );
29 | }
--------------------------------------------------------------------------------
/client/src/components/ballLoading/BallLoading.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "./ball.css";
3 | import tw from "twin.macro";
4 | import { SectionHeading } from "../misc/Headings.js";
5 | const Heading = tw(
6 | SectionHeading
7 | )`text-left text-3xl sm:text-4xl lg:text-5xl text-center md:text-left leading-tight`;
8 | const BallLoading = () => {
9 | return (
10 |
18 | );
19 | };
20 |
21 | export default BallLoading;
22 |
--------------------------------------------------------------------------------
/client/src/components/ballLoading/ball.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | height: auto;
3 | }
4 | .box {
5 | margin: 0 auto;
6 | width: 40px;
7 | height: 140px;
8 | margin-top: 10em;
9 | text-align: "center";
10 | position: relative;
11 | }
12 | .shadow {
13 | position: absolute;
14 | width: 100%;
15 | height: 10px;
16 | background-color: grey;
17 | bottom: 0;
18 | border-radius: 100%;
19 | transform: scaleX(.8);
20 | opacity: .6;
21 | animation: shadowScale 1s linear infinite;
22 | }
23 |
24 | .gravity {
25 | width: 40px;
26 | height: 40px;
27 | animation: bounce 1s cubic-bezier(0.68, 0.35, 0.29, 0.54) infinite;
28 | }
29 | .ball {
30 | width: 40px;
31 | height: 40px;
32 | background-image: url('https://cdn2.iconfinder.com/data/icons/activity-5/50/26BD-soccer-ball-128.png');
33 | background-size: cover;
34 | animation: roll .7s linear infinite;
35 | }
36 |
37 | @keyframes roll {
38 | 0% {}
39 | 100% { transform: rotate(360deg) }
40 | }
41 | @keyframes bounce {
42 | 0% {}
43 | 50% { transform: translateY(100px) }
44 | 100% {}
45 | }
46 | @keyframes shadowScale {
47 | 0% {}
48 | 50% { transform: scaleX(1); opacity: .8;}
49 | 100% {}
50 | }
--------------------------------------------------------------------------------
/client/src/components/checkout/cardUtils.js:
--------------------------------------------------------------------------------
1 | import Payment from "payment";
2 |
3 | function clearNumber(value = "") {
4 | return value.replace(/\D+/g, "");
5 | }
6 |
7 | export function formatCreditCardNumber(value) {
8 | if (!value) {
9 | return value;
10 | }
11 |
12 | const issuer = Payment.fns.cardType(value);
13 | const clearValue = clearNumber(value);
14 | let nextValue;
15 |
16 | switch (issuer) {
17 | case "amex":
18 | nextValue = `${clearValue.slice(0, 4)} ${clearValue.slice(
19 | 4,
20 | 10
21 | )} ${clearValue.slice(10, 15)}`;
22 | break;
23 | case "dinersclub":
24 | nextValue = `${clearValue.slice(0, 4)} ${clearValue.slice(
25 | 4,
26 | 10
27 | )} ${clearValue.slice(10, 14)}`;
28 | break;
29 | default:
30 | nextValue = `${clearValue.slice(0, 4)} ${clearValue.slice(
31 | 4,
32 | 8
33 | )} ${clearValue.slice(8, 12)} ${clearValue.slice(12, 19)}`;
34 | break;
35 | }
36 |
37 | return nextValue.trim();
38 | }
39 |
40 | export function formatCVC(value, prevValue, allValues = {}) {
41 | const clearValue = clearNumber(value);
42 | let maxLength = 4;
43 |
44 | if (allValues.number) {
45 | const issuer = Payment.fns.cardType(allValues.number);
46 | maxLength = issuer === "amex" ? 4 : 3;
47 | }
48 |
49 | return clearValue.slice(0, maxLength);
50 | }
51 |
52 | export function formatExpirationDate(value) {
53 | const clearValue = clearNumber(value);
54 |
55 | if (clearValue.length >= 3) {
56 | return `${clearValue.slice(0, 2)}/${clearValue.slice(2, 4)}`;
57 | }
58 |
59 | return clearValue;
60 | }
61 |
--------------------------------------------------------------------------------
/client/src/components/checkout/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/client/src/components/checkout/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/client/src/components/contact/TwoColContactUsWithIllustrationFullForm.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import tw from "twin.macro";
3 | import styled from "styled-components";
4 | import { css } from "styled-components/macro"; //eslint-disable-line
5 | import {
6 | SectionHeading,
7 | Subheading as SubheadingBase,
8 | } from "../misc/Headings.js";
9 | import { PrimaryButton as PrimaryButtonBase } from "../misc/Buttons.js";
10 | import EmailIllustrationSrc from "../../images/email-illustration.svg";
11 |
12 | const Container = tw.div`relative`;
13 | const TwoColumn = tw.div`flex flex-col md:flex-row justify-between max-w-screen-xl mx-auto py-20 md:py-24`;
14 | const Column = tw.div`w-full max-w-md mx-auto md:max-w-none md:mx-0`;
15 | const ImageColumn = tw(Column)`md:w-5/12 flex-shrink-0 h-80 md:h-auto`;
16 | const TextColumn = styled(Column)((props) => [
17 | tw`md:w-7/12 mt-16 md:mt-0`,
18 | props.textOnLeft
19 | ? tw`md:mr-12 lg:mr-16 md:order-first`
20 | : tw`md:ml-12 lg:ml-16 md:order-last`,
21 | ]);
22 |
23 | const Image = styled.div((props) => [
24 | `background-image: url("${props.imageSrc}");`,
25 | tw`rounded bg-contain bg-no-repeat bg-center h-full`,
26 | ]);
27 | const TextContent = tw.div`lg:py-8 text-center md:text-left`;
28 |
29 | const Subheading = tw(SubheadingBase)`text-center md:text-left`;
30 | const Heading = tw(
31 | SectionHeading
32 | )`mt-4 font-black text-left text-3xl sm:text-4xl lg:text-5xl text-center md:text-left leading-tight`;
33 | const Description = tw.p`mt-4 text-center md:text-left text-sm md:text-base lg:text-lg font-medium leading-relaxed text-secondary-100`;
34 |
35 | const Form = tw.form`mt-8 md:mt-10 text-sm flex flex-col max-w-sm mx-auto md:mx-0`;
36 | const Input = tw.input`mt-6 first:mt-0 border-b-2 py-3 focus:outline-none font-medium transition duration-300 hocus:border-primary-500`;
37 | const Textarea = styled(Input).attrs({ as: "textarea" })`
38 | ${tw`h-24`}
39 | `;
40 |
41 | const SubmitButton = tw(PrimaryButtonBase)`inline-block mt-8`;
42 |
43 | export default ({
44 | subheading = "Contact Us",
45 | heading = (
46 | <>
47 | Feel free to get in touch
48 | with us.
49 | >
50 | ),
51 | description = "We will respond to any questions you have in just a few hours.",
52 | submitButtonText = "Send",
53 | formAction = "#",
54 | formMethod = "get",
55 | textOnLeft = true,
56 | }) => {
57 | // The textOnLeft boolean prop can be used to display either the text on left or right side of the image.
58 |
59 | return (
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | {subheading && {subheading} }
68 | {heading}
69 | {description && {description} }
70 |
81 |
82 |
83 |
84 |
85 | );
86 | };
87 |
--------------------------------------------------------------------------------
/client/src/components/features/DashedBorderSixFeatures.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "styled-components";
3 | import tw from "twin.macro";
4 | //eslint-disable-next-line
5 | import { css } from "styled-components/macro";
6 | import { SectionHeading } from "../misc/Headings.js";
7 |
8 | import defaultCardImage from "../../images/shield-icon.svg";
9 |
10 | import { ReactComponent as SvgDecoratorBlob3 } from "../../images/svg-decorator-blob-3.svg";
11 |
12 | import SupportIconImage from "../../images/support-icon.svg";
13 | import ShieldIconImage from "../../images/shield-icon.svg";
14 | import CustomizeIconImage from "../../images/customize-icon.svg";
15 | import FastIconImage from "../../images/fast-icon.svg";
16 | import ReliableIconImage from "../../images/reliable-icon.svg";
17 | import SimpleIconImage from "../../images/simple-icon.svg";
18 |
19 | const Container = tw.div`relative`;
20 |
21 | const ThreeColumnContainer = styled.div`
22 | ${tw`flex flex-col items-center md:items-stretch md:flex-row flex-wrap md:justify-center max-w-screen-xl mx-auto py-20 md:py-24`}
23 | `;
24 | const Heading = tw(SectionHeading)`w-full`;
25 |
26 | const Column = styled.div`
27 | ${tw`md:w-1/2 lg:w-1/3 px-6 flex`}
28 | `;
29 |
30 | const Card = styled.div`
31 | ${tw`flex flex-col mx-auto max-w-xs items-center px-6 py-10 border-2 border-dashed border-primary-500 rounded-lg mt-12`}
32 | .imageContainer {
33 | ${tw`border-2 border-primary-500 text-center rounded-full p-6 flex-shrink-0 relative`}
34 | img {
35 | ${tw`w-8 h-8`}
36 | }
37 | }
38 |
39 | .textContainer {
40 | ${tw`mt-6 text-center`}
41 | }
42 |
43 | .title {
44 | ${tw`mt-2 font-bold text-xl leading-none text-primary-500`}
45 | }
46 |
47 | .description {
48 | ${tw`mt-3 font-semibold text-secondary-100 text-sm leading-loose`}
49 | }
50 | `;
51 |
52 | const DecoratorBlob = styled(SvgDecoratorBlob3)`
53 | ${tw`pointer-events-none absolute right-0 bottom-0 w-64 opacity-25 transform translate-x-32 translate-y-48 `}
54 | `;
55 |
56 | export default () => {
57 | /*
58 | * This componets has an array of object denoting the cards defined below. Each object in the cards array can have the key (Change it according to your need, you can also add more objects to have more cards in this feature component):
59 | * 1) imageSrc - the image shown at the top of the card
60 | * 2) title - the title of the card
61 | * 3) description - the description of the card
62 | * If a key for a particular card is not provided, a default value is used
63 | */
64 |
65 | const cards = [
66 | {
67 | imageSrc: ShieldIconImage,
68 | title: "Ads Management",
69 | description:
70 | "We create and manage ads that you need, from creation to deployment. Lorem ipsum donor sit amet consicou.",
71 | },
72 | { imageSrc: SupportIconImage, title: "Video Marketing" },
73 | { imageSrc: CustomizeIconImage, title: "Customer Relation" },
74 | { imageSrc: ReliableIconImage, title: "Product Outreach" },
75 | { imageSrc: FastIconImage, title: "PR Campaign" },
76 | { imageSrc: SimpleIconImage, title: "Product Expansion" },
77 | ];
78 |
79 | return (
80 |
81 |
82 |
83 | Our Professional Services
84 |
85 | {cards.map((card, i) => (
86 |
87 |
88 |
89 |
90 |
91 |
92 | {card.title || "Fully Secure"}
93 |
94 | {card.description ||
95 | "Lorem ipsum donor amet siti ceali ut enim ad minim veniam, quis nostrud. Sic Semper Tyrannis. Neoas Calie artel."}
96 |
97 |
98 |
99 |
100 | ))}
101 |
102 |
103 |
104 | );
105 | };
106 |
--------------------------------------------------------------------------------
/client/src/components/features/ThreeColCenteredStatsPrimaryBackground.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import tw from "twin.macro";
3 | import { css } from "styled-components/macro"; //eslint-disable-line
4 | import {
5 | SectionHeading,
6 | Subheading as SubheadingBase,
7 | } from "../misc/Headings.js";
8 | import {
9 | Container as ContainerBase,
10 | ContentWithPaddingXl,
11 | } from "../misc/Layouts";
12 | import { SectionDescription } from "../misc/Typography";
13 |
14 | const Container = tw(
15 | ContainerBase
16 | )`my-8 lg:my-10 bg-primary-900 text-gray-100 -mx-8 px-8`;
17 | const HeadingContainer = tw.div``;
18 | const Heading = tw(SectionHeading)`sm:text-3xl md:text-4xl lg:text-5xl`;
19 | const Subheading = tw(SubheadingBase)`text-gray-100 text-center`;
20 | const Description = tw(
21 | SectionDescription
22 | )`text-gray-400 text-center mx-auto max-w-screen-md`;
23 |
24 | const StatsContainer = tw.div`mt-8 flex flex-col sm:flex-row items-center justify-center flex-wrap max-w-screen-md justify-between mx-auto`;
25 | const Stat = tw.div`flex flex-col text-center p-4 tracking-wide`;
26 | const StatKey = tw.div`text-xl font-medium`;
27 | const StatValue = tw.div`text-4xl sm:text-3xl md:text-4xl lg:text-5xl font-black`;
28 |
29 | export default ({
30 | subheading = "",
31 | heading = "Over 9000 Projects Completed",
32 | description = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
33 | stats = [
34 | {
35 | key: "Clients",
36 | value: "2500+",
37 | },
38 | {
39 | key: "Revenue",
40 | value: "$100M+",
41 | },
42 | {
43 | key: "Employees",
44 | value: "150+",
45 | },
46 | ],
47 | }) => {
48 | return (
49 |
50 |
51 |
52 | {subheading && {subheading} }
53 | {heading}
54 | {description && {description} }
55 |
56 |
57 | {stats.map((stat, index) => (
58 |
59 | {stat.value}
60 | {stat.key}
61 |
62 | ))}
63 |
64 |
65 |
66 | );
67 | };
68 |
--------------------------------------------------------------------------------
/client/src/components/features/ThreeColSimple.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "styled-components";
3 | import tw from "twin.macro";
4 | import { css } from "styled-components/macro"; //eslint-disable-line
5 | import {
6 | SectionHeading,
7 | Subheading as SubheadingBase,
8 | } from "../misc/Headings.js";
9 | import { SectionDescription } from "../misc/Typography.js";
10 | import { Container, ContentWithPaddingXl } from "../misc/Layouts.js";
11 | import { ReactComponent as ArrowRightIcon } from "../../images/arrow-right-icon.svg";
12 | import SupportIconImage from "../../images/support-icon.svg";
13 | import ShieldIconImage from "../../images/shield-icon.svg";
14 | import CustomizeIconImage from "../../images/customize-icon.svg";
15 | import { ReactComponent as SvgDecoratorBlob3 } from "../../images/svg-decorator-blob-3.svg";
16 |
17 | const Heading = tw(SectionHeading)``;
18 | const Subheading = tw(SubheadingBase)`text-center mb-3`;
19 | const Description = tw(SectionDescription)`text-center mx-auto`;
20 | const ThreeColumnContainer = styled.div`
21 | ${tw`mt-10 flex flex-col items-center lg:items-stretch lg:flex-row flex-wrap lg:justify-center max-w-screen-lg mx-auto`}
22 | `;
23 | const Column = styled.div`
24 | ${tw`lg:w-1/3 max-w-xs`}
25 | `;
26 |
27 | const Card = styled.a`
28 | ${tw`flex flex-col items-center text-center h-full mx-4 px-4 py-8 rounded transition-transform duration-300 hover:cursor-pointer transform hover:scale-105 `}
29 | .imageContainer {
30 | ${tw`text-center rounded-full p-4 bg-gray-100`}
31 | img {
32 | ${tw`w-8 h-8`}
33 | }
34 | }
35 |
36 | .title {
37 | ${tw`mt-4 font-bold text-xl leading-none`}
38 | }
39 |
40 | .description {
41 | ${tw`mt-4 text-sm font-medium text-secondary-300`}
42 | }
43 |
44 | .link {
45 | ${tw`mt-auto inline-flex items-center pt-5 text-sm font-bold text-primary-300 leading-none hocus:text-primary-900 transition duration-300`}
46 | .icon {
47 | ${tw`ml-2 w-4`}
48 | }
49 | }
50 | `;
51 |
52 | const DecoratorBlob = styled(SvgDecoratorBlob3)`
53 | ${tw`pointer-events-none absolute right-0 bottom-0 w-64 opacity-25 transform translate-x-32 translate-y-40`}
54 | `;
55 |
56 | export default ({
57 | cards = [
58 | {
59 | imageSrc: ShieldIconImage,
60 | title: "Secure",
61 | description:
62 | "We strictly only deal with vendors that provide top notch security.",
63 | url: "https://timerse.com",
64 | },
65 | {
66 | imageSrc: SupportIconImage,
67 | title: "24/7 Support",
68 | description: "Lorem ipsum donor amet siti ceali placeholder text",
69 | url: "https://google.com",
70 | },
71 | {
72 | imageSrc: CustomizeIconImage,
73 | title: "Customizable",
74 | description: "Lorem ipsum donor amet siti ceali placeholder text",
75 | url: "https://reddit.com",
76 | },
77 | ],
78 | linkText = "Learn More",
79 | heading = "",
80 | subheading = "",
81 | description = "",
82 | imageContainerCss = null,
83 | imageCss = null,
84 | }) => {
85 | /*
86 | * This componets accepts a prop - `cards` which is an array of object denoting the cards. Each object in the cards array can have the following keys (Change it according to your need, you can also add more objects to have more cards in this feature component):
87 | * 1) imageSrc - the image shown at the top of the card
88 | * 2) title - the title of the card
89 | * 3) description - the description of the card
90 | * 4) url - the url that the card should goto on click
91 | */
92 | return (
93 |
94 |
95 | {subheading && {subheading} }
96 | {heading && {heading} }
97 | {description && {description} }
98 |
99 | {cards.map((card, i) => (
100 |
101 |
102 |
103 |
104 |
105 | {card.title}
106 | {card.description}
107 | {linkText && (
108 |
109 | {linkText}
110 |
111 |
112 | )}
113 |
114 |
115 | ))}
116 |
117 |
118 |
119 |
120 | );
121 | };
122 |
--------------------------------------------------------------------------------
/client/src/components/features/ThreeColWithSideImage.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "styled-components";
3 | import tw from "twin.macro";
4 | //eslint-disable-next-line
5 | import { css } from "styled-components/macro";
6 | import { SectionHeading, Subheading as SubheadingBase } from "components/misc/Headings.js";
7 | import { SectionDescription } from "components/misc/Typography.js";
8 |
9 | import defaultCardImage from "images/shield-icon.svg";
10 |
11 | import { ReactComponent as SvgDecoratorBlob3 } from "images/svg-decorator-blob-3.svg";
12 |
13 | import SupportIconImage from "images/support-icon.svg";
14 | import ShieldIconImage from "images/shield-icon.svg";
15 | import CustomizeIconImage from "images/customize-icon.svg";
16 | import FastIconImage from "images/fast-icon.svg";
17 | import ReliableIconImage from "images/reliable-icon.svg";
18 | import SimpleIconImage from "images/simple-icon.svg";
19 |
20 | const Container = tw.div`relative`;
21 |
22 | const ThreeColumnContainer = styled.div`
23 | ${tw`flex flex-col items-center md:items-stretch md:flex-row flex-wrap md:justify-center max-w-screen-lg mx-auto py-20 md:py-24`}
24 | `;
25 | const Subheading = tw(SubheadingBase)`mb-4`;
26 | const Heading = tw(SectionHeading)`w-full`;
27 | const Description = tw(SectionDescription)`w-full text-center`;
28 |
29 | const VerticalSpacer = tw.div`mt-10 w-full`
30 |
31 | const Column = styled.div`
32 | ${tw`md:w-1/2 lg:w-1/3 max-w-sm`}
33 | `;
34 |
35 | const Card = styled.div`
36 | ${tw`flex flex-col sm:flex-row items-center sm:items-start text-center sm:text-left h-full mx-4 px-2 py-8`}
37 | .imageContainer {
38 | ${tw`border text-center rounded-full p-5 flex-shrink-0`}
39 | img {
40 | ${tw`w-6 h-6`}
41 | }
42 | }
43 |
44 | .textContainer {
45 | ${tw`sm:ml-4 mt-4 sm:mt-2`}
46 | }
47 |
48 | .title {
49 | ${tw`mt-4 tracking-wide font-bold text-2xl leading-none`}
50 | }
51 |
52 | .description {
53 | ${tw`mt-1 sm:mt-4 font-medium text-secondary-100 leading-loose`}
54 | }
55 | `;
56 |
57 | const DecoratorBlob = styled(SvgDecoratorBlob3)`
58 | ${tw`pointer-events-none absolute right-0 bottom-0 w-64 opacity-25 transform translate-x-32 translate-y-48 `}
59 | `;
60 |
61 | export default ({ cards = null, heading = "Amazing Features", subheading = "Features", description = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." }) => {
62 | /*
63 | * This componets has an array of object denoting the cards defined below. Each object in the cards array can have the key (Change it according to your need, you can also add more objects to have more cards in this feature component) or you can directly pass this using the cards prop:
64 | * 1) imageSrc - the image shown at the top of the card
65 | * 2) title - the title of the card
66 | * 3) description - the description of the card
67 | * If a key for a particular card is not provided, a default value is used
68 | */
69 |
70 | const defaultCards = [
71 | {
72 | imageSrc: ShieldIconImage,
73 | title: "Secure",
74 | description: "We strictly only deal with vendors that provide top notch security."
75 | },
76 | { imageSrc: SupportIconImage, title: "24/7 Support" },
77 | { imageSrc: CustomizeIconImage, title: "Customizable" },
78 | { imageSrc: ReliableIconImage, title: "Reliable" },
79 | { imageSrc: FastIconImage, title: "Fast" },
80 | { imageSrc: SimpleIconImage, title: "Easy" }
81 | ];
82 |
83 | if (!cards) cards = defaultCards;
84 |
85 | return (
86 |
87 |
88 | {subheading && {subheading} }
89 | {heading}
90 | {description && {description} }
91 |
92 | {cards.map((card, i) => (
93 |
94 |
95 |
96 |
97 |
98 |
99 | {card.title || "Fully Secure"}
100 |
101 | {card.description || "Lorem ipsum donor amet siti ceali ut enim ad minim veniam, quis nostrud."}
102 |
103 |
104 |
105 |
106 | ))}
107 |
108 |
109 |
110 | );
111 | };
112 |
--------------------------------------------------------------------------------
/client/src/components/features/ThreeColWithSideImageWithPrimaryBackground.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "styled-components";
3 | import tw from "twin.macro";
4 | //eslint-disable-next-line
5 | import { css } from "styled-components/macro";
6 | import { SectionHeading, Subheading as SubheadingBase } from "components/misc/Headings.js";
7 | import { SectionDescription } from "components/misc/Typography.js";
8 |
9 | import defaultCardImage from "images/shield-icon.svg";
10 |
11 | import SupportIconImage from "images/support-icon.svg";
12 | import ShieldIconImage from "images/shield-icon.svg";
13 | import CustomizeIconImage from "images/customize-icon.svg";
14 | import FastIconImage from "images/fast-icon.svg";
15 | import ReliableIconImage from "images/reliable-icon.svg";
16 | import SimpleIconImage from "images/simple-icon.svg";
17 |
18 | const Container = tw.div`relative bg-primary-900 -mx-8 px-8 text-gray-100`;
19 |
20 | const ThreeColumnContainer = styled.div`
21 | ${tw`flex flex-col items-center md:items-stretch md:flex-row flex-wrap md:justify-center max-w-screen-lg mx-auto py-20 md:py-24`}
22 | `;
23 | const Subheading = tw(SubheadingBase)`mb-4 text-gray-100`;
24 | const Heading = tw(SectionHeading)`w-full`;
25 | const Description = tw(SectionDescription)`w-full text-center text-gray-300`;
26 |
27 | const VerticalSpacer = tw.div`mt-10 w-full`;
28 |
29 | const Column = styled.div`
30 | ${tw`md:w-1/2 lg:w-1/3 max-w-xs`}
31 | `;
32 |
33 | const Card = styled.div`
34 | ${tw`flex flex-col items-center sm:items-start text-center sm:text-left h-full mx-4 px-2 py-8`}
35 | .imageContainer {
36 | ${tw`bg-gray-100 text-center rounded-full p-5 flex-shrink-0`}
37 | img {
38 | ${tw`w-6 h-6`}
39 | }
40 | }
41 |
42 | .textContainer {
43 | ${tw`mt-6`}
44 | }
45 |
46 | .title {
47 | ${tw`tracking-wider font-bold text-xl leading-none`}
48 | }
49 |
50 | .description {
51 | ${tw`mt-2 font-normal text-gray-400 leading-snug`}
52 | }
53 | `;
54 |
55 | export default ({
56 | cards = null,
57 | heading = "Amazing Features",
58 | subheading = "",
59 | description = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
60 | }) => {
61 | /*
62 | * This componets has an array of object denoting the cards defined below. Each object in the cards array can have the key (Change it according to your need, you can also add more objects to have more cards in this feature component) or you can directly pass this using the cards prop:
63 | * 1) imageSrc - the image shown at the top of the card
64 | * 2) title - the title of the card
65 | * 3) description - the description of the card
66 | * If a key for a particular card is not provided, a default value is used
67 | */
68 |
69 | const defaultCards = [
70 | {
71 | imageSrc: ShieldIconImage,
72 | title: "Secure",
73 | description: "We strictly only deal with vendors that provide top notch security infrastructure."
74 | },
75 | { imageSrc: SupportIconImage, title: "24/7 Support" },
76 | { imageSrc: CustomizeIconImage, title: "Customizable" },
77 | { imageSrc: ReliableIconImage, title: "Reliable" },
78 | { imageSrc: FastIconImage, title: "Fast" },
79 | { imageSrc: SimpleIconImage, title: "Easy" }
80 | ];
81 |
82 | if (!cards) cards = defaultCards;
83 |
84 | return (
85 |
86 |
87 | {subheading && {subheading} }
88 | {heading}
89 | {description && {description} }
90 |
91 | {cards.map((card, i) => (
92 |
93 |
94 |
95 |
96 |
97 |
98 | {card.title || "Fully Secure"}
99 |
100 | {card.description || "Lorem ipsum donor amet siti ceali ut enim ad minim veniam, quis nostrud."}
101 |
102 |
103 |
104 |
105 | ))}
106 |
107 |
108 | );
109 | };
110 |
--------------------------------------------------------------------------------
/client/src/components/features/TwoColSingleFeatureWithStats2.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import tw from "twin.macro";
3 | import styled from "styled-components";
4 | import { css } from "styled-components/macro"; //eslint-disable-line
5 | import {
6 | SectionHeading,
7 | Subheading as SubheadingBase,
8 | } from "../misc/Headings.js";
9 | import { PrimaryButton as PrimaryButtonBase } from "../misc/Buttons.js";
10 | import StatsIllustrationSrc from "../../images/stats-illustration.svg";
11 | import { ReactComponent as SvgDotPattern } from "../../images/dot-pattern.svg";
12 | import AnchorLink from "react-anchor-link-smooth-scroll";
13 |
14 | const Container = tw.div`relative`;
15 | const TwoColumn = tw.div`flex flex-col md:flex-row justify-between max-w-screen-xl mx-auto py-20 md:py-24`;
16 | const Column = tw.div`w-full max-w-md mx-auto md:max-w-none md:mx-0`;
17 | const ImageColumn = tw(Column)`md:w-5/12 flex-shrink-0 h-80 md:h-auto relative`;
18 | const TextColumn = styled(Column)((props) => [
19 | tw`md:w-7/12 mt-16 md:mt-0`,
20 | props.textOnLeft
21 | ? tw`md:mr-12 lg:mr-16 md:order-first`
22 | : tw`md:ml-12 lg:ml-16 md:order-last`,
23 | ]);
24 |
25 | const Image = styled.div((props) => [
26 | `background-image: url("${props.imageSrc}");`,
27 | tw`rounded bg-contain bg-no-repeat bg-center h-full`,
28 | ]);
29 | const TextContent = tw.div`lg:py-8 text-center md:text-left`;
30 |
31 | const Subheading = tw(SubheadingBase)`text-center md:text-left`;
32 | const Heading = tw(
33 | SectionHeading
34 | )`mt-4 font-black text-left text-3xl sm:text-4xl lg:text-5xl text-center md:text-left leading-tight`;
35 | const Description = tw.p`mt-4 text-center md:text-left text-sm md:text-base lg:text-lg font-medium leading-relaxed text-secondary-100`;
36 |
37 | const Statistics = tw.div`flex flex-col items-center sm:block text-center md:text-left mt-4`;
38 | const Statistic = tw.div`text-left sm:inline-block sm:mr-12 last:mr-0 mt-4`;
39 | const Value = tw.div`font-bold text-lg sm:text-xl lg:text-2xl text-secondary-500 tracking-wide`;
40 | const Key = tw.div`font-medium text-primary-700`;
41 |
42 | const PrimaryButton = tw(
43 | PrimaryButtonBase
44 | )`mt-8 md:mt-10 text-sm inline-block mx-auto md:mx-0`;
45 |
46 | const DecoratorBlob = styled(SvgDotPattern)((props) => [
47 | tw`w-20 h-20 absolute right-0 bottom-0 transform translate-x-1/2 translate-y-1/2 fill-current text-primary-500 -z-10`,
48 | ]);
49 |
50 | export default ({
51 | subheading = "Our Track Record",
52 | heading = (
53 | <>
54 | We have been doing this since{" "}
55 | 1999.
56 | >
57 | ),
58 | description = "We are one of the top ticket sellers in the world. Our company is the #1 ticket provider for the FIFA World Cup 2022. It is guaranteed that you will get your ticket, or get your money back in case something goes wrong.",
59 | primaryButtonText = "Learn More",
60 | primaryButtonUrl = "https://timerse.com",
61 | imageSrc = StatsIllustrationSrc,
62 | imageCss = null,
63 | imageContainerCss = null,
64 | imageDecoratorBlob = false,
65 | imageDecoratorBlobCss = null,
66 | imageInsideDiv = true,
67 | statistics = null,
68 | textOnLeft = false,
69 | }) => {
70 | // The textOnLeft boolean prop can be used to display either the text on left or right side of the image.
71 | //Change the statistics variable as you like, add or delete objects
72 | const defaultStatistics = [
73 | {
74 | key: "Clients",
75 | value: "2282+",
76 | },
77 | {
78 | key: "Projects",
79 | value: "3891+",
80 | },
81 | {
82 | key: "Awards",
83 | value: "1000+",
84 | },
85 | ];
86 |
87 | if (!statistics) statistics = defaultStatistics;
88 |
89 | return (
90 |
91 |
92 |
93 | {imageInsideDiv ? (
94 |
95 | ) : (
96 |
97 | )}
98 | {imageDecoratorBlob && }
99 |
100 |
101 |
102 | {subheading && {subheading} }
103 | {heading}
104 | {description}
105 |
106 | {statistics.map((statistic, index) => (
107 |
108 | {statistic.value}
109 | {statistic.key}
110 |
111 | ))}
112 |
113 |
114 | Order Now
115 |
116 |
117 |
118 |
119 |
120 | );
121 | };
122 |
--------------------------------------------------------------------------------
/client/src/components/features/TwoColWithButton.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import tw from "twin.macro";
3 | import styled from "styled-components";
4 | import { css } from "styled-components/macro"; //eslint-disable-line
5 | import {
6 | SectionHeading,
7 | Subheading as SubheadingBase,
8 | } from "../misc/Headings.js";
9 | import { PrimaryButton as PrimaryButtonBase } from "../misc/Buttons.js";
10 | import TeamIllustrationSrc from "../../images/team-illustration-2.svg";
11 | import { ReactComponent as SvgDotPattern } from "../../images/dot-pattern.svg";
12 |
13 | const Container = tw.div`relative`;
14 | const TwoColumn = tw.div`flex flex-col md:flex-row justify-between max-w-screen-xl mx-auto py-20 md:py-24 items-center`;
15 | const Column = tw.div`w-full max-w-md mx-auto md:max-w-none md:mx-0`;
16 | const ImageColumn = tw(Column)`md:w-6/12 flex-shrink-0 relative`;
17 | const TextColumn = styled(Column)((props) => [
18 | tw`md:w-6/12 mt-16 md:mt-0`,
19 | props.textOnLeft
20 | ? tw`md:mr-12 lg:mr-16 md:order-first`
21 | : tw`md:ml-12 lg:ml-16 md:order-last`,
22 | ]);
23 |
24 | const Image = styled.img((props) => [
25 | props.imageRounded && tw`rounded`,
26 | props.imageBorder && tw`border`,
27 | props.imageShadow && tw`shadow`,
28 | ]);
29 |
30 | const DecoratorBlob = styled(SvgDotPattern)((props) => [
31 | tw`w-20 h-20 absolute right-0 bottom-0 transform translate-x-1/2 translate-y-1/2 fill-current text-primary-500 -z-10`,
32 | ]);
33 |
34 | const TextContent = tw.div`lg:py-8 text-center md:text-left`;
35 |
36 | const Subheading = tw(SubheadingBase)`text-center md:text-left`;
37 | const Heading = tw(
38 | SectionHeading
39 | )`mt-4 font-black text-left text-3xl sm:text-4xl lg:text-5xl text-center md:text-left leading-tight`;
40 | const Description = tw.p`mt-4 text-center md:text-left text-sm md:text-base lg:text-lg font-medium leading-relaxed text-secondary-100`;
41 |
42 | const PrimaryButton = styled(PrimaryButtonBase)((props) => [
43 | tw`mt-8 md:mt-8 text-sm inline-block mx-auto md:mx-0`,
44 | props.buttonRounded && tw`rounded-full`,
45 | ]);
46 |
47 | export default ({
48 | subheading = "Our Expertise",
49 | heading = (
50 | <>
51 | Designed & Developed by Professionals.
52 | >
53 | ),
54 | description = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
55 | primaryButtonText = "Learn More",
56 | primaryButtonUrl = "https://timerse.com",
57 | imageSrc = TeamIllustrationSrc,
58 | buttonRounded = true,
59 | imageRounded = true,
60 | imageBorder = false,
61 | imageShadow = false,
62 | imageCss = null,
63 | imageDecoratorBlob = false,
64 | imageDecoratorBlobCss = null,
65 | textOnLeft = true,
66 | }) => {
67 | // The textOnLeft boolean prop can be used to display either the text on left or right side of the image.
68 |
69 | return (
70 |
71 |
72 |
73 |
80 | {imageDecoratorBlob && }
81 |
82 |
83 |
84 | {subheading}
85 | {heading}
86 | {description}
87 |
92 | {primaryButtonText}
93 |
94 |
95 |
96 |
97 |
98 | );
99 | };
100 |
--------------------------------------------------------------------------------
/client/src/components/features/TwoColWithSteps.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import tw from "twin.macro";
3 | import styled from "styled-components";
4 | import { css } from "styled-components/macro"; //eslint-disable-line
5 | import { SectionHeading, Subheading as SubheadingBase } from "components/misc/Headings.js";
6 | import TeamIllustrationSrc from "images/team-illustration-2.svg";
7 | import { ReactComponent as SvgDotPattern } from "images/dot-pattern.svg";
8 |
9 | const Container = tw.div`relative`;
10 | const TwoColumn = tw.div`flex flex-col md:flex-row justify-between max-w-screen-xl mx-auto py-20 md:py-24 items-center`;
11 | const Column = tw.div`w-full max-w-md mx-auto md:max-w-none md:mx-0`;
12 | const ImageColumn = tw(Column)`md:w-6/12 flex-shrink-0 relative`;
13 | const TextColumn = styled(Column)(props => [
14 | tw`md:w-6/12 mt-16 md:mt-0`,
15 | props.textOnLeft ? tw`md:mr-12 lg:mr-16 md:order-first` : tw`md:ml-12 lg:ml-16 md:order-last`
16 | ]);
17 |
18 | const Image = styled.img(props => [
19 | props.imageRounded && tw`rounded`,
20 | props.imageBorder && tw`border`,
21 | props.imageShadow && tw`shadow`
22 | ]);
23 |
24 | const DecoratorBlob = styled(SvgDotPattern)(() => [
25 | tw`w-20 h-20 absolute right-0 bottom-0 transform translate-x-1/2 translate-y-1/2 fill-current text-primary-500 -z-10`
26 | ]);
27 |
28 | const TextContent = tw.div`lg:py-8 text-center md:text-left`;
29 |
30 | const Subheading = tw(SubheadingBase)`text-center md:text-left`;
31 | const Heading = tw(
32 | SectionHeading
33 | )`mt-4 font-black text-left text-3xl sm:text-4xl lg:text-5xl text-center md:text-left leading-tight`;
34 |
35 | const Steps = tw.ul`mt-12`;
36 | const Step = tw.li`mt-8 flex flex-col md:flex-row items-center md:items-start`;
37 | const StepNumber = tw.div`font-semibold text-4xl leading-none text-gray-400`;
38 | const StepText = tw.div`mt-3 md:mt-0 md:ml-6`;
39 | const StepHeading = tw.h6`leading-none text-xl font-semibold`;
40 | const StepDescription = tw.p`mt-3 max-w-xs leading-loose text-sm text-gray-600 font-medium`;
41 |
42 | export default ({
43 | subheading = "Our Expertise",
44 | heading = (
45 | <>
46 | Designed & Developed by Professionals.
47 | >
48 | ),
49 | imageSrc = TeamIllustrationSrc,
50 | imageRounded = true,
51 | imageBorder = false,
52 | imageShadow = false,
53 | imageDecoratorBlob = false,
54 | textOnLeft = true,
55 | steps = null,
56 | decoratorBlobCss = null,
57 | }) => {
58 | // The textOnLeft boolean prop can be used to display either the text on left or right side of the image.
59 |
60 | const defaultSteps = [
61 | {
62 | heading: "Register",
63 | description: "Create an account with us using Google or Facebook."
64 | },
65 | {
66 | heading: "Download",
67 | description: "Browse and Download the template that you like from the marketplace."
68 | },
69 | {
70 | heading: "Run",
71 | description: "Follow the instructions to setup and customize the template to your needs."
72 | }
73 | ];
74 |
75 | if (!steps) steps = defaultSteps;
76 |
77 | return (
78 |
79 |
80 |
81 |
82 | {imageDecoratorBlob && }
83 |
84 |
85 |
86 | {subheading}
87 | {heading}
88 |
89 | {steps.map((step, index) => (
90 |
91 | {(index+1).toString().padStart(2,'0')}
92 |
93 | {step.heading}
94 | {step.description}
95 |
96 |
97 | ))}
98 |
99 |
100 |
101 |
102 |
103 | );
104 | };
105 |
--------------------------------------------------------------------------------
/client/src/components/features/TwoColWithTwoFeaturesAndButtons.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import tw from "twin.macro";
3 | import styled from "styled-components";
4 | import { css } from "styled-components/macro"; //eslint-disable-line
5 | import { SectionHeading, Subheading as SubheadingBase } from "components/misc/Headings.js";
6 | import { PrimaryButton as PrimaryButtonBase } from "components/misc/Buttons.js";
7 | import { ReactComponent as BriefcaseIcon } from "feather-icons/dist/icons/briefcase.svg";
8 | import { ReactComponent as MoneyIcon } from "feather-icons/dist/icons/dollar-sign.svg";
9 | import TeamIllustrationSrc from "images/team-illustration-2.svg";
10 |
11 | const Container = tw.div`relative`;
12 | const TwoColumn = tw.div`flex flex-col md:flex-row justify-between max-w-screen-xl mx-auto py-20 md:py-24`;
13 | const Column = tw.div`w-full max-w-md mx-auto md:max-w-none md:mx-0`;
14 | const ImageColumn = tw(Column)`md:w-5/12 flex-shrink-0 h-80 md:h-auto`;
15 | const TextColumn = styled(Column)(props => [
16 | tw`md:w-7/12 mt-16 md:mt-0`,
17 | props.textOnLeft ? tw`md:mr-12 lg:mr-16 md:order-first` : tw`md:ml-12 lg:ml-16 md:order-last`
18 | ]);
19 |
20 | const Image = styled.div(props => [
21 | `background-image: url("${props.imageSrc}");`,
22 | tw`rounded bg-contain bg-no-repeat bg-center h-full`
23 | ]);
24 | const TextContent = tw.div`lg:py-8 text-center md:text-left`;
25 |
26 | const Subheading = tw(SubheadingBase)`text-center md:text-left`;
27 | const Heading = tw(
28 | SectionHeading
29 | )`mt-4 font-black text-left text-3xl sm:text-4xl lg:text-5xl text-center md:text-left leading-tight`;
30 | const Description = tw.p`mt-4 text-center md:text-left text-sm md:text-base lg:text-lg font-medium leading-relaxed text-secondary-100`;
31 |
32 | const Features = tw.div`mt-8 max-w-sm mx-auto md:mx-0`;
33 | const Feature = tw.div`mt-8 flex items-start flex-col md:flex-row`;
34 |
35 | const FeatureIconContainer = styled.div`
36 | ${tw`mx-auto inline-block border border-primary-500 text-center rounded-full p-2 flex-shrink-0`}
37 | svg {
38 | ${tw`w-5 h-5 text-primary-500`}
39 | }
40 | `;
41 |
42 | const FeatureText = tw.div`mt-4 md:mt-0 md:ml-4 text-center md:text-left`;
43 | const FeatureHeading = tw.div`font-bold text-lg text-primary-500`;
44 | const FeatureDescription = tw.div`mt-1 text-sm`;
45 |
46 | const PrimaryButton = tw(PrimaryButtonBase)`mt-8 md:mt-10 text-sm inline-block mx-auto md:mx-0`;
47 |
48 | export default ({
49 | subheading = "Our Expertise",
50 | heading = (
51 | <>
52 | We have the most professional marketing team.
53 | >
54 | ),
55 | description = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
56 | primaryButtonText = "See Our Portfolio",
57 | primaryButtonUrl = "https://timerse.com",
58 | features = null,
59 | textOnLeft = true
60 | }) => {
61 | // The textOnLeft boolean prop can be used to display either the text on left or right side of the image.
62 |
63 | /*
64 | * Change the features variable as you like, add or delete objects
65 | * `icon` must be a React SVG component. See how BriefcaseIcon is imported above. For a full list of available icons, see Feather Icons.
66 | */
67 | const defaultFeatures = [
68 | {
69 | Icon: BriefcaseIcon,
70 | title: "Professionalism",
71 | description: "We have the best professional marketing people across the globe just to work with you."
72 | },
73 | {
74 | Icon: MoneyIcon,
75 | title: "Affordable",
76 | description: "We promise to offer you the best rate we can - at par with the industry standard."
77 | }
78 | ];
79 |
80 | if (!features) features = defaultFeatures;
81 |
82 | return (
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 | {subheading}
91 | {heading}
92 | {description}
93 |
94 | {features.map((feature, index) => (
95 |
96 | { }
97 |
98 | {feature.title}
99 | {feature.description}
100 |
101 |
102 | ))}
103 |
104 |
105 | {primaryButtonText}
106 |
107 |
108 |
109 |
110 |
111 | );
112 | };
113 |
--------------------------------------------------------------------------------
/client/src/components/features/VerticalWithAlternateImageAndText.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "styled-components";
3 | import tw from "twin.macro";
4 | import { ReactComponent as SvgDotPatternIcon } from "../../images/dot-pattern.svg";
5 | import { SectionHeading as HeadingTitle } from "../misc/Headings.js";
6 |
7 | const Container = tw.div`relative`;
8 |
9 | const SingleColumn = tw.div`max-w-screen-xl mx-auto py-20 lg:py-24`;
10 |
11 | const HeadingInfoContainer = tw.div`flex flex-col items-center`;
12 | const HeadingDescription = tw.p`mt-4 font-medium text-gray-600 text-center max-w-sm`;
13 |
14 | const Content = tw.div`mt-16`;
15 |
16 | const Card = styled.div(props => [
17 | tw`mt-24 md:flex justify-center items-center`,
18 | props.reversed ? tw`flex-row-reverse` : "flex-row"
19 | ]);
20 | const Image = styled.div(props => [
21 | `background-image: url("${props.imageSrc}");`,
22 | tw`rounded md:w-1/2 lg:w-5/12 xl:w-1/3 flex-shrink-0 h-80 md:h-144 bg-cover bg-center mx-4 sm:mx-8 md:mx-4 lg:mx-8`
23 | ]);
24 | const Details = tw.div`mt-4 md:mt-0 md:max-w-md mx-4 sm:mx-8 md:mx-4 lg:mx-8`;
25 | const Subtitle = tw.div`font-bold tracking-wide text-secondary-100`;
26 | const Title = tw.h4`text-3xl font-bold text-gray-900`;
27 | const Description = tw.p`mt-2 text-sm leading-loose`;
28 | const Link = tw.a`inline-block mt-4 text-sm text-primary-500 font-bold cursor-pointer transition duration-300 border-b-2 border-transparent hover:border-primary-500`;
29 |
30 | const SvgDotPattern1 = tw(
31 | SvgDotPatternIcon
32 | )`absolute top-0 left-0 transform -translate-x-20 rotate-90 translate-y-8 -z-10 opacity-25 text-primary-500 fill-current w-24`;
33 | const SvgDotPattern2 = tw(
34 | SvgDotPatternIcon
35 | )`absolute top-0 right-0 transform translate-x-20 rotate-45 translate-y-24 -z-10 opacity-25 text-primary-500 fill-current w-24`;
36 | const SvgDotPattern3 = tw(
37 | SvgDotPatternIcon
38 | )`absolute bottom-0 left-0 transform -translate-x-20 rotate-45 -translate-y-8 -z-10 opacity-25 text-primary-500 fill-current w-24`;
39 | const SvgDotPattern4 = tw(
40 | SvgDotPatternIcon
41 | )`absolute bottom-0 right-0 transform translate-x-20 rotate-90 -translate-y-24 -z-10 opacity-25 text-primary-500 fill-current w-24`;
42 |
43 | export default () => {
44 | const cards = [
45 | {
46 | imageSrc:
47 | "https://images.unsplash.com/photo-1550699026-4114bbf4fb49?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=632&q=80",
48 | subtitle: "Paid",
49 | title: "Loachella, NYC",
50 | description:
51 | "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
52 | url: "https://timerse.com"
53 | },
54 |
55 | {
56 | imageSrc:
57 | "https://images.unsplash.com/photo-1543423924-b9f161af87e4?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=634&q=80",
58 | subtitle: "Free",
59 | title: "Rock In Rio, Upstate",
60 | description:
61 | "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
62 | url: "https://timerse.com"
63 | },
64 |
65 | {
66 | imageSrc:
67 | "https://images.unsplash.com/photo-1509824227185-9c5a01ceba0d?ixlib=rb-1.2.1&auto=format&fit=crop&w=658&q=80",
68 | subtitle: "Exclusive",
69 | title: "Lollapalooza, Manhattan",
70 | description:
71 | "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
72 | url: "https://timerse.com"
73 | }
74 | ];
75 |
76 | return (
77 |
78 |
79 |
80 | Popular Events
81 |
82 | Here are some of the most popular events in New York City curated by professionals.
83 |
84 |
85 |
86 |
87 | {cards.map((card, i) => (
88 |
89 |
90 |
91 | {card.subtitle}
92 | {card.title}
93 | {card.description}
94 | See Event Details
95 |
96 |
97 | ))}
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | );
106 | };
107 |
--------------------------------------------------------------------------------
/client/src/components/footers/MiniCenteredFooter.js:
--------------------------------------------------------------------------------
1 |
2 | import React from "react";
3 | import tw from "twin.macro";
4 | import styled from "styled-components";
5 | import { Container as ContainerBase } from "../misc/Layouts.js";
6 | import logo from "../../images/logo.svg";
7 | import { ReactComponent as FacebookIcon } from "../../images/facebook-icon.svg";
8 | import { ReactComponent as TwitterIcon } from "../../images/twitter-icon.svg";
9 | import { ReactComponent as YoutubeIcon } from "../../images/youtube-icon.svg";
10 |
11 | const Container = tw(ContainerBase)`bg-gray-900 text-gray-100 -mx-8 -mb-8`;
12 | const Content = tw.div`max-w-screen-xl mx-auto py-20 lg:py-24`;
13 |
14 | const Row = tw.div`flex items-center justify-center flex-col px-8`;
15 |
16 | const LogoContainer = tw.div`flex items-center justify-center md:justify-start`;
17 | const LogoImg = tw.img`w-8`;
18 | const LogoText = tw.h5`ml-2 text-2xl font-black tracking-wider`;
19 |
20 | const LinksContainer = tw.div`mt-8 font-medium flex flex-wrap justify-center items-center flex-col sm:flex-row`;
21 | const Link = tw.a`border-b-2 border-transparent hocus:text-gray-300 hocus:border-gray-300 pb-1 transition duration-300 mt-2 mx-4`;
22 |
23 | const SocialLinksContainer = tw.div`mt-10`;
24 | const SocialLink = styled.a`
25 | ${tw`cursor-pointer inline-block text-gray-100 hover:text-gray-500 transition duration-300 mx-4`}
26 | svg {
27 | ${tw`w-5 h-5`}
28 | }
29 | `;
30 |
31 | const CopyrightText = tw.p`text-center mt-10 font-medium tracking-wide text-sm text-gray-600`;
32 | export default () => {
33 | return (
34 |
35 |
36 |
37 |
38 |
39 | TICKETaka
40 |
41 |
42 | Home
43 | About
44 | Contact Us
45 | Blog
46 | Reviews
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | © Copyright 2020, Treact Inc. All Rights Reserved.
61 |
62 |
63 |
64 |
65 | );
66 | };
67 |
--------------------------------------------------------------------------------
/client/src/components/form.css:
--------------------------------------------------------------------------------
1 | .formzz{
2 | background-color: #222020;
3 | display: grid;
4 | grid-template-columns: 3fr 3fr 3fr ;
5 | justify-content: space-around;
6 | align-items: center;
7 | width: auto;
8 | padding: 0px;
9 | margin: 6px;
10 | border-radius: 5px;
11 | }
12 |
13 | .formzz span{
14 | margin: 0;
15 | padding: 0;
16 | color: white;
17 | font-size: 1rem;
18 | }
19 | .formzz select{
20 | margin: 0;
21 | padding: 0;
22 | width: 100%;
23 | height: 100%;
24 | background-color: gray;
25 | color: white;
26 | font-size: 1rem;
27 | border: none;
28 | outline: none;
29 | }
30 |
--------------------------------------------------------------------------------
/client/src/components/hero/TwoColumnWithVideo.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "styled-components";
3 | import tw from "twin.macro";
4 |
5 | import { css } from "styled-components/macro";
6 | import AnchorLink from "react-anchor-link-smooth-scroll";
7 |
8 | import HeaderBase from "../headers/light.js";
9 |
10 | import { ReactComponent as SvgDecoratorBlob1 } from "../../images/svg-decorator-blob-1.svg";
11 | import { ReactComponent as SvgDecoratorBlob2 } from "../../images/dot-pattern.svg";
12 |
13 | const Container = tw.div`relative`;
14 | const TwoColumn = tw.div`flex flex-col lg:flex-row md:items-center max-w-screen-xl mx-auto py-20 md:py-24`;
15 | const LeftColumn = tw.div`relative lg:w-6/12 lg:pr-12 flex-shrink-0 text-center lg:text-left`;
16 | const RightColumn = tw.div`relative mt-12 lg:mt-0 flex flex-col justify-center`;
17 | const Header = tw(HeaderBase)`max-w-none`;
18 | const Heading = tw.h1`font-black text-3xl md:text-5xl leading-snug max-w-3xl`;
19 | const Paragraph = tw.p`my-5 lg:my-8 text-sm lg:text-base font-medium text-gray-600 max-w-lg mx-auto lg:mx-0`;
20 |
21 | const Actions = tw.div`flex flex-col items-center sm:flex-row justify-center lg:justify-start mt-8`;
22 | const PrimaryButton = tw.button`font-bold px-8 lg:px-10 py-3 rounded bg-primary-500 text-gray-100 hocus:bg-primary-700 focus:shadow-outline focus:outline-none transition duration-300`;
23 |
24 | const IllustrationContainer = tw.div`flex justify-center md:justify-end items-center relative max-w-3xl lg:max-w-none`;
25 |
26 | // Random Decorator Blobs (shapes that you see in background)
27 | const DecoratorBlob1 = styled(SvgDecoratorBlob1)`
28 | ${tw`pointer-events-none opacity-5 absolute left-0 bottom-0 h-64 w-64 transform -translate-x-2/3 -z-10`}
29 | `;
30 | const DecoratorBlob2 = styled(SvgDecoratorBlob2)`
31 | ${tw`pointer-events-none fill-current text-primary-500 opacity-25 absolute w-32 h-32 right-0 bottom-0 transform translate-x-10 translate-y-10 -z-10`}
32 | `;
33 |
34 | export default ({
35 | heading = "Modern React Templates, Just For You",
36 | description = "Our templates are easy to setup, understand and customize. Fully modular components with a variety of pages and components.",
37 | imageCss = null,
38 | imageDecoratorBlob = false,
39 | }) => {
40 | return (
41 | <>
42 |
43 |
44 |
45 |
46 | {heading}
47 | {description}
48 |
49 |
50 | Order Now
51 |
52 |
53 |
54 |
55 |
56 |
57 | {imageDecoratorBlob && }
58 |
59 |
60 |
61 |
62 |
63 | >
64 | );
65 | };
66 |
--------------------------------------------------------------------------------
/client/src/components/hero/hero.css:
--------------------------------------------------------------------------------
1 |
2 | .typing-demo {
3 | width: 102ch;
4 | animation: typing 2s steps(22), blink .5s step-end infinite alternate;
5 |
6 |
7 | }
8 |
9 | @keyframes typing {
10 | from {
11 | width: 0
12 | }
13 | }
14 |
15 | @keyframes blink {
16 | 50% {
17 | border-color: transparent
18 | }
19 | }
--------------------------------------------------------------------------------
/client/src/components/misc/Buttons.js:
--------------------------------------------------------------------------------
1 | import tw from "twin.macro";
2 | export const PrimaryButton = tw.button`px-8 py-3 font-bold rounded bg-primary-500 text-gray-100 hocus:bg-primary-700 hocus:text-gray-200 focus:shadow-outline focus:outline-none transition duration-300`;
3 |
--------------------------------------------------------------------------------
/client/src/components/misc/Headings.js:
--------------------------------------------------------------------------------
1 | import tw from "twin.macro";
2 |
3 | export const SectionHeading = tw.h2`text-4xl sm:text-5xl font-black tracking-wide text-center`
4 | export const Subheading = tw.h5`font-bold text-primary-500`
5 |
--------------------------------------------------------------------------------
/client/src/components/misc/Layouts.js:
--------------------------------------------------------------------------------
1 | import tw from "twin.macro";
2 |
3 | export const Container = tw.div`relative`;
4 | export const ContentWithPaddingXl= tw.div`max-w-screen-xl mx-auto py-20 lg:py-24`;
5 | export const ContentWithPaddingLg= tw.div`max-w-screen-lg mx-auto py-20 lg:py-24`;
6 | export const ContentWithVerticalPadding = tw.div`py-20 lg:py-24`;
7 | export const Content2Xl= tw.div`max-w-screen-2xl mx-auto`;
8 |
--------------------------------------------------------------------------------
/client/src/components/misc/Links.js:
--------------------------------------------------------------------------------
1 | import tw from "twin.macro";
2 |
3 | export const PrimaryLink = tw.a`cursor-pointer font-bold text-primary-500 border-b-2 border-transparent hocus:border-primary-500 hocus:text-primary-800 transition duration-300`;
4 |
--------------------------------------------------------------------------------
/client/src/components/misc/Typography.js:
--------------------------------------------------------------------------------
1 | import tw from "twin.macro";
2 | export const SectionDescription = tw.p`mt-4 text-sm md:text-base lg:text-lg font-medium leading-relaxed text-secondary-100 max-w-xl`;
3 |
--------------------------------------------------------------------------------
/client/src/components/myTickets/Ticket.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "./Ticket.css";
3 | import tw from "twin.macro";
4 |
5 | const { useState } = React;
6 | const rootElement = document.getElementById("root");
7 | const PrimaryButton = tw.button`font-bold px-8 lg:px-10 py-3 rounded bg-primary-500 text-gray-100 hocus:bg-primary-700 focus:shadow-outline focus:outline-none transition duration-300`;
8 | const SecondaryButton = tw.button`font-bold px-8 lg:px-10 py-3 rounded bg-primary-500 text-gray-100`;
9 | const Value = tw.span`font-bold text-primary-500`;
10 | const Key = tw.div`font-medium text-gray-700`;
11 | const Ticket = (props) => {
12 | const { value } = props;
13 | const [active, handleActive] = useState(false);
14 | const match = value.Ticket.Match;
15 | const day = match.Date.substring(0, 10);
16 | const tiime = match.Date.substring(11, 19);
17 | return (
18 | <>
19 | {
26 | handleActive(!active);
27 | }}
28 | >
29 |
30 |
31 |
32 | Home
33 |
34 | {match.homeTeam}
35 |
36 |
37 |
38 |
39 | Away
40 |
41 | {match.awayTeam}
42 |
43 |
44 |
53 |
54 |
55 |
56 | Category:
57 | {value.Ticket.category}
58 |
59 |
66 |
71 | Ticket ID:
72 | {value.Ticket.id}
73 |
74 |
75 |
76 |
77 |
78 |
79 | {match.roundNumber}
80 |
Round
81 |
82 |
83 | {tiime}
84 |
Time
85 |
86 |
87 |
88 |
89 | {match.location}
90 |
Location
91 |
92 |
93 | {day}
94 |
Date
95 |
96 |
97 |
98 |
99 | {value.Ticket.gateOpens}
100 |
Gate Opens
101 |
102 |
103 | {match.group}
104 |
Group
105 |
106 |
107 |
108 |
117 |
118 |
119 |
120 |
121 | ${value.Ticket.price}
122 |
Ticket Price
123 |
124 |
125 | {value.Ticket.category}
126 |
Category
127 |
128 |
132 |
133 |
134 |
135 |
136 |
137 |
138 | >
139 | );
140 | };
141 |
142 | export default Ticket;
143 |
--------------------------------------------------------------------------------
/client/src/components/paymentSF/PaymentFail.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import "./payment.scss";
3 | import { useNavigate } from "react-router-dom";
4 | import { Helmet } from "react-helmet";
5 |
6 | const PaymentFail = () => {
7 | const navigate = useNavigate();
8 | const [timeLeft, setTimeLeft] = useState(5);
9 |
10 | useEffect(() => {
11 | if (timeLeft === 0) {
12 | navigate("/");
13 | }
14 |
15 | // exit early when we reach 0
16 | if (!timeLeft) return;
17 |
18 | // save intervalId to clear the interval when the
19 | // component re-renders
20 | const intervalId = setInterval(() => {
21 | setTimeLeft(timeLeft - 1);
22 | }, 1000);
23 |
24 | // clear interval on re-render to avoid memory leaks
25 | return () => clearInterval(intervalId);
26 | // add timeLeft as a dependency to re-rerun the effect
27 | // when we update it
28 | }, [timeLeft]);
29 | return (
30 | <>
31 |
32 |
33 |
34 |
43 |
44 |
49 |
60 |
70 |
82 |
94 |
95 |
96 |
Payment Failed
97 |
98 | Please try again later...
99 | You'll be redirected in: {timeLeft} seconds
100 |
101 |
102 |
103 | >
104 | );
105 | };
106 |
107 | export default PaymentFail;
108 |
--------------------------------------------------------------------------------
/client/src/components/paymentSF/PaymentSuccess.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import "./payment.scss";
3 | import { useNavigate } from "react-router-dom";
4 | import { Helmet } from "react-helmet";
5 |
6 | const PaymentSuccess = () => {
7 | const navigate = useNavigate();
8 | const [timeLeft, setTimeLeft] = useState(5);
9 |
10 | useEffect(() => {
11 | if (timeLeft === 0) {
12 | navigate("/");
13 | }
14 |
15 | // exit early when we reach 0
16 | if (!timeLeft) return;
17 |
18 | // save intervalId to clear the interval when the
19 | // component re-renders
20 | const intervalId = setInterval(() => {
21 | setTimeLeft(timeLeft - 1);
22 | }, 1000);
23 |
24 | // clear interval on re-render to avoid memory leaks
25 | return () => clearInterval(intervalId);
26 | // add timeLeft as a dependency to re-rerun the effect
27 | // when we update it
28 | }, [timeLeft]);
29 | return (
30 | <>
31 |
32 |
33 |
34 |
43 |
44 |
81 |
Success
82 |
83 | We received your purchase request!
84 | You'll be redirected in: {timeLeft} seconds
85 |
86 |
87 |
88 | >
89 | );
90 | };
91 |
92 | export default PaymentSuccess;
93 |
--------------------------------------------------------------------------------
/client/src/components/paymentSF/payment.scss:
--------------------------------------------------------------------------------
1 |
2 | @import url("https://fonts.googleapis.com/css?family=Nunito+Sans:400,400i,700,900&display=swap");
3 |
4 | $font: "Nunito Sans", "Helvetica Neue", sans-serif;
5 |
6 |
7 | .hh1 {
8 | color: #73AF55;
9 | font-family: $font;
10 | font-weight: 900;
11 | font-size: 40px;
12 | margin-top: 20px;
13 | margin-bottom: 10px;
14 | }
15 | .hk1 {
16 | color: #D06079;
17 | font-family: $font;
18 | font-weight: 900;
19 | font-size: 40px;
20 | margin-top: 20px;
21 | margin-bottom: 10px;
22 | }
23 | .ppp {
24 | color: #404F5E;
25 | font-family: $font;
26 | font-size:20px;
27 | margin: 0;
28 | }
29 | .iiii {
30 | color: #9ABC66;
31 | font-size: 100px;
32 | line-height: 200px;
33 | margin-left:-15px;
34 | }
35 | .cardddd {
36 | background: white;
37 | padding: 60px;
38 | border-radius: 4px;
39 | box-shadow: 0 2px 3px #C8D0D8;
40 | display: inline-block;
41 | margin: 0 auto;
42 | margin-top: 10px;
43 | }
44 | .jiii {
45 | color: #c64747;
46 | font-size: 100px;
47 | line-height: 200px;
48 |
49 | }
50 |
51 |
52 |
--------------------------------------------------------------------------------
/client/src/components/select.js:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import MenuItem from "@mui/material/MenuItem";
3 | import FormControl from "@mui/material/FormControl";
4 | import Select from "@mui/material/Select";
5 | import AppContext from "../context/Total";
6 |
7 | export default function SelectLabels({ num, setProgress, progress, remaining }) {
8 | // eslint-disable-next-line no-unused-vars
9 | const [selected, setSelected] = React.useState({ amount: "", category: num });
10 | const { calculateTotal } = React.useContext(AppContext);
11 |
12 | const handleChange = (event) => {
13 | //set the selected to the amount of tickets and num
14 | setSelected({
15 | amount: event.target.value,
16 | category: num,
17 | });
18 | calculateTotal({ amount: event.target.value, category: num });
19 | if (progress < 50) {
20 | setProgress((prev) => prev + 25);
21 | }
22 | };
23 |
24 | return (
25 |
26 |
27 |
34 |
35 | 0
36 |
37 | {remaining >= 1 && 1 }
38 | { remaining >=2 && 2 }
39 | {remaining >=3 && 3 }
40 | {remaining >=4 && 4 }
41 |
42 |
43 |
44 | );
45 | }
46 |
--------------------------------------------------------------------------------
/client/src/components/socialMedia/SocialMedia.jsx:
--------------------------------------------------------------------------------
1 | import './socialMedia.css'
2 |
3 | const SocialMedia = () => {
4 | return (
5 | <>
6 |
7 |
8 | GOOGLE
9 |
10 | >
11 | )
12 | }
13 |
14 | export default SocialMedia
--------------------------------------------------------------------------------
/client/src/components/testimonials/ThreeColumnWithProfileImage.js:
--------------------------------------------------------------------------------
1 |
2 | import React from "react";
3 | import styled from "styled-components";
4 | import tw from "twin.macro";
5 | import { css } from "styled-components/macro";
6 | import { ContentWithPaddingXl, Container } from "../misc/Layouts.js";
7 | import {
8 | SectionHeading as Heading,
9 | Subheading as SubheadingBase,
10 | } from "../misc/Headings.js";
11 | import { ReactComponent as SvgDecoratorBlob1 } from "../../images/svg-decorator-blob-7.svg";
12 | import { ReactComponent as SvgDecoratorBlob2 } from "../../images/svg-decorator-blob-8.svg";
13 |
14 | const Subheading = tw(SubheadingBase)`text-center`;
15 | const Testimonials = tw.div`flex flex-col lg:flex-row items-center lg:items-stretch`;
16 | const TestimonialContainer = tw.div`mt-16 lg:w-1/3`;
17 | const Testimonial = tw.div`px-4 text-center max-w-xs mx-auto flex flex-col items-center`;
18 | const Image = tw.img`w-20 h-20 rounded-full`;
19 | const Quote = tw.blockquote`mt-5 text-gray-600 font-medium leading-loose`;
20 | const CustomerName = tw.p`mt-5 text-gray-900 font-semibold uppercase text-sm tracking-wide`;
21 |
22 | const DecoratorBlob1 = styled(SvgDecoratorBlob1)`
23 | ${tw`pointer-events-none -z-20 absolute left-0 top-0 h-56 w-56 opacity-15 transform -translate-x-2/3 -translate-y-12 text-teal-400`}
24 | `;
25 | const DecoratorBlob2 = styled(SvgDecoratorBlob2)`
26 | ${tw`pointer-events-none -z-20 absolute right-0 bottom-0 h-64 w-64 opacity-15 transform translate-x-2/3 text-yellow-500`}
27 | `;
28 |
29 | export default ({
30 | subheading = "Testimonials",
31 | heading = "Customer's Review",
32 | testimonials = [
33 | {
34 | imageSrc: "/assets/customer1.jpeg",
35 | quote:
36 | "Exceptional service! I would recommend anyone who wants wc tickets to check them out!",
37 | customerName: "Dr Amr",
38 | },
39 | {
40 | imageSrc: "/assets/customer2.jpeg",
41 | quote:
42 | "Outstanding. Got my tickets in about 6 hours. Amazing customer service as well.",
43 | customerName: "Mustafa Gouda",
44 | },
45 | {
46 | imageSrc:
47 | "https://images.unsplash.com/photo-1580852300654-03c803a14e24?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=4.25&w=512&h=512&q=80",
48 | quote:
49 | "What is this? Unbelievable service! Got my tickets and really enjoyed the match :)",
50 | customerName: "Steven Marcetti",
51 | },
52 | ],
53 | }) => {
54 | return (
55 |
56 |
57 | {subheading && {subheading} }
58 | {heading}
59 |
60 | {testimonials.map((testimonial, index) => (
61 |
62 |
63 |
64 | "{testimonial.quote}"
65 | - {testimonial.customerName}
66 |
67 |
68 | ))}
69 |
70 |
71 |
72 |
73 |
74 |
75 | );
76 | };
77 |
--------------------------------------------------------------------------------
/client/src/context/Total.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react";
2 | import { createContext } from "react";
3 |
4 | const AppContext = createContext();
5 |
6 | export const AppContextProvider = ({ children }) => {
7 | const [total, setTotal] = useState(0);
8 | const [totalChoices, setTotalChoices] = useState(0);
9 | const [choices, setChoices] = useState({
10 | 1: 0,
11 | 2: 0,
12 | 3: 0,
13 | 4: 0,
14 | });
15 | const [without, setWithout] = useState({
16 | 1: 0,
17 | 2: 0,
18 | 3: 0,
19 | 4: 0,
20 | });
21 | const priceForCategory = {
22 | 1: 75,
23 | 2: 125,
24 | 3: 195,
25 | 4: 400,
26 | };
27 |
28 | useEffect(() => {
29 | //set values to default
30 | setTotal(0);
31 | setTotalChoices(0);
32 | setChoices({
33 | 1: 0,
34 | 2: 0,
35 | 3: 0,
36 | 4: 0,
37 | });
38 | setWithout({
39 | 1: 0,
40 | 2: 0,
41 | 3: 0,
42 | 4: 0,
43 | });
44 | }, []);
45 |
46 | const calculateTotal = ({ amount, category }) => {
47 | const newChoices = { ...choices };
48 | const newWithout = { ...without };
49 | newChoices[category] = amount * priceForCategory[category];
50 | newWithout[category] = amount;
51 | setWithout(newWithout);
52 | setChoices(newChoices);
53 | //! loop through the newChoices object and add up the values
54 | const newTotal = Object.values(newChoices).reduce(
55 | (acc, curr) => acc + curr,
56 | 0
57 | );
58 | const newTotalChoices = Object.values(newWithout).reduce(
59 | (acc, curr) => acc + curr,
60 | 0
61 | );
62 | setTotalChoices(newTotalChoices);
63 | setTotal(newTotal);
64 | };
65 |
66 | return (
67 |
70 | {children}
71 |
72 | );
73 | };
74 |
75 | export default AppContext;
76 |
--------------------------------------------------------------------------------
/client/src/firebase/config.js:
--------------------------------------------------------------------------------
1 | import firebase from "firebase/app";
2 | import "firebase/firestore";
3 | import "firebase/auth";
4 | import "firebase/storage";
5 | import axios from "axios";
6 |
7 | const firebaseConfig = {
8 | apiKey: "AIzaSyB0wrVkEFTxmnTGNdJ4d3xrV8q7VvTrA4w",
9 | authDomain: "authexample-ed1cc.firebaseapp.com",
10 | projectId: "authexample-ed1cc",
11 | storageBucket: "authexample-ed1cc.appspot.com",
12 | messagingSenderId: "387386983833",
13 | appId: "1:387386983833:web:03f3325b57126601bd7f08",
14 | };
15 |
16 | // init firebase
17 | firebase.initializeApp(firebaseConfig);
18 |
19 | // init services
20 |
21 | //! this give us user info, is authenticated
22 | const auth = firebase.auth();
23 |
24 | const signInWithGoogle = ({ setRerender }) => {
25 | auth
26 | .signInWithPopup(provider)
27 | .then(async (result) => {
28 | var credential = result.credential;
29 | var token = credential.accessToken;
30 | var user = result.user;
31 | const { displayName, email, photoURL } = user;
32 | // console.log(displayName, email , photoURL)
33 |
34 | // send an axios request to https://user-blush.vercel.app/api/users/create with body of{name, email, token}
35 | const { data } = await axios.post(
36 | "https://user-blush.vercel.app/api/users/create",
37 | { name: displayName, email, token }
38 | );
39 |
40 | if (!data) {
41 | console.log("error");
42 | }
43 |
44 | setRerender((prev) => !prev);
45 | })
46 | .catch((error) => {
47 | console.log(error);
48 | });
49 | };
50 |
51 | var provider = new firebase.auth.GoogleAuthProvider();
52 |
53 | export { auth, provider, signInWithGoogle };
54 |
55 | //! to login
56 | // import { signInWithGoogle } from '../../firebase/config'
57 | // onClick={signInWithGoogle}
58 |
59 | //! to logout
60 | // import { auth } from '../firebase/config'
61 | // auth.signOut() ==> onClick
62 |
--------------------------------------------------------------------------------
/client/src/helpers/AnimationRevealPage.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import tw from "twin.macro";
3 |
4 | /* framer-motion and useInView here are used to animate the sections in when we reach them in the viewport
5 | */
6 | import { motion } from "framer-motion";
7 | import useInView from "./useInView";
8 |
9 | const StyledDiv = tw.div`font-display min-h-screen text-secondary-500 p-8 overflow-hidden`;
10 | function AnimationReveal({ disabled, children }) {
11 | if (disabled) {
12 | return <>{children}>;
13 | }
14 |
15 | if (!Array.isArray(children)) children = [children];
16 |
17 | const directions = ["left", "right"];
18 | const childrenWithAnimation = children.map((child, i) => {
19 | return (
20 |
24 | {child}
25 |
26 | );
27 | });
28 | return <>{childrenWithAnimation}>;
29 | }
30 |
31 | function AnimatedSlideInComponent({
32 | direction = "left",
33 | offset = 30,
34 | children,
35 | }) {
36 | const [ref, inView] = useInView({ margin: `-${offset}px 0px 0px 0px` });
37 |
38 | const x = { target: "0%" };
39 |
40 | if (direction === "left") x.initial = "-150%";
41 | else x.initial = "150%";
42 |
43 | return (
44 |
45 |
55 | {children}
56 |
57 |
58 | );
59 | }
60 |
61 | export default (props) => (
62 |
63 |
64 |
65 | );
66 |
--------------------------------------------------------------------------------
/client/src/helpers/useAnimatedNavToggler.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import { useAnimation, useCycle } from "framer-motion";
3 |
4 | //Below logic is for toggling the navbar when toggleNavbar is called. It is used on mobile toggling of navbar.
5 | export default function useAnimatedNavToggler() {
6 | const [showNavLinks, setShowNavLinks] = useState(false);
7 | const [x, cycleX] = useCycle("0%", "150%");
8 | const animation = useAnimation();
9 |
10 | const toggleNavbar = () => {
11 | setShowNavLinks(!showNavLinks);
12 | animation.start({ x: x, display: "block" });
13 | cycleX();
14 | };
15 |
16 | return {showNavLinks,animation, toggleNavbar }
17 | }
18 |
--------------------------------------------------------------------------------
/client/src/helpers/useInView.js:
--------------------------------------------------------------------------------
1 | import { useRef } from "react"
2 | import { useInView as useInViewFromFramer } from 'framer-motion'
3 |
4 | export default function useInView({ once = true, margin = "-30px 0px 0px 0px"} = {}) {
5 | const ref = useRef(null)
6 | const isInView = useInViewFromFramer(ref, {
7 | once: once
8 | })
9 |
10 | return [ref, isInView]
11 | }
--------------------------------------------------------------------------------
/client/src/images/arrow-left-2-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/client/src/images/arrow-left-3-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/client/src/images/arrow-right-2-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/client/src/images/arrow-right-3-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/client/src/images/arrow-right-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/client/src/images/celebration-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/src/images/checkbox-circle.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/client/src/images/chef-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/src/images/customer-support-illustration.svg:
--------------------------------------------------------------------------------
1 | active support
--------------------------------------------------------------------------------
/client/src/images/customers-logo-strip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/src/images/customers-logo-strip.png
--------------------------------------------------------------------------------
/client/src/images/customize-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/src/images/email-newsletter-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/client/src/images/facebook-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/client/src/images/fast-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/src/images/github-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
--------------------------------------------------------------------------------
/client/src/images/google-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/src/images/google-icon.png
--------------------------------------------------------------------------------
/client/src/images/google-play-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/src/images/google-play-icon.png
--------------------------------------------------------------------------------
/client/src/images/handle-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/client/src/images/handshake.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/src/images/handshake.jpg
--------------------------------------------------------------------------------
/client/src/images/hero-screenshot-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/src/images/hero-screenshot-1.png
--------------------------------------------------------------------------------
/client/src/images/hero-screenshot-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/src/images/hero-screenshot-2.png
--------------------------------------------------------------------------------
/client/src/images/linkedin-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/client/src/images/loc-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/src/images/loc-icon.png
--------------------------------------------------------------------------------
/client/src/images/logo-full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/src/images/logo-full.png
--------------------------------------------------------------------------------
/client/src/images/logo-light.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/client/src/images/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/client/src/images/quotes-l.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/client/src/images/quotes-r.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/client/src/images/reliable-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/src/images/shield-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/src/images/shop-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/src/images/simple-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/src/images/stadium-empty.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/src/images/stadium-empty.png
--------------------------------------------------------------------------------
/client/src/images/stadium-left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/src/images/stadium-left.png
--------------------------------------------------------------------------------
/client/src/images/stadium-right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/src/images/stadium-right.png
--------------------------------------------------------------------------------
/client/src/images/star-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/client/src/images/support-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/src/images/svg-decorator-blob-1.svg:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/client/src/images/svg-decorator-blob-10.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/client/src/images/svg-decorator-blob-2.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | bubbles
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/client/src/images/svg-decorator-blob-3.svg:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/client/src/images/svg-decorator-blob-4.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/client/src/images/svg-decorator-blob-5.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/client/src/images/svg-decorator-blob-6.svg:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/client/src/images/svg-decorator-blob-7.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/client/src/images/svg-decorator-blob-8.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/client/src/images/svg-decorator-blob-9.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/client/src/images/twitter-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/client/src/images/twitter-icon.png
--------------------------------------------------------------------------------
/client/src/images/twitter-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/client/src/images/youtube-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/client/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { createRoot } from "react-dom/client";
3 | import App from "./App";
4 | import Modal from "react-modal";
5 |
6 | Modal.setAppElement("#root");
7 |
8 | const container = document.getElementById("root");
9 | const root = createRoot(container);
10 | root.render( );
11 |
--------------------------------------------------------------------------------
/client/src/pages/About.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Profile from "../components/cards/ProfileThreeColGrid";
3 | import { Container, ContentWithPaddingXl } from "../components/misc/Layouts.js";
4 | import tw from "twin.macro";
5 | import styled from "styled-components";
6 | import { ReactComponent as SvgDecoratorBlob1 } from "../images/svg-decorator-blob-5.svg";
7 | import { ReactComponent as SvgDecoratorBlob2 } from "../images/svg-decorator-blob-7.svg";
8 | import HeaderBase from "../components/headers/light.js";
9 | import { css } from "styled-components/macro"; //eslint-disable-line
10 | import AnimationRevealPage from "../helpers/AnimationRevealPage";
11 |
12 | //decorations
13 | const DecoratorBlob1 = styled(SvgDecoratorBlob1)`
14 | ${tw`pointer-events-none -z-20 absolute right-0 top-0 h-64 w-64 opacity-15 transform translate-x-2/3 -translate-y-12 text-pink-400`}
15 | `;
16 | const DecoratorBlob2 = styled(SvgDecoratorBlob2)`
17 | ${tw`pointer-events-none -z-20 absolute left-0 bottom-0 h-80 w-80 opacity-15 transform -translate-x-2/3 text-primary-500`}
18 | `;
19 |
20 | const Header = tw(HeaderBase)`max-w-none`;
21 | const About = () => {
22 | return (
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | );
32 | };
33 |
34 | export default About;
35 |
--------------------------------------------------------------------------------
/client/src/pages/Book.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import tw from "twin.macro";
3 | import HeaderBase from "../components/headers/light.js";
4 | import AnimationRevealPage from "../helpers/AnimationRevealPage.js";
5 | import MainFeature from "../components/features/TwoColSingleFeatureWithStats.js";
6 | import { useAsync } from "react-async-hook";
7 | import { getCatagories } from "../services/shop";
8 | const Header = tw(HeaderBase)`max-w-none`;
9 | import { useParams,useNavigate } from "react-router-dom";
10 | import BallLoading from "../components/ballLoading/BallLoading.js";
11 | import { auth } from "../firebase/config";
12 |
13 | export const Book = () => {
14 | const { matchId } = useParams();
15 | const navigate = useNavigate();
16 | const { result, loading, error } = useAsync(getCatagories, [matchId]);
17 | if (loading) return ;
18 | if (error) {
19 | navigate("/", { state: { errors: error.message } });
20 | }
21 |
22 | return (
23 |
24 |
25 |
26 |
27 | );
28 | };
29 |
--------------------------------------------------------------------------------
/client/src/pages/Checkout.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { SectionHeading } from "../components/misc/Headings.js";
3 | import { useLocation, useParams, useNavigate } from "react-router-dom";
4 | import tw from "twin.macro";
5 | import styled from "styled-components";
6 | import HeaderBase from "../components/headers/light.js";
7 | import AnimationRevealPage from "../helpers/AnimationRevealPage.js";
8 | import CheckoutComponent from "../components/checkout/index";
9 | import ProgressBar from "@ramonak/react-progress-bar";
10 | import Countdown from "react-countdown";
11 | import { ReactComponent as SvgDecoratorBlob1 } from "../images/svg-decorator-blob-5.svg";
12 | import { ReactComponent as SvgDecoratorBlob2 } from "../images/svg-decorator-blob-7.svg";
13 | import { Container, ContentWithPaddingXl } from "../components/misc/Layouts.js";
14 | import { getHoldById } from "../services/shop";
15 | import { useAsync } from "react-async-hook";
16 | import BallLoading from "../components/ballLoading/BallLoading.js";
17 | import axios from "axios";
18 |
19 | const DecoratorBlob1 = styled(SvgDecoratorBlob1)`
20 | ${tw`pointer-events-none -z-20 absolute right-0 top-0 h-64 w-64 opacity-15 transform translate-x-2/3 -translate-y-12 text-pink-400`}
21 | `;
22 | const DecoratorBlob2 = styled(SvgDecoratorBlob2)`
23 | ${tw`pointer-events-none -z-20 absolute left-0 bottom-0 h-80 w-80 opacity-15 transform -translate-x-2/3 text-primary-500`}
24 | `;
25 | const Value = tw.div`font-bold text-lg sm:text-xl lg:text-2xl text-secondary-500 tracking-wide`;
26 |
27 | const Header = tw(HeaderBase)`max-w-none`;
28 | const Key = tw.div`font-medium text-primary-700`;
29 | const Checkout = () => {
30 | const { state } = useLocation();
31 | const navigate = useNavigate();
32 | const [progress, setProgress] = React.useState(50);
33 | const { id } = useParams();
34 |
35 | const { result, error, loading } = useAsync(getHoldById, [id]);
36 |
37 | if (loading) return ;
38 |
39 | if (error || !state) {
40 | navigate("/");
41 | }
42 |
43 | const Completionist = async () => {
44 | const body = {
45 | session: sessionId,
46 | kafka: state.kafka,
47 | };
48 | navigate("/", {
49 | state: {
50 | errors: "Session expired",
51 | },
52 | });
53 | await axios.post(
54 | "https://reservation-two.vercel.app/api/reservation/cancel",
55 | body
56 | );
57 |
58 | return Session ended ;
59 | };
60 |
61 | const renderer = ({ minutes, seconds, completed }) => {
62 | if (completed) {
63 | return ;
64 | } else {
65 | return (
66 |
67 | {minutes}:{seconds}
68 |
69 | );
70 | }
71 | };
72 |
73 | return (
74 |
75 |
76 |
77 |
78 |
79 |
87 |
94 |
95 | Session ends in
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | );
106 | };
107 |
108 | export default Checkout;
109 |
--------------------------------------------------------------------------------
/client/src/pages/Contact.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Profile from "../components/cards/ProfileThreeColGrid";
3 | import { Container, ContentWithPaddingXl } from "../components/misc/Layouts.js";
4 | import tw from "twin.macro";
5 | import styled from "styled-components";
6 | import { ReactComponent as SvgDecoratorBlob1 } from "../images/svg-decorator-blob-5.svg";
7 | import { ReactComponent as SvgDecoratorBlob2 } from "../images/svg-decorator-blob-7.svg";
8 | import HeaderBase from "../components/headers/light.js";
9 | import Form from "../components/contact/TwoColContactUsWithIllustrationFullForm.js";
10 | import FAQs from "../components/contact/SingleCol.js";
11 | import { css } from "styled-components/macro"; //eslint-disable-line
12 | import AnimationRevealPage from "../helpers/AnimationRevealPage";
13 |
14 | //decorations
15 | const DecoratorBlob1 = styled(SvgDecoratorBlob1)`
16 | ${tw`pointer-events-none -z-20 absolute right-0 top-0 h-64 w-64 opacity-15 transform translate-x-2/3 -translate-y-12 text-pink-400`}
17 | `;
18 | const DecoratorBlob2 = styled(SvgDecoratorBlob2)`
19 | ${tw`pointer-events-none -z-20 absolute left-0 bottom-0 h-80 w-80 opacity-15 transform -translate-x-2/3 text-primary-500`}
20 | `;
21 |
22 | const Header = tw(HeaderBase)`max-w-none`;
23 | const Contact = () => {
24 | return (
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | );
33 | };
34 |
35 | export default Contact;
36 |
--------------------------------------------------------------------------------
/client/src/services/payment.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | const baseUrl = 'https://payment-eosin.vercel.app/api/'
3 |
4 |
5 | export const createPaymentIntent = async (ticket) => {
6 | const response = await axios.post(baseUrl + "pay", ticket);
7 | return response.data;
8 | }
9 |
--------------------------------------------------------------------------------
/client/src/services/reservation.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | const baseUrl = "https://reservation-two.vercel.app/api/reservation/";
3 |
4 |
5 | export const addTicketToCart = async (ticket) => {
6 | const response = await axios.post(baseUrl + "pending", ticket);
7 | return response.data;
8 | }
9 |
10 | export const purchased = async (ticket) => {
11 | const response = await axios.post(baseUrl + "purchase", ticket);
12 | return response.data;
13 | }
14 |
15 | export const cancel = async (ticket) => {
16 | const response = await axios.post(baseUrl + "cancel", ticket);
17 | return response.data;
18 | }
19 |
--------------------------------------------------------------------------------
/client/src/services/shop.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | const baseUrl = "https://shop-delta-khaki.vercel.app/api/shop/"
3 |
4 | export const getAllMatches= async () => {
5 | const response = await axios.get(baseUrl + "matches");
6 | return response.data;
7 | }
8 |
9 | export const getAvailability = async (matchId) => {
10 | const response = await axios.get(baseUrl + "availability/" + matchId);
11 | return response.data;
12 | }
13 |
14 | export const getAllAvailability = async () => {
15 | const response = await axios.get(baseUrl + "allAvailability");
16 | return response.data;
17 | }
18 |
19 | export const getHold = async () => {
20 | const response = await axios.get(baseUrl + "hold");
21 | return response.data;
22 | }
23 | // get hold by id
24 | export const getHoldById = async (id) => {
25 | const response = await axios.get(baseUrl + "hold/" + id);
26 | return response.data;
27 | }
28 |
29 | export const getCatagories = async (matchId) => {
30 | const response = await axios.get(baseUrl + "catagories/" + matchId);
31 | return response.data;
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/client/tailwind.config.js:
--------------------------------------------------------------------------------
1 | const withMT = require("@material-tailwind/react/utils/withMT");
2 |
3 | module.exports = withMT({
4 | content: ["./src/**/*.{js,jsx,ts,tsx}"],
5 | theme: {
6 | extend: {},
7 | },
8 | plugins: [],
9 | });
--------------------------------------------------------------------------------
/payment/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/payment/.DS_Store
--------------------------------------------------------------------------------
/payment/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/payment/app.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const createError = require('http-errors');
3 | const morgan = require('morgan');
4 | require('dotenv').config();
5 | const cors = require('cors');
6 |
7 | const app = express();
8 | app.use(express.json());
9 | app.use(express.urlencoded({ extended: false }));
10 | app.use(morgan('dev'));
11 | app.use(cors());
12 |
13 |
14 | app.get('/', (req, res) => {
15 | res.send('beeb-beeb-boob');
16 | });
17 |
18 | app.use('/api', require('./routes/api.route'));
19 |
20 | app.use((req, res, next) => {
21 | next(createError.NotFound());
22 | });
23 |
24 | app.use((err, req, res, next) => {
25 | res.status(err.status || 500);
26 | res.send({
27 | status: err.status || 500,
28 | message: err.message,
29 | });
30 | });
31 |
32 | const PORT = process.env.PORT || 3001;
33 | app.listen(PORT, () => console.log(`🚀 @ http://localhost:${PORT}`));
34 |
--------------------------------------------------------------------------------
/payment/controller/payment.js:
--------------------------------------------------------------------------------
1 | const axios = require("axios");
2 |
3 |
4 | const createPaymentIntent = async (req, res, next) => {
5 | const stripe = require("stripe")(
6 | "sk_test_51MItmuKwGmHCcom1WKlEEhKiyXIRXxGfn5TxYnfz5cdCzjMI7wVqSwJnyBJDMs4fqEbJFO8rhd10BV7vCX3ALnVX001sM8GugT"
7 | );
8 |
9 | const { amount, email, token,holdId, uid,kafka } = req.body;
10 |
11 | console.log("hi")
12 | //! create a customer
13 | stripe.customers
14 | .create({
15 | email: email,
16 | source: token.id,
17 | name: token.card.name,
18 | })
19 | .then((customer) => {
20 | return stripe.charges.create({
21 | amount: parseFloat(amount) * 100,
22 | description: `Payment for USD ${amount}`,
23 | currency: "USD",
24 | customer: customer.id,
25 | });
26 | })
27 | .then(async (charge) => {
28 | await axios.post('https://reservation-two.vercel.app/api/reservation/purchase', {holdId: holdId, user: uid, kafka: kafka})
29 | return res.status(200).json(charge)
30 | })
31 | .catch(async(err) =>{
32 | await axios.post('https://reservation-two.vercel.app/api/reservation/cancel', {session: holdId})
33 | console.log(err)
34 | return res.status(400).json(err)
35 | });
36 | };
37 |
38 | module.exports = {
39 | createPaymentIntent
40 | };
41 |
--------------------------------------------------------------------------------
/payment/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "draft",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "app.js",
6 | "scripts": {
7 | "start": "nodemon app.js"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "MIT",
12 | "dependencies": {
13 | "axios": "^1.2.2",
14 | "cors": "^2.8.5",
15 | "dotenv": "^16.0.3",
16 | "express": "^4.18.2",
17 | "http-errors": "^2.0.0",
18 | "morgan": "^1.10.0",
19 | "stripe": "^11.5.0",
20 | "underscore": "^1.13.6"
21 | },
22 | "devDependencies": {
23 | "nodemon": "^2.0.20"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/payment/routes/api.route.js:
--------------------------------------------------------------------------------
1 | const router = require('express').Router();
2 | const { createPaymentIntent } = require('../controller/payment');
3 |
4 | router.post('/pay', createPaymentIntent);
5 |
6 | module.exports = router;
7 |
--------------------------------------------------------------------------------
/payment/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 2,
3 | "builds": [
4 | {
5 | "src": "app.js",
6 | "use": "@vercel/node"
7 | }
8 | ],
9 | "env": {
10 | "STRIPE":"sk_test_51MItmuKwGmHCcom1WKlEEhKiyXIRXxGfn5TxYnfz5cdCzjMI7wVqSwJnyBJDMs4fqEbJFO8rhd10BV7vCX3ALnVX001sM8GugT"
11 | },
12 | "routes": [{"handle": "filesystem"},
13 | {
14 | "src": "/.*",
15 | "dest": "app.js"
16 | }
17 | ]
18 | }
--------------------------------------------------------------------------------
/security/.gitignore:
--------------------------------------------------------------------------------
1 | .env
2 | node_modules
--------------------------------------------------------------------------------
/security/blacklist.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/security/blacklist.txt
--------------------------------------------------------------------------------
/security/index.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const rateLimit = require('express-rate-limit');
3 | const { isIPBlocked } = require('./middlewares/IPBlocking');
4 | const { corsHeaders } = require('./middlewares/cors');
5 | const axios = require('axios');
6 | const asyncHandler = require('express-async-handler')
7 | const Waf = require('mini-waf/wafbase');
8 | const wafrules = require('mini-waf/wafrules');
9 |
10 | const app = express();
11 | app.use(express.json());
12 | app.use(Waf.WafMiddleware(wafrules.DefaultSettings));
13 |
14 | const limiter = rateLimit({
15 | windowMs: 1000 * 60 * 15,
16 | max : 100,
17 | message : "Too many requests, please try later"
18 | })
19 |
20 | app.get('/matches', corsHeaders , isIPBlocked , limiter , asyncHandler(async(req , res) => {
21 | const par = req.query.p
22 | const url = "https://ticketaty-shop.vercel.app/matches/?p=" + par
23 | await axios.get(url , {
24 | headers: { "Accept-Encoding": "gzip,deflate,compress" }
25 | })
26 | .then((response) => (res.send(response.data)))
27 | .catch((e) => {res.send(e)})
28 | }))
29 |
30 | app.get('/matches/:id' , corsHeaders , isIPBlocked , limiter , asyncHandler(async (req , res) => {
31 | const par = req.params.id
32 | const url = "https://ticketaty-shop.vercel.app/matches/" + par
33 | await axios.get(url , {
34 | headers: { "Accept-Encoding": "gzip,deflate,compress" }
35 | })
36 | .then((response) => (res.send(response.data)))
37 | .catch((e) => {res.send(e)})
38 | }))
39 |
40 | app.get('/search/:team' , corsHeaders , isIPBlocked , limiter , asyncHandler(async (req , res) => {
41 | const par = req.params.team
42 | const url = "https://ticketaty-shop.vercel.app/search/" + par
43 | await axios.get(url , {
44 | headers: { "Accept-Encoding": "gzip,deflate,compress" }
45 | })
46 | .then((response) => (res.send(response.data)))
47 | .catch((e) => {res.send(e)})
48 | //axios call to actual endpoints except pending
49 | }))
50 |
51 | app.post('/reservation' , corsHeaders , isIPBlocked , limiter , asyncHandler(async (req , res) => {
52 | req.body;
53 | const url = "https://ticketaty-reservations.vercel.app/api/reservation"
54 | await axios.post(url , req.body , {
55 | headers: { "Accept-Encoding": "gzip,deflate,compress" }
56 | })
57 | .then((response) => (res.send(response.data)))
58 | .catch((e) => {res.status(400).send(e)})
59 | //axios call to actual endpoints except pending
60 | }))
61 |
62 | app.get('/ticket/:email' , corsHeaders , isIPBlocked , limiter , asyncHandler(async (req , res) => {
63 | const par = req.params.email
64 | const url = "https://ticketaty-shop.vercel.app/ticket/" + par
65 | await axios.get(url , {
66 | headers: { "Accept-Encoding": "gzip,deflate,compress" }
67 | })
68 | .then((response) => (res.send(response.data)))
69 | .catch((e) => {res.send(e)})
70 | //axios call to actual endpoints except pending
71 | }))
72 |
73 | app.options('/reservation',corsHeaders, async (req, res) => {
74 | res.status(200).json({message: 'ok'});
75 | })
76 |
77 | app.listen('4000', () => {
78 | console.log('app listening on port 4000')
79 | })
--------------------------------------------------------------------------------
/security/middlewares/IPBlocking.js:
--------------------------------------------------------------------------------
1 | require('dotenv').config();
2 |
3 | var blacklist = process.env.BLACKLIST.split(", ");
4 |
5 | var getClientIp = function(req) {
6 | var ipAddress = req.connection.remoteAddress;
7 |
8 | if (!ipAddress) {
9 | return '';
10 | }// convert from "::ffff:192.0.0.1" to "192.0.0.1"
11 |
12 | if (ipAddress.substr(0, 7) == "::ffff:") {
13 | ipAddress = ipAddress.substr(7)
14 | }
15 |
16 | return ipAddress;
17 | };
18 |
19 | //Part3, Blocking Client IP, if it is in the blacklist
20 | const isIPBlocked = (req, res, next) => {
21 | var ipAddress = getClientIp(req);
22 | if(blacklist.indexOf(ipAddress) === -1){
23 | next();
24 | }
25 |
26 | else {
27 | res.send(ipAddress + ' IP is not in whiteList')
28 | }
29 | };
30 |
31 | module.exports = {isIPBlocked};
--------------------------------------------------------------------------------
/security/middlewares/cors.js:
--------------------------------------------------------------------------------
1 | const corsHeaders = (req, res, next) => {
2 | res.header("Access-Control-Allow-Origin", "*"); // update to match the domain you will make the request from
3 | res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
4 | next();
5 | }
6 |
7 | module.exports = {corsHeaders}
--------------------------------------------------------------------------------
/security/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ticketaty-security",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "nodemon index.js"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "axios": "^1.2.1",
13 | "mini-waf": "^1.0.0",
14 | "dotenv": "^16.0.3",
15 | "express": "^4.18.2",
16 | "express-async-handler": "^1.2.0",
17 | "express-limiter": "^1.6.1",
18 | "express-rate-limit": "^6.7.0",
19 | "moment": "^2.29.4",
20 | "nodemon": "^2.0.20",
21 | "redis": "^4.5.1",
22 | "request": "^2.88.2"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/security/suspicious.log:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/security/suspicious.log
--------------------------------------------------------------------------------
/security/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 2,
3 | "builds": [
4 | {
5 | "src": "./index.js",
6 | "use": "@vercel/node"
7 | }
8 | ],
9 | "routes": [
10 | {
11 | "src": "/(.*)",
12 | "dest": "index.js"
13 | }
14 | ]
15 | }
--------------------------------------------------------------------------------
/seed/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "shop",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "app.js",
6 | "scripts": {
7 | "start": "nodemon app.js"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "type": "module",
12 | "license": "MIT",
13 | "dependencies": {
14 | "@prisma/client": "^4.7.1",
15 | "axios": "^1.2.0",
16 | "cookie-parser": "^1.4.6",
17 | "cors": "^2.8.5",
18 | "dotenv": "^16.0.3",
19 | "express": "^4.18.2",
20 | "http-errors": "^2.0.0",
21 | "morgan": "^1.10.0",
22 | "node-fetch": "^3.3.0",
23 | "prisma": "^4.7.1"
24 | },
25 | "devDependencies": {
26 | "nodemon": "^2.0.20"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/seed/seeder/MockData/data generator/matches/genMatch.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs')
2 | const data = require('./world_cup_matches.json')
3 | const whichCon = require('./whichCon')
4 | let matchNum = 0
5 |
6 | newData = data.map( (match) => {
7 | const homeTeamContinents = whichCon(match.HomeTeam)
8 | const awayTeamContinents = whichCon(match.AwayTeam)
9 |
10 | matchNum += 1
11 | return {
12 | "roundNumber": match.RoundNumber,
13 | "Date": match.DateUtc,
14 | "location": match.Location,
15 | "homeTeam": match.HomeTeam,
16 | "homeTeamContinents": homeTeamContinents,
17 | "awayTeam": match.AwayTeam,
18 | "awayTeamContinents": awayTeamContinents,
19 | "group": match.Group,
20 | "rank": matchNum
21 | }
22 |
23 |
24 | })
25 |
26 | fs.writeFileSync('./matches.json', JSON.stringify(newData, null, 2) , 'utf-8');
27 |
28 |
--------------------------------------------------------------------------------
/seed/seeder/MockData/data generator/matches/whichCon.js:
--------------------------------------------------------------------------------
1 | // a function which takes a country name and return its continent
2 | module.exports = (country) => {
3 | switch (country) {
4 | case 'Russia':
5 | return 'Europe'
6 | case 'SaudiArabia':
7 | return 'Asia'
8 | case 'Egypt':
9 | return 'Africa'
10 | case 'Uruguay':
11 | return 'South America'
12 | case 'Portugal':
13 | return 'Europe'
14 | case 'Spain':
15 | return 'Europe'
16 | case 'Morocco':
17 | return 'Africa'
18 | case 'Iran':
19 | return 'Asia'
20 | case 'France':
21 | return 'Europe'
22 | case 'Australia':
23 | return 'Oceania'
24 | case 'Peru':
25 | return 'South America'
26 | case 'Denmark':
27 | return 'Europe'
28 | case 'Argentina':
29 | return 'South America'
30 | case 'Iceland':
31 | return 'Europe'
32 | case 'Croatia':
33 | return 'Europe'
34 | case 'Nigeria':
35 | return 'Africa'
36 | case 'Brazil':
37 | return 'South America'
38 | case 'Switzerland':
39 | return 'Europe'
40 | case 'Costa Rica':
41 | return 'North America'
42 | case 'Serbia':
43 | return 'Europe'
44 | case 'Germany':
45 | return 'Europe'
46 | case 'Mexico':
47 | return 'North America'
48 | case 'Sweden':
49 | return 'Europe'
50 | case 'South Korea':
51 | return 'Asia'
52 | case 'Belgium':
53 | return 'Europe'
54 | case 'Panama':
55 | return 'North America'
56 | case 'Tunisia':
57 | return 'Africa'
58 | case 'England':
59 | return 'Europe'
60 | case 'Poland':
61 | return 'Europe'
62 | case 'Senegal':
63 | return 'Africa'
64 | case 'Colombia':
65 | return 'South America'
66 | case 'Netherlands':
67 | return 'Europe'
68 | case 'USA':
69 | return 'North America'
70 | case 'Japan':
71 | return 'Asia'
72 | case 'Canada':
73 | return 'North America'
74 | case 'Korea':
75 | return 'Asia'
76 | case 'Ghana':
77 | return 'Africa'
78 | case 'Wales':
79 | return 'Europe'
80 | case 'Qatar':
81 | return 'Asia'
82 | case 'Ecuador':
83 | return 'South America'
84 | case 'Cameroon':
85 | return 'Africa'
86 | case 'Serbia':
87 | return 'Europe'
88 | default:
89 | return ''
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/seed/seeder/MockData/fillDB.js:
--------------------------------------------------------------------------------
1 | import { PrismaClient } from '@prisma/client'
2 | const prisma = new PrismaClient()
3 | import user from './customer.js'
4 | import match from './matches.js'
5 | import tickets from './tickets.js'
6 |
7 |
8 | async function main(){
9 |
10 |
11 | await prisma.ticket.createMany({
12 | data:[...tickets],
13 | })
14 | await prisma.ticket.createMany({
15 | data:[...tickets],
16 | })
17 | await prisma.ticket.createMany({
18 | data:[...tickets],
19 | })
20 | await prisma.ticket.createMany({
21 | data:[...tickets],
22 | })
23 | await prisma.ticket.createMany({
24 | data:[...tickets],
25 | })
26 | await prisma.ticket.createMany({
27 | data:[...tickets],
28 | })
29 | await prisma.ticket.createMany({
30 | data:[...tickets],
31 | })
32 | await prisma.ticket.createMany({
33 | data:[...tickets],
34 | })
35 | await prisma.ticket.createMany({
36 | data:[...tickets],
37 | })
38 | await prisma.ticket.createMany({
39 | data:[...tickets],
40 | })
41 | await prisma.ticket.createMany({
42 | data:[...tickets],
43 | })
44 | await prisma.ticket.createMany({
45 | data:[...tickets],
46 | })
47 | await prisma.ticket.createMany({
48 | data:[...tickets],
49 | })
50 |
51 |
52 |
53 |
54 |
55 | }
56 |
57 | main().catch((e)=>{
58 | console.log(e.message);
59 | }).finally(async ()=>{
60 | await prisma.$disconnect()
61 | })
--------------------------------------------------------------------------------
/seed/seeder/migrations/20221203141326_init/migration.sql:
--------------------------------------------------------------------------------
1 | -- CreateTable
2 | CREATE TABLE "Match" (
3 | "id" SERIAL NOT NULL,
4 | "name" TEXT NOT NULL DEFAULT 'TBD',
5 | "roundNumber" INTEGER NOT NULL,
6 | "Date" TIMESTAMP(3) NOT NULL,
7 | "location" TEXT NOT NULL,
8 | "homeTeam" TEXT NOT NULL,
9 | "homeTeamContinents" TEXT NOT NULL,
10 | "awayTeam" TEXT NOT NULL,
11 | "awayTeamContinents" TEXT NOT NULL,
12 | "group" TEXT NOT NULL,
13 | "homeTeamScore" INTEGER,
14 | "awayTeamScore" INTEGER,
15 | "rank" INTEGER NOT NULL,
16 |
17 | CONSTRAINT "Match_pkey" PRIMARY KEY ("id")
18 | );
19 |
20 | -- CreateTable
21 | CREATE TABLE "Customer" (
22 | "id" TEXT NOT NULL,
23 | "firstName" TEXT NOT NULL,
24 | "lastName" TEXT NOT NULL,
25 | "phone" INTEGER NOT NULL,
26 | "country" TEXT NOT NULL,
27 | "email" TEXT NOT NULL,
28 | "password" TEXT,
29 |
30 | CONSTRAINT "Customer_pkey" PRIMARY KEY ("id")
31 | );
32 |
33 | -- CreateTable
34 | CREATE TABLE "Ticket" (
35 | "id" TEXT NOT NULL,
36 | "pdfDescription" TEXT NOT NULL,
37 | "price" INTEGER NOT NULL,
38 | "barcode" TEXT,
39 | "gateOpens" TEXT NOT NULL,
40 | "category" TEXT NOT NULL,
41 | "external" BOOLEAN NOT NULL,
42 | "column" INTEGER NOT NULL,
43 | "matchId" INTEGER NOT NULL,
44 |
45 | CONSTRAINT "Ticket_pkey" PRIMARY KEY ("id")
46 | );
47 |
48 | -- CreateTable
49 | CREATE TABLE "Order" (
50 | "id" TEXT NOT NULL,
51 | "orderDate" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
52 | "requiredDate" TIMESTAMP(3),
53 | "shipedDate" TIMESTAMP(3),
54 | "status" TEXT NOT NULL DEFAULT 'shipping',
55 | "comments" TEXT,
56 | "price" INTEGER,
57 | "customerId" TEXT NOT NULL,
58 |
59 | CONSTRAINT "Order_pkey" PRIMARY KEY ("id")
60 | );
61 |
62 | -- CreateTable
63 | CREATE TABLE "Reservation" (
64 | "id" TEXT NOT NULL,
65 | "orderId" TEXT NOT NULL,
66 | "ticketsId" TEXT NOT NULL,
67 |
68 | CONSTRAINT "Reservation_pkey" PRIMARY KEY ("id")
69 | );
70 |
71 | -- AddForeignKey
72 | ALTER TABLE "Ticket" ADD CONSTRAINT "Ticket_matchId_fkey" FOREIGN KEY ("matchId") REFERENCES "Match"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
73 |
74 | -- AddForeignKey
75 | ALTER TABLE "Order" ADD CONSTRAINT "Order_customerId_fkey" FOREIGN KEY ("customerId") REFERENCES "Customer"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
76 |
77 | -- AddForeignKey
78 | ALTER TABLE "Reservation" ADD CONSTRAINT "Reservation_orderId_fkey" FOREIGN KEY ("orderId") REFERENCES "Order"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
79 |
80 | -- AddForeignKey
81 | ALTER TABLE "Reservation" ADD CONSTRAINT "Reservation_ticketsId_fkey" FOREIGN KEY ("ticketsId") REFERENCES "Ticket"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
82 |
--------------------------------------------------------------------------------
/seed/seeder/migrations/20221203142455_update_match/migration.sql:
--------------------------------------------------------------------------------
1 | /*
2 | Warnings:
3 |
4 | - You are about to drop the column `name` on the `Match` table. All the data in the column will be lost.
5 |
6 | */
7 | -- AlterTable
8 | ALTER TABLE "Match" DROP COLUMN "name",
9 | ALTER COLUMN "homeTeam" DROP NOT NULL,
10 | ALTER COLUMN "homeTeam" SET DEFAULT 'TBD',
11 | ALTER COLUMN "homeTeamContinents" DROP NOT NULL,
12 | ALTER COLUMN "awayTeam" DROP NOT NULL,
13 | ALTER COLUMN "awayTeam" SET DEFAULT 'TBD',
14 | ALTER COLUMN "awayTeamContinents" DROP NOT NULL;
15 |
--------------------------------------------------------------------------------
/seed/seeder/migrations/20221204124438_/migration.sql:
--------------------------------------------------------------------------------
1 | /*
2 | Warnings:
3 |
4 | - You are about to drop the column `column` on the `Ticket` table. All the data in the column will be lost.
5 | - You are about to drop the column `pdfDescription` on the `Ticket` table. All the data in the column will be lost.
6 | - A unique constraint covering the columns `[barcode]` on the table `Ticket` will be added. If there are existing duplicate values, this will fail.
7 |
8 | */
9 | -- AlterTable
10 | ALTER TABLE "Customer" ALTER COLUMN "lastName" DROP NOT NULL,
11 | ALTER COLUMN "phone" DROP NOT NULL,
12 | ALTER COLUMN "country" DROP NOT NULL,
13 | ALTER COLUMN "email" DROP NOT NULL;
14 |
15 | -- AlterTable
16 | ALTER TABLE "Ticket" DROP COLUMN "column",
17 | DROP COLUMN "pdfDescription";
18 |
19 | -- CreateIndex
20 | CREATE UNIQUE INDEX "Ticket_barcode_key" ON "Ticket"("barcode");
21 |
--------------------------------------------------------------------------------
/seed/seeder/migrations/20221204132848_init/migration.sql:
--------------------------------------------------------------------------------
1 | -- AlterTable
2 | ALTER TABLE "Match" ALTER COLUMN "roundNumber" DROP NOT NULL,
3 | ALTER COLUMN "Date" DROP NOT NULL,
4 | ALTER COLUMN "location" DROP NOT NULL,
5 | ALTER COLUMN "group" DROP NOT NULL,
6 | ALTER COLUMN "rank" DROP NOT NULL;
7 |
--------------------------------------------------------------------------------
/seed/seeder/migrations/20221204133223_int/migration.sql:
--------------------------------------------------------------------------------
1 | /*
2 | Warnings:
3 |
4 | - You are about to drop the `Match` table. If the table is not empty, all the data it contains will be lost.
5 |
6 | */
7 | -- DropForeignKey
8 | ALTER TABLE "Ticket" DROP CONSTRAINT "Ticket_matchId_fkey";
9 |
10 | -- DropTable
11 | DROP TABLE "Match";
12 |
--------------------------------------------------------------------------------
/seed/seeder/migrations/20221204133312_ll/migration.sql:
--------------------------------------------------------------------------------
1 | -- CreateTable
2 | CREATE TABLE "Match" (
3 | "id" SERIAL NOT NULL,
4 | "roundNumber" INTEGER,
5 | "Date" TIMESTAMP(3),
6 | "location" TEXT,
7 | "homeTeam" TEXT DEFAULT 'TBD',
8 | "homeTeamContinents" TEXT,
9 | "awayTeam" TEXT DEFAULT 'TBD',
10 | "awayTeamContinents" TEXT,
11 | "group" TEXT,
12 | "homeTeamScore" INTEGER,
13 | "awayTeamScore" INTEGER,
14 | "rank" INTEGER,
15 |
16 | CONSTRAINT "Match_pkey" PRIMARY KEY ("id")
17 | );
18 |
19 | -- AddForeignKey
20 | ALTER TABLE "Ticket" ADD CONSTRAINT "Ticket_matchId_fkey" FOREIGN KEY ("matchId") REFERENCES "Match"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
21 |
--------------------------------------------------------------------------------
/seed/seeder/migrations/20230108024909_fix/migration.sql:
--------------------------------------------------------------------------------
1 | /*
2 | Warnings:
3 |
4 | - Made the column `email` on table `Customer` required. This step will fail if there are existing NULL values in that column.
5 |
6 | */
7 | -- DropForeignKey
8 | ALTER TABLE "Order" DROP CONSTRAINT "Order_customerId_fkey";
9 |
10 | -- DropIndex
11 | DROP INDEX "Ticket_barcode_key";
12 |
13 | -- AlterTable
14 | ALTER TABLE "Customer" ALTER COLUMN "phone" SET DATA TYPE TEXT,
15 | ALTER COLUMN "email" SET NOT NULL;
16 |
17 | -- AlterTable
18 | ALTER TABLE "Order" ALTER COLUMN "customerId" DROP NOT NULL;
19 |
20 | -- AlterTable
21 | ALTER TABLE "Ticket" ADD COLUMN "holdId" TEXT,
22 | ADD COLUMN "isHold" BOOLEAN NOT NULL DEFAULT false,
23 | ADD COLUMN "isPurchased" BOOLEAN NOT NULL DEFAULT false;
24 |
25 | -- CreateTable
26 | CREATE TABLE "Hold" (
27 | "id" TEXT NOT NULL,
28 | "expiresIn" TIMESTAMP(3),
29 | "kafkaMessage" TEXT DEFAULT '',
30 |
31 | CONSTRAINT "Hold_pkey" PRIMARY KEY ("id")
32 | );
33 |
34 | -- CreateIndex
35 | CREATE UNIQUE INDEX "Hold_id_key" ON "Hold"("id");
36 |
37 | -- AddForeignKey
38 | ALTER TABLE "Ticket" ADD CONSTRAINT "Ticket_holdId_fkey" FOREIGN KEY ("holdId") REFERENCES "Hold"("id") ON DELETE SET NULL ON UPDATE CASCADE;
39 |
40 | -- AddForeignKey
41 | ALTER TABLE "Order" ADD CONSTRAINT "Order_customerId_fkey" FOREIGN KEY ("customerId") REFERENCES "Customer"("id") ON DELETE SET NULL ON UPDATE CASCADE;
42 |
--------------------------------------------------------------------------------
/seed/seeder/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"
--------------------------------------------------------------------------------
/seed/seeder/schema.prisma:
--------------------------------------------------------------------------------
1 | generator client {
2 | provider = "prisma-client-js"
3 | }
4 |
5 | datasource db {
6 | provider = "postgresql"
7 | url = env("DATABASE_URL")
8 | }
9 |
10 | model Match {
11 | id Int @id @default(autoincrement())
12 | roundNumber Int?
13 | Date DateTime?
14 | location String?
15 | homeTeam String? @default("TBD")
16 | homeTeamContinents String?
17 | awayTeam String? @default("TBD")
18 | awayTeamContinents String?
19 | group String?
20 | homeTeamScore Int?
21 | awayTeamScore Int?
22 | rank Int?
23 | tickets Ticket[]
24 | }
25 |
26 | model Customer {
27 | id String @id @default(uuid())
28 | firstName String
29 | lastName String?
30 | phone String?
31 | country String?
32 | email String
33 | password String?
34 | orders Order[]
35 | }
36 |
37 | model Ticket {
38 | id String @id @default(uuid())
39 | price Int
40 | barcode String?
41 | gateOpens String
42 | category String
43 | external Boolean
44 | matchId Int
45 | isHold Boolean @default(false)
46 | holdId String?
47 | isPurchased Boolean @default(false)
48 | reservations Reservation[]
49 | Hold Hold? @relation(fields: [holdId], references: [id])
50 | match Match @relation(fields: [matchId], references: [id])
51 | }
52 |
53 | model Order {
54 | id String @id @default(uuid())
55 | orderDate DateTime @default(now())
56 | requiredDate DateTime?
57 | shipedDate DateTime?
58 | status String @default("shipping")
59 | comments String?
60 | price Int?
61 | customerId String?
62 | customer Customer? @relation(fields: [customerId], references: [id])
63 | reservation Reservation[]
64 | }
65 |
66 | model Reservation {
67 | id String @id @default(uuid())
68 | orderId String
69 | ticketsId String
70 | order Order @relation(fields: [orderId], references: [id])
71 | tickets Ticket @relation(fields: [ticketsId], references: [id])
72 | }
73 |
74 | model Hold {
75 | id String @id @unique
76 | expiresIn DateTime?
77 | kafkaMessage String? @default("")
78 | Ticket Ticket[]
79 | }
80 |
--------------------------------------------------------------------------------
/shop/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Am0stafa/Tickets-Sales-System/fb211a3db06d269639a8db01872155e25d4f6bbe/shop/.DS_Store
--------------------------------------------------------------------------------
/shop/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@babel/preset-env"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/shop/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/shop/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "workbench.colorCustomizations": {
3 | "activityBar.activeBackground": "#ffffff",
4 | "activityBar.background": "#ffffff",
5 | "activityBar.foreground": "#15202b",
6 | "activityBar.inactiveForeground": "#15202b99",
7 | "activityBarBadge.background": "#df9f9f",
8 | "activityBarBadge.foreground": "#15202b",
9 | "commandCenter.border": "#15202b99",
10 | "sash.hoverBorder": "#ffffff",
11 | "statusBar.background": "#ffffff",
12 | "statusBar.foreground": "#15202b",
13 | "statusBarItem.hoverBackground": "#e6e6e6",
14 | "statusBarItem.remoteBackground": "#ffffff",
15 | "statusBarItem.remoteForeground": "#15202b",
16 | "titleBar.activeBackground": "#ffffff",
17 | "titleBar.activeForeground": "#15202b",
18 | "titleBar.inactiveBackground": "#ffffff99",
19 | "titleBar.inactiveForeground": "#15202b99"
20 | },
21 | "peacock.color": "#ffffff"
22 | }
--------------------------------------------------------------------------------
/shop/README.md:
--------------------------------------------------------------------------------
1 | # shop
2 | https://shop-delta-khaki.vercel.app/api/shop
3 |
--------------------------------------------------------------------------------
/shop/app.js:
--------------------------------------------------------------------------------
1 | import express from "express";
2 | import httpErrors from "http-errors";
3 | import morgan from "morgan";
4 | import cors from "cors";
5 | import cookieParser from "cookie-parser";
6 | import shopRouter from "./routes/shopRouter.js"
7 | import rateLimit from 'express-rate-limit'
8 | import isbot from 'isbot'
9 |
10 |
11 | const limiter = rateLimit({
12 | windowMs: 60000, // 1 minute
13 | max: 58, // Limit each IP to 100 requests per `window` (here, per 15 minutes)
14 | standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
15 | legacyHeaders: false, // Disable the `X-RateLimit-*` headers
16 | })
17 |
18 |
19 |
20 | const app = express();
21 | app.use(express.json());
22 | app.use(express.urlencoded({ extended: false }));
23 | app.use(cookieParser());
24 | app.use(morgan("dev"));
25 | app.use(cors());
26 | app.use(limiter)
27 |
28 | // app.use((req, res, next) => {
29 | // console.log(isbot(req.get('user-agent')))
30 | // if (isbot(req.get('user-agent'))) {
31 | // res.status(403).send("bot detected");
32 | // } else {
33 | // next();
34 | // }
35 | // })
36 | app.get("/", (req, res) => {
37 | res.send("beeb-beeb-boop");
38 | });
39 |
40 | app.use("/api/shop", shopRouter);
41 |
42 |
43 |
44 | app.use((err, req, res, next) => {
45 | res.status(err.status || 500);
46 | res.send({
47 | status: err.status || 500,
48 | message: err.message
49 | });
50 | });
51 |
52 | const PORT = 8082;
53 | app.listen(PORT, () => console.log(`🚀 @ http://localhost:${PORT}`));
54 | export default app;
55 |
--------------------------------------------------------------------------------
/shop/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "user",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "app.js",
6 | "scripts": {
7 | "start": "nodemon app.js"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "type": "module",
12 | "license": "MIT",
13 | "dependencies": {
14 | "@prisma/client": "^4.7.1",
15 | "cookie-parser": "^1.4.6",
16 | "cors": "^2.8.5",
17 | "dotenv": "^16.0.3",
18 | "express": "^4.18.2",
19 | "express-rate-limit": "^6.7.0",
20 | "http-errors": "^2.0.0",
21 | "isbot": "^3.6.5",
22 | "jest": "^29.3.1",
23 | "morgan": "^1.10.0",
24 | "prisma": "^4.7.1",
25 | "supertest": "^6.3.3"
26 | },
27 | "devDependencies": {
28 | "@babel/cli": "^7.20.7",
29 | "@babel/core": "^7.20.12",
30 | "@babel/preset-env": "^7.20.2",
31 | "nodemon": "^2.0.20"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/shop/prisma.js:
--------------------------------------------------------------------------------
1 | const { PrismaClient } = require('@prisma/client');
2 |
3 | const prisma = new PrismaClient();
4 |
5 | export default prisma;
6 |
--------------------------------------------------------------------------------
/shop/prisma/schema.prisma:
--------------------------------------------------------------------------------
1 | generator client {
2 | provider = "prisma-client-js"
3 | }
4 |
5 | datasource db {
6 | provider = "postgresql"
7 | url = env("DATABASE_URL")
8 | }
9 |
10 | model Match {
11 | id Int @id @default(autoincrement())
12 | roundNumber Int?
13 | Date DateTime?
14 | location String?
15 | homeTeam String? @default("TBD")
16 | homeTeamContinents String?
17 | awayTeam String? @default("TBD")
18 | awayTeamContinents String?
19 | group String?
20 | homeTeamScore Int?
21 | awayTeamScore Int?
22 | rank Int?
23 | tickets Ticket[]
24 | }
25 |
26 | model Customer {
27 | id String @id @default(uuid())
28 | firstName String
29 | lastName String?
30 | phone String?
31 | country String?
32 | email String?
33 | password String?
34 | orders Order[]
35 | }
36 |
37 | model Ticket {
38 | id String @id @default(uuid())
39 | price Int
40 | barcode String?
41 | gateOpens String
42 | category String
43 | external Boolean
44 | matchId Int
45 | isHold Boolean @default(false)
46 | holdId String?
47 | isPurchased Boolean @default(false)
48 | reservations Reservation[]
49 | Hold Hold? @relation(fields: [holdId], references: [id])
50 | match Match @relation(fields: [matchId], references: [id])
51 | }
52 |
53 | model Order {
54 | id String @id @default(uuid())
55 | orderDate DateTime @default(now())
56 | requiredDate DateTime?
57 | shipedDate DateTime?
58 | status String @default("shipping")
59 | comments String?
60 | price Int?
61 | customerId String?
62 | customer Customer? @relation(fields: [customerId], references: [id])
63 | reservation Reservation[]
64 | }
65 |
66 | model Reservation {
67 | id String @id @default(uuid())
68 | orderId String
69 | ticketsId String
70 | order Order @relation(fields: [orderId], references: [id])
71 | tickets Ticket @relation(fields: [ticketsId], references: [id])
72 | }
73 |
74 | model Hold {
75 | id String @id
76 | expiresIn DateTime
77 | Ticket Ticket[]
78 | }
79 |
--------------------------------------------------------------------------------
/shop/routes/shopRouter.js:
--------------------------------------------------------------------------------
1 | import express from "express";
2 | import shopController from "../controller/shopController.js";
3 |
4 | const router = express.Router();
5 |
6 | router.route("/matches").get(shopController.getAll);
7 | router.route("/availability/:matchId").get(shopController.getAvailability);
8 | router.route("/allAvailability").get(shopController.getAllAvailability);
9 | router.route("/hold").get(shopController.getHold);
10 | router.route("/hold/:id").get(shopController.getHoldMatch);
11 | router.route("/catagories/:matchId").get(shopController.availableCat);
12 |
13 | // Statistics
14 | router.route("/catagories/sold/:matchId").get(shopController.getSoldCat);
15 | router.route('/dashboard').get(shopController.getDashboard);
16 | router.route('/catagories/stats/:matchId').get(shopController.getCatStats);
17 |
18 | //
19 |
20 | export default router;
--------------------------------------------------------------------------------
/shop/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 2,
3 | "builds": [
4 | {
5 | "src": "app.js",
6 | "use": "@vercel/node"
7 | }
8 | ],
9 | "env": {
10 | "DATABASE_URL":"postgresql://postgres:XNlwLXurO2L5aTTJ@db.ckicjzurvlqafceovnen.supabase.co:5432/postgres"
11 | },
12 | "routes": [{"handle": "filesystem"},
13 | {
14 | "src": "/.*",
15 | "dest": "app.js"
16 | }
17 | ]
18 | }
--------------------------------------------------------------------------------
/user/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/user/app.js:
--------------------------------------------------------------------------------
1 | import express from "express";
2 | import createError from "http-errors";
3 | import morgan from "morgan";
4 | import cors from "cors";
5 | import cookieParser from "cookie-parser";
6 | import userRouter from "./routes/userRoutes.js";
7 | import * as dotenv from 'dotenv'
8 |
9 | const app = express();
10 | app.use(express.json());
11 | app.use(express.urlencoded({ extended: false }));
12 | app.use(cookieParser());
13 | app.use(morgan("dev"));
14 | app.use(cors());
15 | app.options("*", cors());
16 |
17 | app.use("/api/users", userRouter);
18 |
19 | app.use((req, res, next) => {
20 | next(createError.NotFound());
21 | });
22 |
23 | app.use((err, req, res, next) => {
24 | res.status(err.status || 500);
25 | res.send({
26 | status: err.status || 500,
27 | message: err.message
28 | });
29 | });
30 |
31 | const PORT = 3009;
32 | app.listen(PORT, () => console.log(`Server running on http://localhost:${PORT}`));
33 |
34 | export default app;
--------------------------------------------------------------------------------
/user/controllers/userController.js:
--------------------------------------------------------------------------------
1 | import { PrismaClient } from "@prisma/client";
2 | import crypto from "crypto";
3 | const prisma = new PrismaClient();
4 |
5 | const users = prisma.Customer;
6 |
7 | const getAllUsers = async (req, res) => {
8 | try {
9 | const allUsers = await users.findMany();
10 | res.status(200).json({ data: allUsers });
11 | } catch (error) {
12 | res.status(400).send(error.message);
13 | }
14 | };
15 |
16 | const createUser = async (req, res) => {
17 | try {
18 | const {name , email, token} = req.body;
19 |
20 | // first if a user with this email exists then we return status 200
21 | const user = await users.findMany({
22 | where: {
23 | email: email
24 | }
25 | });
26 | if(user.length !== 0) {
27 | return res.status(200).json({
28 | message: "User already exists"
29 | });
30 | }
31 | const id = crypto.randomBytes(16).toString("hex") + Date.now();
32 |
33 | // if user does not exist then we create a new user
34 | const newUser = await users.create({
35 | data: {
36 | id: id,
37 | firstName: name.split(" ")[0],
38 | lastName: name.split(" ")[1],
39 | email: email,
40 | password: token
41 | }
42 | });
43 |
44 | res.status(201).send(`User {${req.body.email}} successfully created`);
45 | } catch (error) {
46 | res.status(400).send(error.message);
47 | }
48 | };
49 |
50 | const getUserById = async (req, res) => {
51 | try {
52 | const foundUser = await users.findUnique({
53 | where: {
54 | id: req.params.id
55 | }
56 | });
57 | res.status(200).json({
58 | status: "success",
59 | data: foundUser
60 | });
61 | } catch (error) {
62 | res.status(400).json({
63 | message: error.message
64 | });
65 | }
66 | };
67 |
68 | const getTicketById = async (req, res) => {
69 | try {
70 | const user = await prisma.Customer.findUnique({
71 | where: {
72 | id: req.params.id
73 | },
74 | include: {
75 | Order: {
76 | include: {
77 | Reservation: {
78 | include: {
79 | Ticket: {
80 | include: {
81 | Match: true
82 | }
83 | }
84 | }
85 | }
86 | }
87 | }
88 | }
89 | });
90 | if(!user) {
91 | return res.status(400).json({
92 | message: "User not found"
93 | });
94 | }
95 | if(user.Order.length === 0) {
96 | return res.status(400).json({
97 | message: "User has no orders"
98 | });
99 | }
100 | // return only which have reservation array not empty
101 | const data = user.Order.filter(order => order.Reservation.length > 0);
102 |
103 | res.status(200).json({
104 | status: "success",
105 | data: data
106 |
107 | });
108 |
109 | } catch (error) {
110 | res.status(400).json({
111 | message: error.message
112 | });
113 | }
114 |
115 | }
116 |
117 | const getIdByEmail = async (req, res) => {
118 | try {
119 | const user = await users.findMany({
120 | where: {
121 | email: req.params.email
122 | }
123 | });
124 | if(!user) {
125 | return res.status(400).json({
126 | message: "User not found"
127 | });
128 | }
129 | res.status(200).json({
130 | status: "success",
131 | data: user[0].id
132 | });
133 | } catch (error) {
134 | res.status(400).json({
135 | message: error.message
136 | });
137 | }
138 | }
139 |
140 |
141 | export default { createUser, getUserById, getAllUsers, getTicketById,getIdByEmail };
142 |
--------------------------------------------------------------------------------
/user/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "user",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "app.js",
6 | "scripts": {
7 | "start": "nodemon app.js",
8 | "test":"node --experimental-vm-modules node_modules/jest/bin/jest.js"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "type": "module",
13 | "license": "MIT",
14 | "dependencies": {
15 | "@prisma/client": "^4.7.1",
16 | "cookie-parser": "^1.4.6",
17 | "cors": "^2.8.5",
18 | "dotenv": "^16.0.3",
19 | "express": "^4.18.2",
20 | "http-errors": "^2.0.0",
21 | "jest": "^29.3.1",
22 | "morgan": "^1.10.0",
23 | "prisma": "^4.7.1",
24 | "supertest": "^6.3.3"
25 | },
26 | "devDependencies": {
27 | "nodemon": "^2.0.20"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/user/prisma/schema.prisma:
--------------------------------------------------------------------------------
1 | generator client {
2 | provider = "prisma-client-js"
3 | }
4 |
5 | datasource db {
6 | provider = "postgresql"
7 | url = env("DATABASE_URL")
8 | }
9 |
10 | model Customer {
11 | id String @id
12 | firstName String
13 | lastName String?
14 | phone String?
15 | country String?
16 | email String?
17 | password String?
18 | Order Order[]
19 | }
20 |
21 | model Hold {
22 | id String @id
23 | expiresIn DateTime
24 | Ticket Ticket[]
25 | }
26 |
27 | model Match {
28 | id Int @id @default(autoincrement())
29 | roundNumber Int?
30 | Date DateTime?
31 | location String?
32 | homeTeam String? @default("TBD")
33 | homeTeamContinents String?
34 | awayTeam String? @default("TBD")
35 | awayTeamContinents String?
36 | group String?
37 | homeTeamScore Int?
38 | awayTeamScore Int?
39 | rank Int?
40 | Ticket Ticket[]
41 | }
42 |
43 | model Order {
44 | id String @id
45 | orderDate DateTime @default(now())
46 | requiredDate DateTime?
47 | shipedDate DateTime?
48 | status String @default("shipping")
49 | comments String?
50 | price Int?
51 | customerId String
52 | Customer Customer @relation(fields: [customerId], references: [id])
53 | Reservation Reservation[]
54 | }
55 |
56 | model Reservation {
57 | id String @id
58 | orderId String
59 | ticketsId String
60 | Order Order @relation(fields: [orderId], references: [id])
61 | Ticket Ticket @relation(fields: [ticketsId], references: [id])
62 | }
63 |
64 | model Ticket {
65 | id String @id
66 | price Int
67 | barcode String?
68 | gateOpens String
69 | category String
70 | external Boolean
71 | matchId Int
72 | holdId String?
73 | isHold Boolean @default(false)
74 | isPurchased Boolean @default(false)
75 | Reservation Reservation[]
76 | Hold Hold? @relation(fields: [holdId], references: [id])
77 | Match Match @relation(fields: [matchId], references: [id])
78 | }
79 |
--------------------------------------------------------------------------------
/user/routes/userRoutes.js:
--------------------------------------------------------------------------------
1 | import express from "express";
2 | import userController from "../controllers/userController.js";
3 | const router = express.Router();
4 |
5 | router.route("/").get(userController.getAllUsers);
6 | router.route("/:id").get(userController.getUserById);
7 | router.route("/mail/:email").get(userController.getIdByEmail);
8 | router.route("/create").post(userController.createUser);
9 | router.route("/ticket/:id").get(userController.getTicketById);
10 |
11 | export default router;
12 |
13 |
--------------------------------------------------------------------------------
/user/user.test.js:
--------------------------------------------------------------------------------
1 | import request from "supertest";
2 | import app from "./app";
3 | import controller from "./controllers/userController"
4 |
5 | describe("POST /create ", () => {
6 | it("expecting 201 status code and the created user's email.", async () => {
7 | const res = await request(app).post("/api/users/create").send({
8 | name: "foo bar",
9 | email: "foo@email.com",
10 | password: "07775000"
11 | })
12 | expect(res.statusCode).toBe(201)
13 | // User {anotherEmail@email.com} successfully created
14 | })
15 | })
16 |
17 | describe("GET /", () => {
18 | it("should return a list of all users", async () => {
19 | const res = await request(app).get("/api/users/");
20 | expect(res.statusCode).toBe(200);
21 | expect(res.body).toHaveProperty("data");
22 | })
23 |
24 | it("should return a user with matching provided email", async()=>{
25 | const testEmail = "foo@email.com";
26 | const res = await request(app).get(`/api/users/mail/${testEmail}`);
27 | expect(res.statusCode).toBe(200);
28 | expect(res.body).toHaveProperty('status', 'success');
29 | })
30 |
31 | it("should return 200 status code and return data for a user with tickets", async()=>{
32 | const userID = "bd61f0ee92b23a7c18b4702e4b7af5071673057600567"
33 | const res = await request(app).get(`/api/users/ticket/${userID}`);
34 | expect(res.statusCode).toBe(200);
35 | expect(res.body).toHaveProperty("status","success");
36 | expect(res.body).toHaveProperty("data");
37 |
38 | })
39 |
40 | it("should return 400 status code for user with no tickets", async()=>{
41 | const userID = "dabbb062-398b-45e2-a420-42cb4da59af0"
42 | const res = await request(app).get(`/api/users/ticket/${userID}`);
43 | expect(res.statusCode).toBe(400);
44 | expect(res.body).toHaveProperty( "message","User has no orders");
45 | })
46 | it("should return 200 status code and return user with matching Id", async()=>{
47 | const userID = "dabbb062-398b-45e2-a420-42cb4da59af0";
48 | const expectedFirstname ="Talbert";
49 | const res = await request(app).get(`/api/users/${userID}`);
50 | expect(res.statusCode).toBe(200);
51 | expect(res.body).toHaveProperty("status","success");
52 | expect(res.body).toHaveProperty("data.firstName","Talbert");
53 | })
54 | })
--------------------------------------------------------------------------------
/user/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 2,
3 | "builds": [
4 | {
5 | "src": "app.js",
6 | "use": "@vercel/node"
7 | }
8 | ],
9 | "env": {
10 | "DATABASE_URL":"postgresql://postgres:XNlwLXurO2L5aTTJ@db.ckicjzurvlqafceovnen.supabase.co:5432/postgres"
11 | },
12 | "routes": [{"handle": "filesystem"},
13 | {
14 | "src": "/.*",
15 | "dest": "app.js"
16 | }
17 | ]
18 | }
--------------------------------------------------------------------------------