├── .gitignore ├── README.md ├── admin-ui ├── .dockerignore ├── .env ├── .gitignore ├── Dockerfile ├── README.md ├── configuration │ └── nginx.conf ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt ├── src │ ├── App.scss │ ├── App.tsx │ ├── Components │ │ └── Pagination.tsx │ ├── Login.tsx │ ├── api │ │ ├── listing │ │ │ ├── CreateListingArgs.ts │ │ │ ├── DeleteListingArgs.ts │ │ │ ├── Listing.ts │ │ │ ├── ListingCountArgs.ts │ │ │ ├── ListingCreateInput.ts │ │ │ ├── ListingFindManyArgs.ts │ │ │ ├── ListingFindUniqueArgs.ts │ │ │ ├── ListingListRelationFilter.ts │ │ │ ├── ListingOrderByInput.ts │ │ │ ├── ListingUpdateInput.ts │ │ │ ├── ListingWhereInput.ts │ │ │ ├── ListingWhereUniqueInput.ts │ │ │ ├── TripCreateNestedManyWithoutListingsInput.ts │ │ │ ├── TripUpdateManyWithoutListingsInput.ts │ │ │ ├── UpdateListingArgs.ts │ │ │ ├── WishlistCreateNestedManyWithoutListingsInput.ts │ │ │ └── WishlistUpdateManyWithoutListingsInput.ts │ │ ├── trip │ │ │ ├── CreateTripArgs.ts │ │ │ ├── DeleteTripArgs.ts │ │ │ ├── Trip.ts │ │ │ ├── TripCountArgs.ts │ │ │ ├── TripCreateInput.ts │ │ │ ├── TripFindManyArgs.ts │ │ │ ├── TripFindUniqueArgs.ts │ │ │ ├── TripListRelationFilter.ts │ │ │ ├── TripOrderByInput.ts │ │ │ ├── TripUpdateInput.ts │ │ │ ├── TripWhereInput.ts │ │ │ ├── TripWhereUniqueInput.ts │ │ │ └── UpdateTripArgs.ts │ │ ├── user │ │ │ ├── CreateUserArgs.ts │ │ │ ├── DeleteUserArgs.ts │ │ │ ├── ListingCreateNestedManyWithoutUsersInput.ts │ │ │ ├── ListingUpdateManyWithoutUsersInput.ts │ │ │ ├── TripCreateNestedManyWithoutUsersInput.ts │ │ │ ├── TripUpdateManyWithoutUsersInput.ts │ │ │ ├── UpdateUserArgs.ts │ │ │ ├── User.ts │ │ │ ├── UserCountArgs.ts │ │ │ ├── UserCreateInput.ts │ │ │ ├── UserFindManyArgs.ts │ │ │ ├── UserFindUniqueArgs.ts │ │ │ ├── UserListRelationFilter.ts │ │ │ ├── UserOrderByInput.ts │ │ │ ├── UserUpdateInput.ts │ │ │ ├── UserWhereInput.ts │ │ │ ├── UserWhereUniqueInput.ts │ │ │ ├── WishlistCreateNestedManyWithoutUsersInput.ts │ │ │ └── WishlistUpdateManyWithoutUsersInput.ts │ │ └── wishlist │ │ │ ├── CreateWishlistArgs.ts │ │ │ ├── DeleteWishlistArgs.ts │ │ │ ├── UpdateWishlistArgs.ts │ │ │ ├── Wishlist.ts │ │ │ ├── WishlistCountArgs.ts │ │ │ ├── WishlistCreateInput.ts │ │ │ ├── WishlistFindManyArgs.ts │ │ │ ├── WishlistFindUniqueArgs.ts │ │ │ ├── WishlistListRelationFilter.ts │ │ │ ├── WishlistOrderByInput.ts │ │ │ ├── WishlistUpdateInput.ts │ │ │ ├── WishlistWhereInput.ts │ │ │ └── WishlistWhereUniqueInput.ts │ ├── auth-provider │ │ ├── ra-auth-http.ts │ │ └── ra-auth-jwt.ts │ ├── auth.ts │ ├── constants.ts │ ├── data-provider │ │ └── graphqlDataProvider.ts │ ├── index.css │ ├── index.tsx │ ├── listing │ │ ├── ListingCreate.tsx │ │ ├── ListingEdit.tsx │ │ ├── ListingList.tsx │ │ ├── ListingShow.tsx │ │ └── ListingTitle.ts │ ├── login.scss │ ├── pages │ │ └── Dashboard.tsx │ ├── reportWebVitals.ts │ ├── setupTests.ts │ ├── theme │ │ └── theme.ts │ ├── trip │ │ ├── TripCreate.tsx │ │ ├── TripEdit.tsx │ │ ├── TripList.tsx │ │ ├── TripShow.tsx │ │ └── TripTitle.ts │ ├── types.ts │ ├── user │ │ ├── EnumRoles.ts │ │ ├── RolesOptions.ts │ │ ├── UserCreate.tsx │ │ ├── UserEdit.tsx │ │ ├── UserList.tsx │ │ ├── UserShow.tsx │ │ ├── UserTitle.ts │ │ └── roles.ts │ ├── util │ │ ├── BooleanFilter.ts │ │ ├── BooleanNullableFilter.ts │ │ ├── DateTimeFilter.ts │ │ ├── DateTimeNullableFilter.ts │ │ ├── FloatFilter.ts │ │ ├── FloatNullableFilter.ts │ │ ├── IntFilter.ts │ │ ├── IntNullableFilter.ts │ │ ├── JsonFilter.ts │ │ ├── JsonNullableFilter.ts │ │ ├── MetaQueryPayload.ts │ │ ├── QueryMode.ts │ │ ├── SortOrder.ts │ │ ├── StringFilter.ts │ │ └── StringNullableFilter.ts │ └── wishlist │ │ ├── WishlistCreate.tsx │ │ ├── WishlistEdit.tsx │ │ ├── WishlistList.tsx │ │ ├── WishlistShow.tsx │ │ └── WishlistTitle.ts └── tsconfig.json ├── package-lock.json ├── package.json ├── public ├── .eslintrc.json ├── .gitignore ├── README.md ├── jsconfig.json ├── next.config.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── public │ ├── empty-profile.png │ ├── home.mp4 │ ├── home2.mp4 │ ├── home3.mp4 │ ├── overview1.webp │ ├── overview2.webp │ └── overview3.webp ├── src │ ├── app │ │ ├── favicon.ico │ │ ├── globals.css │ │ ├── layout.jsx │ │ ├── listing │ │ │ └── [listing] │ │ │ │ ├── components │ │ │ │ ├── ListingAmenties.jsx │ │ │ │ ├── ListingMap.jsx │ │ │ │ ├── ListingPhotos.jsx │ │ │ │ └── TripScheduler.jsx │ │ │ │ ├── loading.jsx │ │ │ │ └── page.jsx │ │ ├── loading.jsx │ │ ├── my-listings │ │ │ ├── loading.jsx │ │ │ └── page.jsx │ │ ├── new-listing │ │ │ ├── loading.jsx │ │ │ └── page.jsx │ │ ├── page.jsx │ │ ├── search │ │ │ ├── components │ │ │ │ └── SearchMap.jsx │ │ │ ├── loading.jsx │ │ │ └── page.jsx │ │ ├── trips │ │ │ └── page.jsx │ │ └── wishlist │ │ │ └── page.jsx │ ├── components │ │ ├── SearchScheduler │ │ │ ├── SearchAddress.jsx │ │ │ ├── SearchBeds.jsx │ │ │ └── SearchDates.jsx │ │ ├── auth │ │ │ └── AuthModal.jsx │ │ ├── common │ │ │ ├── Calender.jsx │ │ │ ├── ContextMenu.jsx │ │ │ ├── FormInput.jsx │ │ │ ├── ImageUpload.jsx │ │ │ ├── NavigationEvents.jsx │ │ │ ├── Pin.jsx │ │ │ ├── Schedule.jsx │ │ │ ├── ScheduleBar.jsx │ │ │ └── Spinner.jsx │ │ ├── footer │ │ │ ├── CompactFooter.jsx │ │ │ └── Footer.jsx │ │ ├── listingCard.jsx │ │ ├── navbar │ │ │ └── Navbar.jsx │ │ ├── process │ │ │ ├── Description.jsx │ │ │ ├── FinishSetup.jsx │ │ │ ├── FloorPlan.jsx │ │ │ ├── ListingCreated.jsx │ │ │ ├── ListingPlaceType.jsx │ │ │ ├── ListingTypeSelector.jsx │ │ │ ├── Overview.jsx │ │ │ ├── Photos.jsx │ │ │ ├── PlaceDetails.jsx │ │ │ ├── PlaceLocation.jsx │ │ │ ├── Price.jsx │ │ │ ├── ProcessAmeneties.jsx │ │ │ ├── Review.jsx │ │ │ ├── StepOneStarter.jsx │ │ │ ├── StepThreeStarter.jsx │ │ │ ├── StepTwoStarter.jsx │ │ │ ├── Title.jsx │ │ │ └── geocoder-control.jsx │ │ └── views │ │ │ ├── ListView.jsx │ │ │ ├── MapView.jsx │ │ │ └── ViewSwitchBadge.jsx │ ├── data │ │ ├── Amenities.jsx │ │ └── listingTypes.jsx │ ├── hooks │ │ └── useClickOutside.jsx │ ├── lib │ │ ├── auth.js │ │ ├── http.js │ │ └── lisitng.js │ ├── store │ │ ├── slices │ │ │ ├── AuthSlice.js │ │ │ ├── ListingsSlice.js │ │ │ ├── ProcessSlice.js │ │ │ └── SearchSlice.js │ │ └── store.js │ └── svg │ │ ├── airbnb-logo-short.jsx │ │ ├── airbnb-logo.jsx │ │ ├── ameneties │ │ ├── ac.jsx │ │ ├── bbq.jsx │ │ ├── beach.jsx │ │ ├── carbon-monoxide-alarm.jsx │ │ ├── fire-ext.jsx │ │ ├── fire-pit.jsx │ │ ├── first-aid.jsx │ │ ├── gym.jsx │ │ ├── hot-tub.jsx │ │ ├── indoor-firplace.jsx │ │ ├── kitchen.jsx │ │ ├── lake.jsx │ │ ├── outdoor-dining.jsx │ │ ├── outdoor-shower.jsx │ │ ├── paid-parking.jsx │ │ ├── parking.jsx │ │ ├── patio.jsx │ │ ├── piano.jsx │ │ ├── pool-table.jsx │ │ ├── pool.jsx │ │ ├── ski.jsx │ │ ├── smoke-alarm.jsx │ │ ├── tv.jsx │ │ ├── washing-machine.jsx │ │ ├── wifi.jsx │ │ └── workplace.jsx │ │ ├── daimond.jsx │ │ └── lisitngTypes │ │ ├── barn.jsx │ │ ├── bed-breakfast.jsx │ │ ├── boat.jsx │ │ ├── cabin.jsx │ │ ├── campervan.jsx │ │ ├── casa-particular.jsx │ │ ├── castle.jsx │ │ ├── cave.jsx │ │ ├── container.jsx │ │ ├── cycladic-home.jsx │ │ ├── dammuso.jsx │ │ ├── dome.jsx │ │ ├── earth-home.jsx │ │ ├── farm.jsx │ │ ├── flat.jsx │ │ ├── guest-house.jsx │ │ ├── hotel.jsx │ │ ├── house.jsx │ │ ├── houseboat.jsx │ │ ├── kezhan.jsx │ │ ├── minsu.jsx │ │ ├── riad.jsx │ │ ├── room.jsx │ │ ├── ryokan.jsx │ │ ├── shared-room.jsx │ │ ├── shepherd-hut.jsx │ │ ├── tent.jsx │ │ ├── tiny-home.jsx │ │ ├── tower.jsx │ │ ├── tree-house.jsx │ │ ├── trullo.jsx │ │ ├── windmill.jsx │ │ └── yurt.jsx ├── tailwind.config.js └── yarn.lock └── server ├── .dockerignore ├── .env ├── .gitignore ├── .prettierignore ├── Dockerfile ├── README.md ├── docker-compose.db.yml ├── docker-compose.yml ├── nest-cli.json ├── package-lock.json ├── package.json ├── prisma └── schema.prisma ├── schema.graphql ├── scripts ├── clean.ts ├── customSeed.ts └── seed.ts ├── src ├── app.module.ts ├── auth │ ├── Credentials.ts │ ├── IAuthStrategy.ts │ ├── ITokenService.ts │ ├── LoginArgs.ts │ ├── UserInfo.ts │ ├── abac.util.ts │ ├── acl.module.ts │ ├── auth.controller.ts │ ├── auth.module.ts │ ├── auth.resolver.ts │ ├── auth.service.spec.ts │ ├── auth.service.ts │ ├── base │ │ └── token.service.base.ts │ ├── constants.ts │ ├── defaultAuth.guard.ts │ ├── gqlAC.guard.ts │ ├── gqlDefaultAuth.guard.ts │ ├── gqlUserRoles.decorator.ts │ ├── jwt │ │ ├── base │ │ │ └── jwt.strategy.base.ts │ │ ├── jwt.strategy.ts │ │ ├── jwtAuth.guard.ts │ │ └── jwtSecretFactory.ts │ ├── password.service.spec.ts │ ├── password.service.ts │ ├── token.service.ts │ └── userData.decorator.ts ├── constants.ts ├── decorators │ ├── api-nested-query.decorator.ts │ └── public.decorator.ts ├── errors.ts ├── filters │ └── HttpExceptions.filter.ts ├── grants.json ├── health │ ├── base │ │ ├── health.controller.base.ts │ │ └── health.service.base.ts │ ├── health.controller.ts │ ├── health.module.ts │ └── health.service.ts ├── interceptors │ ├── aclFilterResponse.interceptor.ts │ └── aclValidateRequest.interceptor.ts ├── listing │ ├── base │ │ ├── CreateListingArgs.ts │ │ ├── DeleteListingArgs.ts │ │ ├── Listing.ts │ │ ├── ListingCountArgs.ts │ │ ├── ListingCreateInput.ts │ │ ├── ListingFindManyArgs.ts │ │ ├── ListingFindUniqueArgs.ts │ │ ├── ListingListRelationFilter.ts │ │ ├── ListingOrderByInput.ts │ │ ├── ListingUpdateInput.ts │ │ ├── ListingWhereInput.ts │ │ ├── ListingWhereUniqueInput.ts │ │ ├── TripCreateNestedManyWithoutListingsInput.ts │ │ ├── TripUpdateManyWithoutListingsInput.ts │ │ ├── UpdateListingArgs.ts │ │ ├── WishlistCreateNestedManyWithoutListingsInput.ts │ │ ├── WishlistUpdateManyWithoutListingsInput.ts │ │ ├── listing.controller.base.spec.ts │ │ ├── listing.controller.base.ts │ │ ├── listing.module.base.ts │ │ ├── listing.resolver.base.ts │ │ └── listing.service.base.ts │ ├── listing.controller.ts │ ├── listing.module.ts │ ├── listing.resolver.ts │ └── listing.service.ts ├── main.ts ├── prisma.util.spec.ts ├── prisma.util.ts ├── prisma │ ├── prisma.module.ts │ └── prisma.service.ts ├── providers │ └── secrets │ │ ├── base │ │ ├── secretsManager.service.base.spec.ts │ │ └── secretsManager.service.base.ts │ │ ├── secretsManager.module.ts │ │ └── secretsManager.service.ts ├── serveStaticOptions.service.ts ├── swagger.ts ├── swagger │ ├── favicon.png │ ├── logo-amplication-white.svg │ └── swagger.css ├── tests │ ├── auth │ │ ├── constants.ts │ │ ├── jwt │ │ │ └── jwt.strategy.spec.ts │ │ └── token.service.spec.ts │ └── health │ │ └── health.service.spec.ts ├── trip │ ├── base │ │ ├── CreateTripArgs.ts │ │ ├── DeleteTripArgs.ts │ │ ├── Trip.ts │ │ ├── TripCountArgs.ts │ │ ├── TripCreateInput.ts │ │ ├── TripFindManyArgs.ts │ │ ├── TripFindUniqueArgs.ts │ │ ├── TripListRelationFilter.ts │ │ ├── TripOrderByInput.ts │ │ ├── TripUpdateInput.ts │ │ ├── TripWhereInput.ts │ │ ├── TripWhereUniqueInput.ts │ │ ├── UpdateTripArgs.ts │ │ ├── trip.controller.base.spec.ts │ │ ├── trip.controller.base.ts │ │ ├── trip.module.base.ts │ │ ├── trip.resolver.base.ts │ │ └── trip.service.base.ts │ ├── trip.controller.ts │ ├── trip.module.ts │ ├── trip.resolver.ts │ └── trip.service.ts ├── types.ts ├── user │ ├── base │ │ ├── CreateUserArgs.ts │ │ ├── DeleteUserArgs.ts │ │ ├── ListingCreateNestedManyWithoutUsersInput.ts │ │ ├── ListingUpdateManyWithoutUsersInput.ts │ │ ├── TripCreateNestedManyWithoutUsersInput.ts │ │ ├── TripUpdateManyWithoutUsersInput.ts │ │ ├── UpdateUserArgs.ts │ │ ├── User.ts │ │ ├── UserCountArgs.ts │ │ ├── UserCreateInput.ts │ │ ├── UserFindManyArgs.ts │ │ ├── UserFindUniqueArgs.ts │ │ ├── UserListRelationFilter.ts │ │ ├── UserOrderByInput.ts │ │ ├── UserUpdateInput.ts │ │ ├── UserWhereInput.ts │ │ ├── UserWhereUniqueInput.ts │ │ ├── WishlistCreateNestedManyWithoutUsersInput.ts │ │ ├── WishlistUpdateManyWithoutUsersInput.ts │ │ ├── user.controller.base.spec.ts │ │ ├── user.controller.base.ts │ │ ├── user.module.base.ts │ │ ├── user.resolver.base.ts │ │ └── user.service.base.ts │ ├── user.controller.ts │ ├── user.module.ts │ ├── user.resolver.ts │ └── user.service.ts ├── util │ ├── BooleanFilter.ts │ ├── BooleanNullableFilter.ts │ ├── DateTimeFilter.ts │ ├── DateTimeNullableFilter.ts │ ├── FloatFilter.ts │ ├── FloatNullableFilter.ts │ ├── IntFilter.ts │ ├── IntNullableFilter.ts │ ├── JsonFilter.ts │ ├── JsonNullableFilter.ts │ ├── MetaQueryPayload.ts │ ├── QueryMode.ts │ ├── SortOrder.ts │ ├── StringFilter.ts │ └── StringNullableFilter.ts ├── validators │ ├── index.ts │ ├── is-json-value-validator.spec.ts │ └── is-json-value-validator.ts └── wishlist │ ├── base │ ├── CreateWishlistArgs.ts │ ├── DeleteWishlistArgs.ts │ ├── UpdateWishlistArgs.ts │ ├── Wishlist.ts │ ├── WishlistCountArgs.ts │ ├── WishlistCreateInput.ts │ ├── WishlistFindManyArgs.ts │ ├── WishlistFindUniqueArgs.ts │ ├── WishlistListRelationFilter.ts │ ├── WishlistOrderByInput.ts │ ├── WishlistUpdateInput.ts │ ├── WishlistWhereInput.ts │ ├── WishlistWhereUniqueInput.ts │ ├── wishlist.controller.base.spec.ts │ ├── wishlist.controller.base.ts │ ├── wishlist.module.base.ts │ ├── wishlist.resolver.base.ts │ └── wishlist.service.base.ts │ ├── wishlist.controller.ts │ ├── wishlist.module.ts │ ├── wishlist.resolver.ts │ └── wishlist.service.ts ├── tsconfig.build.json └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | package-lock.json -------------------------------------------------------------------------------- /admin-ui/.dockerignore: -------------------------------------------------------------------------------- 1 | .dockerignore 2 | docker-compose.yml 3 | Dockerfile 4 | build/ 5 | node_modules 6 | .env 7 | .gitignore 8 | -------------------------------------------------------------------------------- /admin-ui/.env: -------------------------------------------------------------------------------- 1 | PORT=3001 2 | REACT_APP_SERVER_URL=http://localhost:3000 -------------------------------------------------------------------------------- /admin-ui/.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 | -------------------------------------------------------------------------------- /admin-ui/Dockerfile: -------------------------------------------------------------------------------- 1 | # multi-stage: base (build) 2 | FROM node:18.13.0-slim AS base 3 | 4 | # instantiate environment variable 5 | ARG REACT_APP_SERVER_URL=http://localhost:3000 6 | 7 | # set the environment variable that points to the server 8 | ENV REACT_APP_SERVER_URL=$REACT_APP_SERVER_URL 9 | 10 | # create directory where the application will be built 11 | WORKDIR /app 12 | 13 | # copy over the dependency manifests, both the package.json 14 | # and the package-lock.json are copied over 15 | COPY package*.json ./ 16 | 17 | # installs packages and their dependencies 18 | RUN npm install 19 | 20 | # copy over the code base 21 | COPY . . 22 | 23 | # create the bundle of the application 24 | RUN npm run build 25 | 26 | # multi-stage: production (runtime) 27 | FROM nginx:1.22-alpine AS production 28 | 29 | # copy over the bundled code from the build stage 30 | COPY --from=base /app/build /usr/share/nginx/html 31 | COPY --from=base /app/configuration/nginx.conf /etc/nginx/conf.d/default.conf 32 | 33 | # create a new process indication file 34 | RUN touch /var/run/nginx.pid 35 | 36 | # change ownership of nginx related directories and files 37 | RUN chown -R nginx:nginx /var/run/nginx.pid \ 38 | /usr/share/nginx/html \ 39 | /var/cache/nginx \ 40 | /var/log/nginx \ 41 | /etc/nginx/conf.d 42 | 43 | # set user to the created non-privileged user 44 | USER nginx 45 | 46 | # expose a specific port on the docker container 47 | ENV PORT=80 48 | EXPOSE ${PORT} 49 | 50 | # start the server using the previously build application 51 | ENTRYPOINT [ "nginx", "-g", "daemon off;" ] 52 | -------------------------------------------------------------------------------- /admin-ui/configuration/nginx.conf: -------------------------------------------------------------------------------- 1 | server_tokens off; 2 | 3 | server { 4 | listen 8080; 5 | server_name localhost; 6 | location / { 7 | root /usr/share/nginx/html; 8 | index index.html index.htm; 9 | try_files $uri /index.html; 10 | } 11 | } -------------------------------------------------------------------------------- /admin-ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@airbnb-server/admin", 3 | "private": true, 4 | "dependencies": { 5 | "@apollo/client": "3.6.9", 6 | "@material-ui/core": "4.12.4", 7 | "graphql": "15.6.1", 8 | "lodash": "4.17.21", 9 | "pluralize": "8.0.0", 10 | "ra-data-graphql-amplication": "0.0.14", 11 | "react": "16.14.0", 12 | "react-admin": "3.19.11", 13 | "react-dom": "16.14.0", 14 | "react-scripts": "5.0.0", 15 | "sass": "^1.39.0", 16 | "web-vitals": "1.1.2" 17 | }, 18 | "overrides": { 19 | "react-scripts": { 20 | "@svgr/webpack": "6.5.1" 21 | } 22 | }, 23 | "scripts": { 24 | "start": "react-scripts start", 25 | "build": "react-scripts build", 26 | "test": "react-scripts test", 27 | "eject": "react-scripts eject", 28 | "package:container": "docker build ." 29 | }, 30 | "eslintConfig": { 31 | "extends": [ 32 | "react-app", 33 | "react-app/jest" 34 | ] 35 | }, 36 | "browserslist": { 37 | "production": [ 38 | ">0.2%", 39 | "not dead", 40 | "not op_mini all" 41 | ], 42 | "development": [ 43 | "last 1 chrome version", 44 | "last 1 firefox version", 45 | "last 1 safari version" 46 | ] 47 | }, 48 | "devDependencies": { 49 | "@testing-library/jest-dom": "5.14.1", 50 | "@testing-library/react": "11.2.7", 51 | "@testing-library/user-event": "13.2.0", 52 | "@types/jest": "26.0.16", 53 | "@types/lodash": "4.14.178", 54 | "@types/node": "12.20.16", 55 | "@types/react": "16.14.11", 56 | "@types/react-dom": "17.0.0", 57 | "type-fest": "0.13.1", 58 | "typescript": "4.2.4" 59 | } 60 | } -------------------------------------------------------------------------------- /admin-ui/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "airbnb-server", 3 | "name": "airbnb-server", 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 | } -------------------------------------------------------------------------------- /admin-ui/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /admin-ui/src/App.scss: -------------------------------------------------------------------------------- 1 | // .App { 2 | // .MuiAppBar-colorSecondary { 3 | // background-color: black; 4 | 5 | // .RaAppBar-menuButton-13 { 6 | // background-color: yellow; 7 | // } 8 | // } 9 | 10 | // .MuiDrawer-paper { 11 | // background-color: red; 12 | 13 | // .MuiListItemIcon-root { 14 | // color: white; 15 | // } 16 | // } 17 | 18 | // .MuiButton-textPrimary { 19 | // background-color: purple; 20 | // margin: 0 0.5rem; 21 | // color: white; 22 | // padding: 0.5rem 1rem; 23 | 24 | // &:hover { 25 | // background-color: blue; 26 | // } 27 | // } 28 | 29 | // .MuiTableRow-head { 30 | // .MuiTableCell-head { 31 | // background-color: black; 32 | // color: white; 33 | // } 34 | 35 | // .MuiTableSortLabel-root { 36 | // &:hover { 37 | // color: red; 38 | 39 | // .MuiTableSortLabel-icon { 40 | // color: red !important; 41 | // } 42 | // } 43 | // .MuiTableSortLabel-icon { 44 | // color: white !important; 45 | // } 46 | // } 47 | // .MuiTableSortLabel-active { 48 | // color: green; 49 | 50 | // .MuiTableSortLabel-icon { 51 | // color: green !important; 52 | // } 53 | // } 54 | // } 55 | 56 | // .MuiFormLabel-root { 57 | // color: magenta; 58 | // } 59 | // } 60 | -------------------------------------------------------------------------------- /admin-ui/src/Components/Pagination.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Pagination as RAPagination, PaginationProps } from "react-admin"; 3 | 4 | const PAGINATION_OPTIONS = [10, 25, 50, 100, 200]; 5 | 6 | const Pagination = (props: PaginationProps) => ( 7 | 8 | ); 9 | 10 | export default Pagination; 11 | -------------------------------------------------------------------------------- /admin-ui/src/api/listing/CreateListingArgs.ts: -------------------------------------------------------------------------------- 1 | import { ListingCreateInput } from "./ListingCreateInput"; 2 | 3 | export type CreateListingArgs = { 4 | data: ListingCreateInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/listing/DeleteListingArgs.ts: -------------------------------------------------------------------------------- 1 | import { ListingWhereUniqueInput } from "./ListingWhereUniqueInput"; 2 | 3 | export type DeleteListingArgs = { 4 | where: ListingWhereUniqueInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/listing/Listing.ts: -------------------------------------------------------------------------------- 1 | import { User } from "../user/User"; 2 | import { JsonValue } from "type-fest"; 3 | import { Trip } from "../trip/Trip"; 4 | import { Wishlist } from "../wishlist/Wishlist"; 5 | 6 | export type Listing = { 7 | createdAt: Date; 8 | description: string; 9 | id: string; 10 | listingCreatedBy?: User; 11 | locationData: JsonValue; 12 | locationType: string; 13 | mapData: JsonValue; 14 | photos: JsonValue; 15 | placeAmeneites: JsonValue; 16 | placeSpace: JsonValue; 17 | placetype: string; 18 | price: number; 19 | title: string; 20 | trips?: Array; 21 | updatedAt: Date; 22 | wishlists?: Array; 23 | }; 24 | -------------------------------------------------------------------------------- /admin-ui/src/api/listing/ListingCountArgs.ts: -------------------------------------------------------------------------------- 1 | import { ListingWhereInput } from "./ListingWhereInput"; 2 | 3 | export type ListingCountArgs = { 4 | where?: ListingWhereInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/listing/ListingCreateInput.ts: -------------------------------------------------------------------------------- 1 | import { UserWhereUniqueInput } from "../user/UserWhereUniqueInput"; 2 | import { InputJsonValue } from "../../types"; 3 | import { TripCreateNestedManyWithoutListingsInput } from "./TripCreateNestedManyWithoutListingsInput"; 4 | import { WishlistCreateNestedManyWithoutListingsInput } from "./WishlistCreateNestedManyWithoutListingsInput"; 5 | 6 | export type ListingCreateInput = { 7 | description: string; 8 | listingCreatedBy: UserWhereUniqueInput; 9 | locationData: InputJsonValue; 10 | locationType: string; 11 | mapData: InputJsonValue; 12 | photos: InputJsonValue; 13 | placeAmeneites: InputJsonValue; 14 | placeSpace: InputJsonValue; 15 | placetype: string; 16 | price: number; 17 | title: string; 18 | trips?: TripCreateNestedManyWithoutListingsInput; 19 | wishlists?: WishlistCreateNestedManyWithoutListingsInput; 20 | }; 21 | -------------------------------------------------------------------------------- /admin-ui/src/api/listing/ListingFindManyArgs.ts: -------------------------------------------------------------------------------- 1 | import { ListingWhereInput } from "./ListingWhereInput"; 2 | import { ListingOrderByInput } from "./ListingOrderByInput"; 3 | 4 | export type ListingFindManyArgs = { 5 | where?: ListingWhereInput; 6 | orderBy?: Array; 7 | skip?: number; 8 | take?: number; 9 | }; 10 | -------------------------------------------------------------------------------- /admin-ui/src/api/listing/ListingFindUniqueArgs.ts: -------------------------------------------------------------------------------- 1 | import { ListingWhereUniqueInput } from "./ListingWhereUniqueInput"; 2 | 3 | export type ListingFindUniqueArgs = { 4 | where: ListingWhereUniqueInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/listing/ListingListRelationFilter.ts: -------------------------------------------------------------------------------- 1 | import { ListingWhereInput } from "./ListingWhereInput"; 2 | 3 | export type ListingListRelationFilter = { 4 | every?: ListingWhereInput; 5 | some?: ListingWhereInput; 6 | none?: ListingWhereInput; 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/api/listing/ListingOrderByInput.ts: -------------------------------------------------------------------------------- 1 | import { SortOrder } from "../../util/SortOrder"; 2 | 3 | export type ListingOrderByInput = { 4 | createdAt?: SortOrder; 5 | description?: SortOrder; 6 | id?: SortOrder; 7 | listingCreatedById?: SortOrder; 8 | locationData?: SortOrder; 9 | locationType?: SortOrder; 10 | mapData?: SortOrder; 11 | photos?: SortOrder; 12 | placeAmeneites?: SortOrder; 13 | placeSpace?: SortOrder; 14 | placetype?: SortOrder; 15 | price?: SortOrder; 16 | title?: SortOrder; 17 | updatedAt?: SortOrder; 18 | }; 19 | -------------------------------------------------------------------------------- /admin-ui/src/api/listing/ListingUpdateInput.ts: -------------------------------------------------------------------------------- 1 | import { UserWhereUniqueInput } from "../user/UserWhereUniqueInput"; 2 | import { InputJsonValue } from "../../types"; 3 | import { TripUpdateManyWithoutListingsInput } from "./TripUpdateManyWithoutListingsInput"; 4 | import { WishlistUpdateManyWithoutListingsInput } from "./WishlistUpdateManyWithoutListingsInput"; 5 | 6 | export type ListingUpdateInput = { 7 | description?: string; 8 | listingCreatedBy?: UserWhereUniqueInput; 9 | locationData?: InputJsonValue; 10 | locationType?: string; 11 | mapData?: InputJsonValue; 12 | photos?: InputJsonValue; 13 | placeAmeneites?: InputJsonValue; 14 | placeSpace?: InputJsonValue; 15 | placetype?: string; 16 | price?: number; 17 | title?: string; 18 | trips?: TripUpdateManyWithoutListingsInput; 19 | wishlists?: WishlistUpdateManyWithoutListingsInput; 20 | }; 21 | -------------------------------------------------------------------------------- /admin-ui/src/api/listing/ListingWhereInput.ts: -------------------------------------------------------------------------------- 1 | import { StringFilter } from "../../util/StringFilter"; 2 | import { UserWhereUniqueInput } from "../user/UserWhereUniqueInput"; 3 | import { JsonFilter } from "../../util/JsonFilter"; 4 | import { IntFilter } from "../../util/IntFilter"; 5 | import { TripListRelationFilter } from "../trip/TripListRelationFilter"; 6 | import { WishlistListRelationFilter } from "../wishlist/WishlistListRelationFilter"; 7 | 8 | export type ListingWhereInput = { 9 | description?: StringFilter; 10 | id?: StringFilter; 11 | listingCreatedBy?: UserWhereUniqueInput; 12 | locationData?: JsonFilter; 13 | locationType?: StringFilter; 14 | mapData?: JsonFilter; 15 | photos?: JsonFilter; 16 | placeAmeneites?: JsonFilter; 17 | placeSpace?: JsonFilter; 18 | placetype?: StringFilter; 19 | price?: IntFilter; 20 | title?: StringFilter; 21 | trips?: TripListRelationFilter; 22 | wishlists?: WishlistListRelationFilter; 23 | }; 24 | -------------------------------------------------------------------------------- /admin-ui/src/api/listing/ListingWhereUniqueInput.ts: -------------------------------------------------------------------------------- 1 | export type ListingWhereUniqueInput = { 2 | id: string; 3 | }; 4 | -------------------------------------------------------------------------------- /admin-ui/src/api/listing/TripCreateNestedManyWithoutListingsInput.ts: -------------------------------------------------------------------------------- 1 | import { TripWhereUniqueInput } from "../trip/TripWhereUniqueInput"; 2 | 3 | export type TripCreateNestedManyWithoutListingsInput = { 4 | connect?: Array; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/listing/TripUpdateManyWithoutListingsInput.ts: -------------------------------------------------------------------------------- 1 | import { TripWhereUniqueInput } from "../trip/TripWhereUniqueInput"; 2 | 3 | export type TripUpdateManyWithoutListingsInput = { 4 | connect?: Array; 5 | disconnect?: Array; 6 | set?: Array; 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/api/listing/UpdateListingArgs.ts: -------------------------------------------------------------------------------- 1 | import { ListingWhereUniqueInput } from "./ListingWhereUniqueInput"; 2 | import { ListingUpdateInput } from "./ListingUpdateInput"; 3 | 4 | export type UpdateListingArgs = { 5 | where: ListingWhereUniqueInput; 6 | data: ListingUpdateInput; 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/api/listing/WishlistCreateNestedManyWithoutListingsInput.ts: -------------------------------------------------------------------------------- 1 | import { WishlistWhereUniqueInput } from "../wishlist/WishlistWhereUniqueInput"; 2 | 3 | export type WishlistCreateNestedManyWithoutListingsInput = { 4 | connect?: Array; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/listing/WishlistUpdateManyWithoutListingsInput.ts: -------------------------------------------------------------------------------- 1 | import { WishlistWhereUniqueInput } from "../wishlist/WishlistWhereUniqueInput"; 2 | 3 | export type WishlistUpdateManyWithoutListingsInput = { 4 | connect?: Array; 5 | disconnect?: Array; 6 | set?: Array; 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/api/trip/CreateTripArgs.ts: -------------------------------------------------------------------------------- 1 | import { TripCreateInput } from "./TripCreateInput"; 2 | 3 | export type CreateTripArgs = { 4 | data: TripCreateInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/trip/DeleteTripArgs.ts: -------------------------------------------------------------------------------- 1 | import { TripWhereUniqueInput } from "./TripWhereUniqueInput"; 2 | 3 | export type DeleteTripArgs = { 4 | where: TripWhereUniqueInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/trip/Trip.ts: -------------------------------------------------------------------------------- 1 | import { Listing } from "../listing/Listing"; 2 | import { JsonValue } from "type-fest"; 3 | import { User } from "../user/User"; 4 | 5 | export type Trip = { 6 | createdAt: Date; 7 | id: string; 8 | listing?: Listing; 9 | tripData: JsonValue; 10 | updatedAt: Date; 11 | user?: User; 12 | }; 13 | -------------------------------------------------------------------------------- /admin-ui/src/api/trip/TripCountArgs.ts: -------------------------------------------------------------------------------- 1 | import { TripWhereInput } from "./TripWhereInput"; 2 | 3 | export type TripCountArgs = { 4 | where?: TripWhereInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/trip/TripCreateInput.ts: -------------------------------------------------------------------------------- 1 | import { ListingWhereUniqueInput } from "../listing/ListingWhereUniqueInput"; 2 | import { InputJsonValue } from "../../types"; 3 | import { UserWhereUniqueInput } from "../user/UserWhereUniqueInput"; 4 | 5 | export type TripCreateInput = { 6 | listing: ListingWhereUniqueInput; 7 | tripData: InputJsonValue; 8 | user: UserWhereUniqueInput; 9 | }; 10 | -------------------------------------------------------------------------------- /admin-ui/src/api/trip/TripFindManyArgs.ts: -------------------------------------------------------------------------------- 1 | import { TripWhereInput } from "./TripWhereInput"; 2 | import { TripOrderByInput } from "./TripOrderByInput"; 3 | 4 | export type TripFindManyArgs = { 5 | where?: TripWhereInput; 6 | orderBy?: Array; 7 | skip?: number; 8 | take?: number; 9 | }; 10 | -------------------------------------------------------------------------------- /admin-ui/src/api/trip/TripFindUniqueArgs.ts: -------------------------------------------------------------------------------- 1 | import { TripWhereUniqueInput } from "./TripWhereUniqueInput"; 2 | 3 | export type TripFindUniqueArgs = { 4 | where: TripWhereUniqueInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/trip/TripListRelationFilter.ts: -------------------------------------------------------------------------------- 1 | import { TripWhereInput } from "./TripWhereInput"; 2 | 3 | export type TripListRelationFilter = { 4 | every?: TripWhereInput; 5 | some?: TripWhereInput; 6 | none?: TripWhereInput; 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/api/trip/TripOrderByInput.ts: -------------------------------------------------------------------------------- 1 | import { SortOrder } from "../../util/SortOrder"; 2 | 3 | export type TripOrderByInput = { 4 | createdAt?: SortOrder; 5 | id?: SortOrder; 6 | listingId?: SortOrder; 7 | tripData?: SortOrder; 8 | updatedAt?: SortOrder; 9 | userId?: SortOrder; 10 | }; 11 | -------------------------------------------------------------------------------- /admin-ui/src/api/trip/TripUpdateInput.ts: -------------------------------------------------------------------------------- 1 | import { ListingWhereUniqueInput } from "../listing/ListingWhereUniqueInput"; 2 | import { InputJsonValue } from "../../types"; 3 | import { UserWhereUniqueInput } from "../user/UserWhereUniqueInput"; 4 | 5 | export type TripUpdateInput = { 6 | listing?: ListingWhereUniqueInput; 7 | tripData?: InputJsonValue; 8 | user?: UserWhereUniqueInput; 9 | }; 10 | -------------------------------------------------------------------------------- /admin-ui/src/api/trip/TripWhereInput.ts: -------------------------------------------------------------------------------- 1 | import { StringFilter } from "../../util/StringFilter"; 2 | import { ListingWhereUniqueInput } from "../listing/ListingWhereUniqueInput"; 3 | import { JsonFilter } from "../../util/JsonFilter"; 4 | import { UserWhereUniqueInput } from "../user/UserWhereUniqueInput"; 5 | 6 | export type TripWhereInput = { 7 | id?: StringFilter; 8 | listing?: ListingWhereUniqueInput; 9 | tripData?: JsonFilter; 10 | user?: UserWhereUniqueInput; 11 | }; 12 | -------------------------------------------------------------------------------- /admin-ui/src/api/trip/TripWhereUniqueInput.ts: -------------------------------------------------------------------------------- 1 | export type TripWhereUniqueInput = { 2 | id: string; 3 | }; 4 | -------------------------------------------------------------------------------- /admin-ui/src/api/trip/UpdateTripArgs.ts: -------------------------------------------------------------------------------- 1 | import { TripWhereUniqueInput } from "./TripWhereUniqueInput"; 2 | import { TripUpdateInput } from "./TripUpdateInput"; 3 | 4 | export type UpdateTripArgs = { 5 | where: TripWhereUniqueInput; 6 | data: TripUpdateInput; 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/CreateUserArgs.ts: -------------------------------------------------------------------------------- 1 | import { UserCreateInput } from "./UserCreateInput"; 2 | 3 | export type CreateUserArgs = { 4 | data: UserCreateInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/DeleteUserArgs.ts: -------------------------------------------------------------------------------- 1 | import { UserWhereUniqueInput } from "./UserWhereUniqueInput"; 2 | 3 | export type DeleteUserArgs = { 4 | where: UserWhereUniqueInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/ListingCreateNestedManyWithoutUsersInput.ts: -------------------------------------------------------------------------------- 1 | import { ListingWhereUniqueInput } from "../listing/ListingWhereUniqueInput"; 2 | 3 | export type ListingCreateNestedManyWithoutUsersInput = { 4 | connect?: Array; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/ListingUpdateManyWithoutUsersInput.ts: -------------------------------------------------------------------------------- 1 | import { ListingWhereUniqueInput } from "../listing/ListingWhereUniqueInput"; 2 | 3 | export type ListingUpdateManyWithoutUsersInput = { 4 | connect?: Array; 5 | disconnect?: Array; 6 | set?: Array; 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/TripCreateNestedManyWithoutUsersInput.ts: -------------------------------------------------------------------------------- 1 | import { TripWhereUniqueInput } from "../trip/TripWhereUniqueInput"; 2 | 3 | export type TripCreateNestedManyWithoutUsersInput = { 4 | connect?: Array; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/TripUpdateManyWithoutUsersInput.ts: -------------------------------------------------------------------------------- 1 | import { TripWhereUniqueInput } from "../trip/TripWhereUniqueInput"; 2 | 3 | export type TripUpdateManyWithoutUsersInput = { 4 | connect?: Array; 5 | disconnect?: Array; 6 | set?: Array; 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/UpdateUserArgs.ts: -------------------------------------------------------------------------------- 1 | import { UserWhereUniqueInput } from "./UserWhereUniqueInput"; 2 | import { UserUpdateInput } from "./UserUpdateInput"; 3 | 4 | export type UpdateUserArgs = { 5 | where: UserWhereUniqueInput; 6 | data: UserUpdateInput; 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/User.ts: -------------------------------------------------------------------------------- 1 | import { Listing } from "../listing/Listing"; 2 | import { JsonValue } from "type-fest"; 3 | import { Trip } from "../trip/Trip"; 4 | import { Wishlist } from "../wishlist/Wishlist"; 5 | 6 | export type User = { 7 | createdAt: Date; 8 | firstName: string | null; 9 | id: string; 10 | lastName: string | null; 11 | listings?: Array; 12 | roles: JsonValue; 13 | trips?: Array; 14 | updatedAt: Date; 15 | username: string; 16 | wishlists?: Array; 17 | }; 18 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/UserCountArgs.ts: -------------------------------------------------------------------------------- 1 | import { UserWhereInput } from "./UserWhereInput"; 2 | 3 | export type UserCountArgs = { 4 | where?: UserWhereInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/UserCreateInput.ts: -------------------------------------------------------------------------------- 1 | import { ListingCreateNestedManyWithoutUsersInput } from "./ListingCreateNestedManyWithoutUsersInput"; 2 | import { InputJsonValue } from "../../types"; 3 | import { TripCreateNestedManyWithoutUsersInput } from "./TripCreateNestedManyWithoutUsersInput"; 4 | import { WishlistCreateNestedManyWithoutUsersInput } from "./WishlistCreateNestedManyWithoutUsersInput"; 5 | 6 | export type UserCreateInput = { 7 | firstName?: string | null; 8 | lastName?: string | null; 9 | listings?: ListingCreateNestedManyWithoutUsersInput; 10 | password: string; 11 | roles: InputJsonValue; 12 | trips?: TripCreateNestedManyWithoutUsersInput; 13 | username: string; 14 | wishlists?: WishlistCreateNestedManyWithoutUsersInput; 15 | }; 16 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/UserFindManyArgs.ts: -------------------------------------------------------------------------------- 1 | import { UserWhereInput } from "./UserWhereInput"; 2 | import { UserOrderByInput } from "./UserOrderByInput"; 3 | 4 | export type UserFindManyArgs = { 5 | where?: UserWhereInput; 6 | orderBy?: Array; 7 | skip?: number; 8 | take?: number; 9 | }; 10 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/UserFindUniqueArgs.ts: -------------------------------------------------------------------------------- 1 | import { UserWhereUniqueInput } from "./UserWhereUniqueInput"; 2 | 3 | export type UserFindUniqueArgs = { 4 | where: UserWhereUniqueInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/UserListRelationFilter.ts: -------------------------------------------------------------------------------- 1 | import { UserWhereInput } from "./UserWhereInput"; 2 | 3 | export type UserListRelationFilter = { 4 | every?: UserWhereInput; 5 | some?: UserWhereInput; 6 | none?: UserWhereInput; 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/UserOrderByInput.ts: -------------------------------------------------------------------------------- 1 | import { SortOrder } from "../../util/SortOrder"; 2 | 3 | export type UserOrderByInput = { 4 | createdAt?: SortOrder; 5 | firstName?: SortOrder; 6 | id?: SortOrder; 7 | lastName?: SortOrder; 8 | password?: SortOrder; 9 | roles?: SortOrder; 10 | updatedAt?: SortOrder; 11 | username?: SortOrder; 12 | }; 13 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/UserUpdateInput.ts: -------------------------------------------------------------------------------- 1 | import { ListingUpdateManyWithoutUsersInput } from "./ListingUpdateManyWithoutUsersInput"; 2 | import { InputJsonValue } from "../../types"; 3 | import { TripUpdateManyWithoutUsersInput } from "./TripUpdateManyWithoutUsersInput"; 4 | import { WishlistUpdateManyWithoutUsersInput } from "./WishlistUpdateManyWithoutUsersInput"; 5 | 6 | export type UserUpdateInput = { 7 | firstName?: string | null; 8 | lastName?: string | null; 9 | listings?: ListingUpdateManyWithoutUsersInput; 10 | password?: string; 11 | roles?: InputJsonValue; 12 | trips?: TripUpdateManyWithoutUsersInput; 13 | username?: string; 14 | wishlists?: WishlistUpdateManyWithoutUsersInput; 15 | }; 16 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/UserWhereInput.ts: -------------------------------------------------------------------------------- 1 | import { StringNullableFilter } from "../../util/StringNullableFilter"; 2 | import { StringFilter } from "../../util/StringFilter"; 3 | import { ListingListRelationFilter } from "../listing/ListingListRelationFilter"; 4 | import { TripListRelationFilter } from "../trip/TripListRelationFilter"; 5 | import { WishlistListRelationFilter } from "../wishlist/WishlistListRelationFilter"; 6 | 7 | export type UserWhereInput = { 8 | firstName?: StringNullableFilter; 9 | id?: StringFilter; 10 | lastName?: StringNullableFilter; 11 | listings?: ListingListRelationFilter; 12 | trips?: TripListRelationFilter; 13 | username?: StringFilter; 14 | wishlists?: WishlistListRelationFilter; 15 | }; 16 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/UserWhereUniqueInput.ts: -------------------------------------------------------------------------------- 1 | export type UserWhereUniqueInput = { 2 | id: string; 3 | }; 4 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/WishlistCreateNestedManyWithoutUsersInput.ts: -------------------------------------------------------------------------------- 1 | import { WishlistWhereUniqueInput } from "../wishlist/WishlistWhereUniqueInput"; 2 | 3 | export type WishlistCreateNestedManyWithoutUsersInput = { 4 | connect?: Array; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/WishlistUpdateManyWithoutUsersInput.ts: -------------------------------------------------------------------------------- 1 | import { WishlistWhereUniqueInput } from "../wishlist/WishlistWhereUniqueInput"; 2 | 3 | export type WishlistUpdateManyWithoutUsersInput = { 4 | connect?: Array; 5 | disconnect?: Array; 6 | set?: Array; 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/api/wishlist/CreateWishlistArgs.ts: -------------------------------------------------------------------------------- 1 | import { WishlistCreateInput } from "./WishlistCreateInput"; 2 | 3 | export type CreateWishlistArgs = { 4 | data: WishlistCreateInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/wishlist/DeleteWishlistArgs.ts: -------------------------------------------------------------------------------- 1 | import { WishlistWhereUniqueInput } from "./WishlistWhereUniqueInput"; 2 | 3 | export type DeleteWishlistArgs = { 4 | where: WishlistWhereUniqueInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/wishlist/UpdateWishlistArgs.ts: -------------------------------------------------------------------------------- 1 | import { WishlistWhereUniqueInput } from "./WishlistWhereUniqueInput"; 2 | import { WishlistUpdateInput } from "./WishlistUpdateInput"; 3 | 4 | export type UpdateWishlistArgs = { 5 | where: WishlistWhereUniqueInput; 6 | data: WishlistUpdateInput; 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/api/wishlist/Wishlist.ts: -------------------------------------------------------------------------------- 1 | import { Listing } from "../listing/Listing"; 2 | import { User } from "../user/User"; 3 | 4 | export type Wishlist = { 5 | createdAt: Date; 6 | id: string; 7 | listing?: Listing; 8 | updatedAt: Date; 9 | user?: User; 10 | }; 11 | -------------------------------------------------------------------------------- /admin-ui/src/api/wishlist/WishlistCountArgs.ts: -------------------------------------------------------------------------------- 1 | import { WishlistWhereInput } from "./WishlistWhereInput"; 2 | 3 | export type WishlistCountArgs = { 4 | where?: WishlistWhereInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/wishlist/WishlistCreateInput.ts: -------------------------------------------------------------------------------- 1 | import { ListingWhereUniqueInput } from "../listing/ListingWhereUniqueInput"; 2 | import { UserWhereUniqueInput } from "../user/UserWhereUniqueInput"; 3 | 4 | export type WishlistCreateInput = { 5 | listing: ListingWhereUniqueInput; 6 | user: UserWhereUniqueInput; 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/api/wishlist/WishlistFindManyArgs.ts: -------------------------------------------------------------------------------- 1 | import { WishlistWhereInput } from "./WishlistWhereInput"; 2 | import { WishlistOrderByInput } from "./WishlistOrderByInput"; 3 | 4 | export type WishlistFindManyArgs = { 5 | where?: WishlistWhereInput; 6 | orderBy?: Array; 7 | skip?: number; 8 | take?: number; 9 | }; 10 | -------------------------------------------------------------------------------- /admin-ui/src/api/wishlist/WishlistFindUniqueArgs.ts: -------------------------------------------------------------------------------- 1 | import { WishlistWhereUniqueInput } from "./WishlistWhereUniqueInput"; 2 | 3 | export type WishlistFindUniqueArgs = { 4 | where: WishlistWhereUniqueInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/wishlist/WishlistListRelationFilter.ts: -------------------------------------------------------------------------------- 1 | import { WishlistWhereInput } from "./WishlistWhereInput"; 2 | 3 | export type WishlistListRelationFilter = { 4 | every?: WishlistWhereInput; 5 | some?: WishlistWhereInput; 6 | none?: WishlistWhereInput; 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/api/wishlist/WishlistOrderByInput.ts: -------------------------------------------------------------------------------- 1 | import { SortOrder } from "../../util/SortOrder"; 2 | 3 | export type WishlistOrderByInput = { 4 | createdAt?: SortOrder; 5 | id?: SortOrder; 6 | listingId?: SortOrder; 7 | updatedAt?: SortOrder; 8 | userId?: SortOrder; 9 | }; 10 | -------------------------------------------------------------------------------- /admin-ui/src/api/wishlist/WishlistUpdateInput.ts: -------------------------------------------------------------------------------- 1 | import { ListingWhereUniqueInput } from "../listing/ListingWhereUniqueInput"; 2 | import { UserWhereUniqueInput } from "../user/UserWhereUniqueInput"; 3 | 4 | export type WishlistUpdateInput = { 5 | listing?: ListingWhereUniqueInput; 6 | user?: UserWhereUniqueInput; 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/api/wishlist/WishlistWhereInput.ts: -------------------------------------------------------------------------------- 1 | import { StringFilter } from "../../util/StringFilter"; 2 | import { ListingWhereUniqueInput } from "../listing/ListingWhereUniqueInput"; 3 | import { UserWhereUniqueInput } from "../user/UserWhereUniqueInput"; 4 | 5 | export type WishlistWhereInput = { 6 | id?: StringFilter; 7 | listing?: ListingWhereUniqueInput; 8 | user?: UserWhereUniqueInput; 9 | }; 10 | -------------------------------------------------------------------------------- /admin-ui/src/api/wishlist/WishlistWhereUniqueInput.ts: -------------------------------------------------------------------------------- 1 | export type WishlistWhereUniqueInput = { 2 | id: string; 3 | }; 4 | -------------------------------------------------------------------------------- /admin-ui/src/auth.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from "events"; 2 | import { CREDENTIALS_LOCAL_STORAGE_ITEM } from "./constants"; 3 | import { Credentials } from "./types"; 4 | 5 | const eventEmitter = new EventEmitter(); 6 | 7 | export function isAuthenticated(): boolean { 8 | return Boolean(getCredentials()); 9 | } 10 | 11 | export function listen(listener: (authenticated: boolean) => void): void { 12 | eventEmitter.on("change", () => { 13 | listener(isAuthenticated()); 14 | }); 15 | } 16 | 17 | export function setCredentials(credentials: Credentials) { 18 | localStorage.setItem( 19 | CREDENTIALS_LOCAL_STORAGE_ITEM, 20 | JSON.stringify(credentials) 21 | ); 22 | } 23 | 24 | export function getCredentials(): Credentials | null { 25 | const raw = localStorage.getItem(CREDENTIALS_LOCAL_STORAGE_ITEM); 26 | if (raw === null) { 27 | return null; 28 | } 29 | return JSON.parse(raw); 30 | } 31 | 32 | export function removeCredentials(): void { 33 | localStorage.removeItem(CREDENTIALS_LOCAL_STORAGE_ITEM); 34 | } 35 | -------------------------------------------------------------------------------- /admin-ui/src/constants.ts: -------------------------------------------------------------------------------- 1 | export const CREDENTIALS_LOCAL_STORAGE_ITEM = "credentials"; 2 | export const USER_DATA_LOCAL_STORAGE_ITEM = "userData"; 3 | -------------------------------------------------------------------------------- /admin-ui/src/data-provider/graphqlDataProvider.ts: -------------------------------------------------------------------------------- 1 | import buildGraphQLProvider from "ra-data-graphql-amplication"; 2 | import { ApolloClient, InMemoryCache, createHttpLink } from "@apollo/client"; 3 | import { setContext } from "@apollo/client/link/context"; 4 | import { CREDENTIALS_LOCAL_STORAGE_ITEM } from "../constants"; 5 | 6 | const httpLink = createHttpLink({ 7 | uri: `${process.env.REACT_APP_SERVER_URL}/graphql`, 8 | }); 9 | 10 | // eslint-disable-next-line @typescript-eslint/naming-convention 11 | const authLink = setContext((_, { headers }) => { 12 | const token = localStorage.getItem(CREDENTIALS_LOCAL_STORAGE_ITEM); 13 | return { 14 | headers: { 15 | ...headers, 16 | authorization: token ? token : "", 17 | }, 18 | }; 19 | }); 20 | 21 | export const apolloClient = new ApolloClient({ 22 | cache: new InMemoryCache(), 23 | link: authLink.concat(httpLink), 24 | }); 25 | 26 | export default buildGraphQLProvider({ 27 | client: apolloClient, 28 | }); 29 | -------------------------------------------------------------------------------- /admin-ui/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 4 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | #root { 11 | height: 100vh; 12 | } 13 | 14 | code { 15 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 16 | monospace; 17 | } 18 | 19 | .amp-breadcrumbs { 20 | padding: var(--default-spacing); 21 | } 22 | 23 | .entity-id { 24 | color: var(--primary); 25 | text-decoration: underline; 26 | } 27 | -------------------------------------------------------------------------------- /admin-ui/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import "./index.css"; 4 | // @ts-ignore 5 | // eslint-disable-next-line import/no-unresolved 6 | import App from "./App"; 7 | import reportWebVitals from "./reportWebVitals"; 8 | 9 | ReactDOM.render( 10 | 11 | 12 | , 13 | document.getElementById("root") 14 | ); 15 | 16 | // If you want to start measuring performance in your app, pass a function 17 | // to log results (for example: reportWebVitals(console.log)) 18 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 19 | reportWebVitals(); 20 | -------------------------------------------------------------------------------- /admin-ui/src/listing/ListingTitle.ts: -------------------------------------------------------------------------------- 1 | import { Listing as TListing } from "../api/listing/Listing"; 2 | 3 | export const LISTING_TITLE_FIELD = "title"; 4 | 5 | export const ListingTitle = (record: TListing): string => { 6 | return record.title || String(record.id); 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/pages/Dashboard.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import Card from "@material-ui/core/Card"; 3 | import CardContent from "@material-ui/core/CardContent"; 4 | import { Title } from "react-admin"; 5 | const Dashboard = () => ( 6 | 7 | 8 | <CardContent>Welcome</CardContent> 9 | </Card> 10 | ); 11 | 12 | export default Dashboard; 13 | -------------------------------------------------------------------------------- /admin-ui/src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from "web-vitals"; 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler): void => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | void import("web-vitals").then( 6 | ({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 7 | getCLS(onPerfEntry); 8 | getFID(onPerfEntry); 9 | getFCP(onPerfEntry); 10 | getLCP(onPerfEntry); 11 | getTTFB(onPerfEntry); 12 | } 13 | ); 14 | } 15 | }; 16 | 17 | export default reportWebVitals; 18 | -------------------------------------------------------------------------------- /admin-ui/src/setupTests.ts: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /admin-ui/src/theme/theme.ts: -------------------------------------------------------------------------------- 1 | import { defaultTheme } from "react-admin"; 2 | import { createTheme, ThemeOptions } from "@material-ui/core/styles"; 3 | import { merge } from "lodash"; 4 | import createPalette from "@material-ui/core/styles/createPalette"; 5 | 6 | const palette = createPalette( 7 | merge({}, defaultTheme.palette, { 8 | primary: { 9 | main: "#20a4f3", 10 | }, 11 | secondary: { 12 | main: "#7950ed", 13 | }, 14 | error: { 15 | main: "#e93c51", 16 | }, 17 | warning: { 18 | main: "#f6aa50", 19 | }, 20 | info: { 21 | main: "#144bc1", 22 | }, 23 | success: { 24 | main: "#31c587", 25 | }, 26 | }) 27 | ); 28 | 29 | const themeOptions: ThemeOptions = { 30 | palette, 31 | }; 32 | 33 | export const theme = createTheme(merge({}, defaultTheme, themeOptions)); 34 | -------------------------------------------------------------------------------- /admin-ui/src/trip/TripCreate.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { 3 | Create, 4 | SimpleForm, 5 | CreateProps, 6 | ReferenceInput, 7 | SelectInput, 8 | } from "react-admin"; 9 | import { ListingTitle } from "../listing/ListingTitle"; 10 | import { UserTitle } from "../user/UserTitle"; 11 | 12 | export const TripCreate = (props: CreateProps): React.ReactElement => { 13 | return ( 14 | <Create {...props}> 15 | <SimpleForm> 16 | <ReferenceInput source="listing.id" reference="Listing" label="listing"> 17 | <SelectInput optionText={ListingTitle} /> 18 | </ReferenceInput> 19 | <div /> 20 | <ReferenceInput source="user.id" reference="User" label="user"> 21 | <SelectInput optionText={UserTitle} /> 22 | </ReferenceInput> 23 | </SimpleForm> 24 | </Create> 25 | ); 26 | }; 27 | -------------------------------------------------------------------------------- /admin-ui/src/trip/TripEdit.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { 3 | Edit, 4 | SimpleForm, 5 | EditProps, 6 | ReferenceInput, 7 | SelectInput, 8 | } from "react-admin"; 9 | import { ListingTitle } from "../listing/ListingTitle"; 10 | import { UserTitle } from "../user/UserTitle"; 11 | 12 | export const TripEdit = (props: EditProps): React.ReactElement => { 13 | return ( 14 | <Edit {...props}> 15 | <SimpleForm> 16 | <ReferenceInput source="listing.id" reference="Listing" label="listing"> 17 | <SelectInput optionText={ListingTitle} /> 18 | </ReferenceInput> 19 | <div /> 20 | <ReferenceInput source="user.id" reference="User" label="user"> 21 | <SelectInput optionText={UserTitle} /> 22 | </ReferenceInput> 23 | </SimpleForm> 24 | </Edit> 25 | ); 26 | }; 27 | -------------------------------------------------------------------------------- /admin-ui/src/trip/TripList.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { 3 | List, 4 | Datagrid, 5 | ListProps, 6 | DateField, 7 | TextField, 8 | ReferenceField, 9 | } from "react-admin"; 10 | import Pagination from "../Components/Pagination"; 11 | import { LISTING_TITLE_FIELD } from "../listing/ListingTitle"; 12 | import { USER_TITLE_FIELD } from "../user/UserTitle"; 13 | 14 | export const TripList = (props: ListProps): React.ReactElement => { 15 | return ( 16 | <List 17 | {...props} 18 | bulkActionButtons={false} 19 | title={"trips"} 20 | perPage={50} 21 | pagination={<Pagination />} 22 | > 23 | <Datagrid rowClick="show"> 24 | <DateField source="createdAt" label="Created At" /> 25 | <TextField label="ID" source="id" /> 26 | <ReferenceField label="listing" source="listing.id" reference="Listing"> 27 | <TextField source={LISTING_TITLE_FIELD} /> 28 | </ReferenceField> 29 | <TextField label="tripData" source="tripData" /> 30 | <DateField source="updatedAt" label="Updated At" /> 31 | <ReferenceField label="user" source="user.id" reference="User"> 32 | <TextField source={USER_TITLE_FIELD} /> 33 | </ReferenceField> 34 | </Datagrid> 35 | </List> 36 | ); 37 | }; 38 | -------------------------------------------------------------------------------- /admin-ui/src/trip/TripShow.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { 3 | Show, 4 | SimpleShowLayout, 5 | ShowProps, 6 | DateField, 7 | TextField, 8 | ReferenceField, 9 | } from "react-admin"; 10 | import { LISTING_TITLE_FIELD } from "../listing/ListingTitle"; 11 | import { USER_TITLE_FIELD } from "../user/UserTitle"; 12 | 13 | export const TripShow = (props: ShowProps): React.ReactElement => { 14 | return ( 15 | <Show {...props}> 16 | <SimpleShowLayout> 17 | <DateField source="createdAt" label="Created At" /> 18 | <TextField label="ID" source="id" /> 19 | <ReferenceField label="listing" source="listing.id" reference="Listing"> 20 | <TextField source={LISTING_TITLE_FIELD} /> 21 | </ReferenceField> 22 | <TextField label="tripData" source="tripData" /> 23 | <DateField source="updatedAt" label="Updated At" /> 24 | <ReferenceField label="user" source="user.id" reference="User"> 25 | <TextField source={USER_TITLE_FIELD} /> 26 | </ReferenceField> 27 | </SimpleShowLayout> 28 | </Show> 29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /admin-ui/src/trip/TripTitle.ts: -------------------------------------------------------------------------------- 1 | import { Trip as TTrip } from "../api/trip/Trip"; 2 | 3 | export const TRIP_TITLE_FIELD = "id"; 4 | 5 | export const TripTitle = (record: TTrip): string => { 6 | return record.id || String(record.id); 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/types.ts: -------------------------------------------------------------------------------- 1 | import { JsonValue } from "type-fest"; 2 | 3 | export type Credentials = { 4 | username: string; 5 | password: string; 6 | }; 7 | export type LoginMutateResult = { 8 | login: { 9 | username: string; 10 | accessToken: string; 11 | }; 12 | }; 13 | export type InputJsonValue = Omit<JsonValue, "null">; 14 | -------------------------------------------------------------------------------- /admin-ui/src/user/EnumRoles.ts: -------------------------------------------------------------------------------- 1 | export enum EnumRoles { 2 | AirbnbUser = "airbnbUser", 3 | User = "user", 4 | } 5 | -------------------------------------------------------------------------------- /admin-ui/src/user/RolesOptions.ts: -------------------------------------------------------------------------------- 1 | //@ts-ignore 2 | import { ROLES } from "./roles"; 3 | 4 | declare interface Role { 5 | name: string; 6 | displayName: string; 7 | } 8 | 9 | export const ROLES_OPTIONS = ROLES.map((role: Role) => ({ 10 | value: role.name, 11 | label: role.displayName, 12 | })); 13 | -------------------------------------------------------------------------------- /admin-ui/src/user/UserList.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { List, Datagrid, ListProps, DateField, TextField } from "react-admin"; 3 | import Pagination from "../Components/Pagination"; 4 | 5 | export const UserList = (props: ListProps): React.ReactElement => { 6 | return ( 7 | <List 8 | {...props} 9 | bulkActionButtons={false} 10 | title={"Users"} 11 | perPage={50} 12 | pagination={<Pagination />} 13 | > 14 | <Datagrid rowClick="show"> 15 | <DateField source="createdAt" label="Created At" /> 16 | <TextField label="First Name" source="firstName" /> 17 | <TextField label="ID" source="id" /> 18 | <TextField label="Last Name" source="lastName" /> 19 | <TextField label="Roles" source="roles" /> 20 | <DateField source="updatedAt" label="Updated At" /> 21 | <TextField label="Username" source="username" /> 22 | </Datagrid> 23 | </List> 24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /admin-ui/src/user/UserTitle.ts: -------------------------------------------------------------------------------- 1 | import { User as TUser } from "../api/user/User"; 2 | 3 | export const USER_TITLE_FIELD = "firstName"; 4 | 5 | export const UserTitle = (record: TUser): string => { 6 | return record.firstName || String(record.id); 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/user/roles.ts: -------------------------------------------------------------------------------- 1 | export const ROLES = [ 2 | { 3 | name: "airbnbUser", 4 | displayName: "airbnb-user", 5 | }, 6 | { 7 | name: "user", 8 | displayName: "User", 9 | }, 10 | ]; 11 | -------------------------------------------------------------------------------- /admin-ui/src/util/BooleanFilter.ts: -------------------------------------------------------------------------------- 1 | export class BooleanFilter { 2 | equals?: boolean; 3 | not?: boolean; 4 | } 5 | -------------------------------------------------------------------------------- /admin-ui/src/util/BooleanNullableFilter.ts: -------------------------------------------------------------------------------- 1 | export class BooleanNullableFilter { 2 | equals?: boolean | null; 3 | not?: boolean | null; 4 | } 5 | -------------------------------------------------------------------------------- /admin-ui/src/util/DateTimeFilter.ts: -------------------------------------------------------------------------------- 1 | export class DateTimeFilter { 2 | equals?: Date; 3 | not?: Date; 4 | in?: Date[]; 5 | notIn?: Date[]; 6 | lt?: Date; 7 | lte?: Date; 8 | gt?: Date; 9 | gte?: Date; 10 | } 11 | -------------------------------------------------------------------------------- /admin-ui/src/util/DateTimeNullableFilter.ts: -------------------------------------------------------------------------------- 1 | export class DateTimeNullableFilter { 2 | equals?: Date | null; 3 | in?: Date[] | null; 4 | notIn?: Date[] | null; 5 | lt?: Date; 6 | lte?: Date; 7 | gt?: Date; 8 | gte?: Date; 9 | not?: Date; 10 | } 11 | -------------------------------------------------------------------------------- /admin-ui/src/util/FloatFilter.ts: -------------------------------------------------------------------------------- 1 | export class FloatFilter { 2 | equals?: number; 3 | in?: number[]; 4 | notIn?: number[]; 5 | lt?: number; 6 | lte?: number; 7 | gt?: number; 8 | gte?: number; 9 | not?: number; 10 | } 11 | -------------------------------------------------------------------------------- /admin-ui/src/util/FloatNullableFilter.ts: -------------------------------------------------------------------------------- 1 | export class FloatNullableFilter { 2 | equals?: number | null; 3 | in?: number[] | null; 4 | notIn?: number[] | null; 5 | lt?: number; 6 | lte?: number; 7 | gt?: number; 8 | gte?: number; 9 | not?: number; 10 | } 11 | -------------------------------------------------------------------------------- /admin-ui/src/util/IntFilter.ts: -------------------------------------------------------------------------------- 1 | export class IntFilter { 2 | equals?: number; 3 | in?: number[]; 4 | notIn?: number[]; 5 | lt?: number; 6 | lte?: number; 7 | gt?: number; 8 | gte?: number; 9 | not?: number; 10 | } 11 | -------------------------------------------------------------------------------- /admin-ui/src/util/IntNullableFilter.ts: -------------------------------------------------------------------------------- 1 | export class IntNullableFilter { 2 | equals?: number | null; 3 | in?: number[] | null; 4 | notIn?: number[] | null; 5 | lt?: number; 6 | lte?: number; 7 | gt?: number; 8 | gte?: number; 9 | not?: number; 10 | } 11 | -------------------------------------------------------------------------------- /admin-ui/src/util/JsonFilter.ts: -------------------------------------------------------------------------------- 1 | import { InputJsonValue } from "../types"; 2 | export class JsonFilter { 3 | equals?: InputJsonValue; 4 | not?: InputJsonValue; 5 | } 6 | -------------------------------------------------------------------------------- /admin-ui/src/util/JsonNullableFilter.ts: -------------------------------------------------------------------------------- 1 | import { JsonValue } from "type-fest"; 2 | export class JsonNullableFilter { 3 | equals?: JsonValue | null; 4 | not?: JsonValue | null; 5 | } 6 | -------------------------------------------------------------------------------- /admin-ui/src/util/MetaQueryPayload.ts: -------------------------------------------------------------------------------- 1 | export class MetaQueryPayload { 2 | count!: number; 3 | } 4 | -------------------------------------------------------------------------------- /admin-ui/src/util/QueryMode.ts: -------------------------------------------------------------------------------- 1 | export enum QueryMode { 2 | Default = "default", 3 | Insensitive = "insensitive", 4 | } 5 | -------------------------------------------------------------------------------- /admin-ui/src/util/SortOrder.ts: -------------------------------------------------------------------------------- 1 | export enum SortOrder { 2 | Asc = "asc", 3 | Desc = "desc", 4 | } 5 | -------------------------------------------------------------------------------- /admin-ui/src/util/StringFilter.ts: -------------------------------------------------------------------------------- 1 | import { QueryMode } from "./QueryMode"; 2 | 3 | export class StringFilter { 4 | equals?: string; 5 | in?: string[]; 6 | notIn?: string[]; 7 | lt?: string; 8 | lte?: string; 9 | gt?: string; 10 | gte?: string; 11 | contains?: string; 12 | startsWith?: string; 13 | endsWith?: string; 14 | mode?: QueryMode; 15 | not?: string; 16 | } 17 | -------------------------------------------------------------------------------- /admin-ui/src/util/StringNullableFilter.ts: -------------------------------------------------------------------------------- 1 | import { QueryMode } from "./QueryMode"; 2 | export class StringNullableFilter { 3 | equals?: string | null; 4 | in?: string[] | null; 5 | notIn?: string[] | null; 6 | lt?: string; 7 | lte?: string; 8 | gt?: string; 9 | gte?: string; 10 | contains?: string; 11 | startsWith?: string; 12 | endsWith?: string; 13 | mode?: QueryMode; 14 | not?: string; 15 | } 16 | -------------------------------------------------------------------------------- /admin-ui/src/wishlist/WishlistCreate.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { 3 | Create, 4 | SimpleForm, 5 | CreateProps, 6 | ReferenceInput, 7 | SelectInput, 8 | } from "react-admin"; 9 | import { ListingTitle } from "../listing/ListingTitle"; 10 | import { UserTitle } from "../user/UserTitle"; 11 | 12 | export const WishlistCreate = (props: CreateProps): React.ReactElement => { 13 | return ( 14 | <Create {...props}> 15 | <SimpleForm> 16 | <ReferenceInput source="listing.id" reference="Listing" label="listing"> 17 | <SelectInput optionText={ListingTitle} /> 18 | </ReferenceInput> 19 | <ReferenceInput source="user.id" reference="User" label="user"> 20 | <SelectInput optionText={UserTitle} /> 21 | </ReferenceInput> 22 | </SimpleForm> 23 | </Create> 24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /admin-ui/src/wishlist/WishlistEdit.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { 3 | Edit, 4 | SimpleForm, 5 | EditProps, 6 | ReferenceInput, 7 | SelectInput, 8 | } from "react-admin"; 9 | import { ListingTitle } from "../listing/ListingTitle"; 10 | import { UserTitle } from "../user/UserTitle"; 11 | 12 | export const WishlistEdit = (props: EditProps): React.ReactElement => { 13 | return ( 14 | <Edit {...props}> 15 | <SimpleForm> 16 | <ReferenceInput source="listing.id" reference="Listing" label="listing"> 17 | <SelectInput optionText={ListingTitle} /> 18 | </ReferenceInput> 19 | <ReferenceInput source="user.id" reference="User" label="user"> 20 | <SelectInput optionText={UserTitle} /> 21 | </ReferenceInput> 22 | </SimpleForm> 23 | </Edit> 24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /admin-ui/src/wishlist/WishlistList.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { 3 | List, 4 | Datagrid, 5 | ListProps, 6 | DateField, 7 | TextField, 8 | ReferenceField, 9 | } from "react-admin"; 10 | import Pagination from "../Components/Pagination"; 11 | import { LISTING_TITLE_FIELD } from "../listing/ListingTitle"; 12 | import { USER_TITLE_FIELD } from "../user/UserTitle"; 13 | 14 | export const WishlistList = (props: ListProps): React.ReactElement => { 15 | return ( 16 | <List 17 | {...props} 18 | bulkActionButtons={false} 19 | title={"wishlists"} 20 | perPage={50} 21 | pagination={<Pagination />} 22 | > 23 | <Datagrid rowClick="show"> 24 | <DateField source="createdAt" label="Created At" /> 25 | <TextField label="ID" source="id" /> 26 | <ReferenceField label="listing" source="listing.id" reference="Listing"> 27 | <TextField source={LISTING_TITLE_FIELD} /> 28 | </ReferenceField> 29 | <DateField source="updatedAt" label="Updated At" /> 30 | <ReferenceField label="user" source="user.id" reference="User"> 31 | <TextField source={USER_TITLE_FIELD} /> 32 | </ReferenceField> 33 | </Datagrid> 34 | </List> 35 | ); 36 | }; 37 | -------------------------------------------------------------------------------- /admin-ui/src/wishlist/WishlistShow.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { 3 | Show, 4 | SimpleShowLayout, 5 | ShowProps, 6 | DateField, 7 | TextField, 8 | ReferenceField, 9 | } from "react-admin"; 10 | import { LISTING_TITLE_FIELD } from "../listing/ListingTitle"; 11 | import { USER_TITLE_FIELD } from "../user/UserTitle"; 12 | 13 | export const WishlistShow = (props: ShowProps): React.ReactElement => { 14 | return ( 15 | <Show {...props}> 16 | <SimpleShowLayout> 17 | <DateField source="createdAt" label="Created At" /> 18 | <TextField label="ID" source="id" /> 19 | <ReferenceField label="listing" source="listing.id" reference="Listing"> 20 | <TextField source={LISTING_TITLE_FIELD} /> 21 | </ReferenceField> 22 | <DateField source="updatedAt" label="Updated At" /> 23 | <ReferenceField label="user" source="user.id" reference="User"> 24 | <TextField source={USER_TITLE_FIELD} /> 25 | </ReferenceField> 26 | </SimpleShowLayout> 27 | </Show> 28 | ); 29 | }; 30 | -------------------------------------------------------------------------------- /admin-ui/src/wishlist/WishlistTitle.ts: -------------------------------------------------------------------------------- 1 | import { Wishlist as TWishlist } from "../api/wishlist/Wishlist"; 2 | 3 | export const WISHLIST_TITLE_FIELD = "id"; 4 | 5 | export const WishlistTitle = (record: TWishlist): string => { 6 | return record.id || String(record.id); 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "noFallthroughCasesInSwitch": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "noEmit": true, 16 | "jsx": "react-jsx", 17 | "strict": true 18 | }, 19 | "include": ["src"], 20 | "exclude": ["./node_modules"] 21 | } 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "airbnb-clone", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "npm-run-all -p start:frontend start:backend", 8 | "start:frontend": "cross-env PORT=5000 npm --prefix public run dev", 9 | "start:admin": "npm --prefix admin-ui start", 10 | "start:backend": "npm --prefix server start", 11 | "postinstall": "npm i --prefix public && npm i --prefix admin-ui && npm i --prefix server" 12 | }, 13 | "devDependencies": { 14 | "cross-env": "^7.0.3", 15 | "npm-run-all": "^4.1.5" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/koolkishan/nextjs-airbnb-clone.git" 20 | }, 21 | "author": "", 22 | "license": "ISC", 23 | "bugs": { 24 | "url": "https://github.com/koolkishan/nextjs-airbnb-clone/issues" 25 | }, 26 | "homepage": "https://github.com/koolkishan/nextjs-airbnb-clone#readme", 27 | "dependencies": { 28 | "qs": "^6.11.2" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /public/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/babel", "next/core-web-vitals"] 3 | } 4 | -------------------------------------------------------------------------------- /public/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env*.local 29 | 30 | # vercel 31 | .vercel 32 | 33 | # typescript 34 | *.tsbuildinfo 35 | next-env.d.ts 36 | -------------------------------------------------------------------------------- /public/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | # or 12 | pnpm dev 13 | ``` 14 | 15 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 16 | 17 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. 18 | 19 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. 20 | 21 | ## Learn More 22 | 23 | To learn more about Next.js, take a look at the following resources: 24 | 25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 27 | 28 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 29 | 30 | ## Deploy on Vercel 31 | 32 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 33 | 34 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 35 | -------------------------------------------------------------------------------- /public/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "paths": { 4 | "airbnb/*": ["./src/*"] 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /public/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: false, 4 | env: { 5 | NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME: "dctahvizk", 6 | }, 7 | images: { 8 | domains: ["res.cloudinary.com"], 9 | }, 10 | }; 11 | 12 | module.exports = nextConfig; 13 | -------------------------------------------------------------------------------- /public/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "public", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@mapbox/mapbox-gl-geocoder": "^5.0.1", 13 | "@types/mapbox__mapbox-gl-geocoder": "^4.7.3", 14 | "@types/node": "20.3.1", 15 | "@types/react": "18.2.12", 16 | "@types/react-date-range": "^1.4.4", 17 | "@types/react-dom": "18.2.5", 18 | "ag-grid-community": "^30.0.3", 19 | "ag-grid-react": "^30.0.4", 20 | "autoprefixer": "10.4.14", 21 | "axios": "^1.4.0", 22 | "eslint": "8.43.0", 23 | "eslint-config-next": "13.4.6", 24 | "mapbox-gl": "^2.15.0", 25 | "next": "13.4.6", 26 | "next-cloudinary": "^4.13.0", 27 | "postcss": "8.4.24", 28 | "react": "18.2.0", 29 | "react-confetti": "^6.1.0", 30 | "react-date-range": "^1.4.0", 31 | "react-datepicker": "^4.15.0", 32 | "react-dom": "18.2.0", 33 | "react-icons": "^4.10.1", 34 | "react-map-gl": "^7.1.0", 35 | "tailwindcss": "3.3.2", 36 | "typescript": "5.1.3", 37 | "zustand": "^4.3.8" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /public/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/public/empty-profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koolkishan/nextjs-airbnb-clone/e5dafbecc58651ed033dcaf5d6f8de060a03117c/public/public/empty-profile.png -------------------------------------------------------------------------------- /public/public/home.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koolkishan/nextjs-airbnb-clone/e5dafbecc58651ed033dcaf5d6f8de060a03117c/public/public/home.mp4 -------------------------------------------------------------------------------- /public/public/home2.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koolkishan/nextjs-airbnb-clone/e5dafbecc58651ed033dcaf5d6f8de060a03117c/public/public/home2.mp4 -------------------------------------------------------------------------------- /public/public/home3.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koolkishan/nextjs-airbnb-clone/e5dafbecc58651ed033dcaf5d6f8de060a03117c/public/public/home3.mp4 -------------------------------------------------------------------------------- /public/public/overview1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koolkishan/nextjs-airbnb-clone/e5dafbecc58651ed033dcaf5d6f8de060a03117c/public/public/overview1.webp -------------------------------------------------------------------------------- /public/public/overview2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koolkishan/nextjs-airbnb-clone/e5dafbecc58651ed033dcaf5d6f8de060a03117c/public/public/overview2.webp -------------------------------------------------------------------------------- /public/public/overview3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koolkishan/nextjs-airbnb-clone/e5dafbecc58651ed033dcaf5d6f8de060a03117c/public/public/overview3.webp -------------------------------------------------------------------------------- /public/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koolkishan/nextjs-airbnb-clone/e5dafbecc58651ed033dcaf5d6f8de060a03117c/public/src/app/favicon.ico -------------------------------------------------------------------------------- /public/src/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | /* Hide scrollbar for Chrome, Safari and Opera */ 6 | .no-scrollbar::-webkit-scrollbar { 7 | display: none; 8 | } 9 | 10 | /* Hide scrollbar for IE, Edge and Firefox */ 11 | .no-scrollbar { 12 | -ms-overflow-style: none; /* IE and Edge */ 13 | scrollbar-width: none; /* Firefox */ 14 | } 15 | 16 | .control-panel { 17 | position: absolute; 18 | top: 0; 19 | right: 0; 20 | max-width: 320px; 21 | background: #fff; 22 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); 23 | padding: 12px 24px; 24 | margin: 20px; 25 | font-size: 13px; 26 | line-height: 2; 27 | color: #6b6b76; 28 | text-transform: uppercase; 29 | outline: none; 30 | } 31 | 32 | .mapboxgl-popup-content { 33 | width: 18rem !important; 34 | } 35 | 36 | .mapboxgl-popup-close-button { 37 | font-size: 1rem !important; 38 | margin-right: 0.2rem; 39 | } 40 | 41 | .mapboxgl-popup { 42 | width: 18rem !important; 43 | } 44 | 45 | div.mapboxgl-popup.mapboxgl-popup-anchor-top { 46 | width: 18rem !important; 47 | max-width: 18rem !important; 48 | font-size: 1rem; 49 | } 50 | -------------------------------------------------------------------------------- /public/src/app/layout.jsx: -------------------------------------------------------------------------------- 1 | import NavigationEvents from "airbnb/components/common/NavigationEvents"; 2 | import "./globals.css"; 3 | import "mapbox-gl/dist/mapbox-gl.css"; 4 | 5 | import { Inter } from "next/font/google"; 6 | import { useRouter } from "next/navigation"; 7 | import { Suspense } from "react"; 8 | const inter = Inter({ subsets: ["latin"] }); 9 | 10 | export const metadata = { 11 | title: "Airbnb Clone", 12 | description: "Created by Kishan Sheth", 13 | }; 14 | 15 | export default function RootLayout({ children }) { 16 | return ( 17 | <html lang="en"> 18 | <link 19 | href="https://api.mapbox.com/mapbox-gl-js/v2.6.1/mapbox-gl.css" 20 | rel="stylesheet" 21 | precedence="default" 22 | /> 23 | <link 24 | rel="stylesheet" 25 | href="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v4.7.2/mapbox-gl-geocoder.css" 26 | type="text/css" 27 | precedence="default" 28 | ></link> 29 | <body className={inter.className}> 30 | {children} 31 | <Suspense fallback={null}> 32 | <NavigationEvents /> 33 | </Suspense> 34 | </body> 35 | </html> 36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /public/src/app/listing/[listing]/components/ListingAmenties.jsx: -------------------------------------------------------------------------------- 1 | import { AmenetiesType } from "airbnb/data/Amenities"; 2 | import { userAppStore } from "airbnb/store/store"; 3 | import React from "react"; 4 | 5 | export default function ListingAmenties() { 6 | const { currentListing } = userAppStore(); 7 | function getSvgPathByName(name) { 8 | for (const amenity of AmenetiesType) { 9 | for (const data of amenity.data) { 10 | if (data.name === name) { 11 | return data.svgPath; 12 | } 13 | } 14 | } 15 | return null; 16 | } 17 | 18 | return ( 19 | <div className="flex flex-col gap-2"> 20 | <h4 className="text-xl font-semibold ">Amenties</h4> 21 | <ul className="grid grid-cols-5 gap-2"> 22 | {currentListing.placeAmeneites.map((amenity) => ( 23 | <li 24 | key={amenity} 25 | className="border border-gray-300 p-3 rounded-lg flex flex-col justify-start items-start" 26 | > 27 | {getSvgPathByName(amenity)} 28 | <span>{amenity}</span> 29 | </li> 30 | ))} 31 | </ul> 32 | </div> 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /public/src/app/listing/[listing]/components/ListingMap.jsx: -------------------------------------------------------------------------------- 1 | import Pin from "airbnb/components/common/Pin"; 2 | import { userAppStore } from "airbnb/store/store"; 3 | import React, { useMemo } from "react"; 4 | import Map, { Marker } from "react-map-gl"; 5 | 6 | const TOKEN = 7 | "pk.eyJ1Ijoia29vbGtpc2hhbiIsImEiOiJjazV3Zm41cG8wa3I1M2tydnVkcW53b2ZpIn0.mYrXogbdTrWSoJECNR1epg"; 8 | 9 | export default function ListingMap() { 10 | const { currentListing } = userAppStore(); 11 | const pins = useMemo(() => { 12 | return ( 13 | <Marker 14 | longitude={currentListing.mapData.longitude} 15 | latitude={currentListing.mapData.latitude} 16 | > 17 | <Pin /> 18 | </Marker> 19 | ); 20 | }, [currentListing]); 21 | return ( 22 | <div className="h-96 w-full"> 23 | <Map 24 | initialViewState={{ 25 | longitude: currentListing.mapData.longitude, 26 | latitude: currentListing.mapData.latitude, 27 | zoom: 13, 28 | }} 29 | scrollZoom={false} 30 | dragPan={false} 31 | dragRotate={false} 32 | doubleClickZoom={false} 33 | mapStyle="mapbox://styles/mapbox/streets-v9" 34 | mapboxAccessToken={TOKEN} 35 | > 36 | {pins} 37 | </Map> 38 | </div> 39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /public/src/app/listing/[listing]/components/ListingPhotos.jsx: -------------------------------------------------------------------------------- 1 | import { userAppStore } from "airbnb/store/store"; 2 | import Image from "next/image"; 3 | import React, { useState } from "react"; 4 | 5 | export default function ListingPhotos() { 6 | const { currentListing } = userAppStore(); 7 | const [currentPhoto, setCurrentPhoto] = useState(0); 8 | return ( 9 | <div className="flex gap-5 flex-col"> 10 | <div className="relative w-full h-[60vh]"> 11 | <Image alt="listing" fill src={currentListing.photos[currentPhoto]} /> 12 | </div> 13 | {currentListing.photos.length > 1 && ( 14 | <ul className="flex gap-5 flex-wrap"> 15 | {currentListing.photos.map((photo, index) => ( 16 | <li 17 | key={photo} 18 | className="relative w-48 h-32 cursor-pointer" 19 | onClick={() => setCurrentPhoto(index)} 20 | > 21 | <Image alt="listing" fill src={photo} /> 22 | </li> 23 | ))} 24 | </ul> 25 | )} 26 | </div> 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /public/src/app/listing/[listing]/loading.jsx: -------------------------------------------------------------------------------- 1 | import Spinner from "airbnb/components/common/Spinner"; 2 | import React from "react"; 3 | 4 | export default function loading() { 5 | return <Spinner />; 6 | } 7 | -------------------------------------------------------------------------------- /public/src/app/loading.jsx: -------------------------------------------------------------------------------- 1 | import Spinner from "airbnb/components/common/Spinner"; 2 | import React from "react"; 3 | 4 | export default function loading() { 5 | return <Spinner />; 6 | } 7 | -------------------------------------------------------------------------------- /public/src/app/my-listings/loading.jsx: -------------------------------------------------------------------------------- 1 | import Spinner from "airbnb/components/common/Spinner"; 2 | import React from "react"; 3 | 4 | export default function loading() { 5 | return <Spinner />; 6 | } 7 | -------------------------------------------------------------------------------- /public/src/app/new-listing/loading.jsx: -------------------------------------------------------------------------------- 1 | import Spinner from "airbnb/components/common/Spinner"; 2 | import React from "react"; 3 | 4 | export default function loading() { 5 | return <Spinner />; 6 | } 7 | -------------------------------------------------------------------------------- /public/src/app/search/loading.jsx: -------------------------------------------------------------------------------- 1 | import Spinner from "airbnb/components/common/Spinner"; 2 | import React from "react"; 3 | 4 | export default function loading() { 5 | return <Spinner />; 6 | } 7 | -------------------------------------------------------------------------------- /public/src/components/SearchScheduler/SearchDates.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default function SearchDates() { 4 | const [state, setState] = useState([ 5 | { 6 | startDate: new Date(), 7 | endDate: addDays(new Date(), 7), 8 | key: "selection", 9 | }, 10 | ]); 11 | 12 | function formatDate(dateString) { 13 | const options = { month: "long", day: "numeric" }; 14 | const date = new Date(dateString); 15 | return date.toLocaleDateString("en-US", options); 16 | } 17 | 18 | return ( 19 | <> 20 | <label htmlFor="" className="text-xs font-semibold flex flex-col"> 21 | Check in 22 | </label> 23 | <span>{formatDate(state[0]?.startDate)}</span> 24 | {selected === "check-in" && ( 25 | <div className="absolute top-28 left-0 shadow-xl"> 26 | <Calender state={state} setState={setState} /> 27 | </div> 28 | )} 29 | </> 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /public/src/components/common/Calender.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { addDays } from "date-fns"; 3 | import { DateRangePicker } from "react-date-range"; 4 | import "react-date-range/dist/styles.css"; // main css file 5 | import "react-date-range/dist/theme/default.css"; // theme css file 6 | export default function Calender({ state, setState }) { 7 | return ( 8 | <div className=""> 9 | <DateRangePicker 10 | onChange={(item) => setState([item.selection])} 11 | showSelectionPreview={true} 12 | moveRangeOnFirstSelection={false} 13 | months={2} 14 | ranges={state} 15 | direction="horizontal" 16 | /> 17 | </div> 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /public/src/components/common/FormInput.jsx: -------------------------------------------------------------------------------- 1 | import React, { Dispatch, SetStateAction } from "react"; 2 | 3 | export default function FormInput({ 4 | name, 5 | type = "text", 6 | value, 7 | setValue, 8 | placeholder, 9 | isListing = false, 10 | }) { 11 | return ( 12 | <input 13 | type={type} 14 | value={value} 15 | name={name} 16 | onChange={(e) => 17 | isListing ? setValue(name, e.target.value) : setValue(e.target.value) 18 | } 19 | placeholder={placeholder} 20 | className="border border-gray-300 px-2 py-4 rounded-md w-full" 21 | /> 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /public/src/components/common/NavigationEvents.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { me } from "airbnb/lib/auth"; 3 | import { userAppStore } from "airbnb/store/store"; 4 | import { usePathname } from "next/navigation"; 5 | import { useEffect } from "react"; 6 | 7 | export default function NavigationEvents() { 8 | const pathName = usePathname(); 9 | const { 10 | setInitialView, 11 | setCurrentListing, 12 | setShowScheduleBar, 13 | showScheduleBar, 14 | setUserInfo, 15 | userInfo, 16 | } = userAppStore(); 17 | useEffect(() => { 18 | setInitialView(); 19 | setCurrentListing(undefined); 20 | if (!userInfo) { 21 | const getData = async () => { 22 | const data = await me(); 23 | setUserInfo(data); 24 | }; 25 | getData(); 26 | } 27 | // if ( showScheduleBar) setShowScheduleBar(); 28 | }, [ 29 | pathName, 30 | setInitialView, 31 | showScheduleBar, 32 | setShowScheduleBar, 33 | setCurrentListing, 34 | setUserInfo, 35 | userInfo, 36 | ]); 37 | return null; 38 | } 39 | -------------------------------------------------------------------------------- /public/src/components/common/Pin.jsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | const ICON = `M20.2,15.7L20.2,15.7c1.1-1.6,1.8-3.6,1.8-5.7c0-5.6-4.5-10-10-10S2,4.5,2,10c0,2,0.6,3.9,1.6,5.4c0,0.1,0.1,0.2,0.2,0.3 4 | c0,0,0.1,0.1,0.1,0.2c0.2,0.3,0.4,0.6,0.7,0.9c2.6,3.1,7.4,7.6,7.4,7.6s4.8-4.5,7.4-7.5c0.2-0.3,0.5-0.6,0.7-0.9 5 | C20.1,15.8,20.2,15.8,20.2,15.7z`; 6 | 7 | const pinStyle = { 8 | cursor: "pointer", 9 | fill: "#d00", 10 | stroke: "none", 11 | }; 12 | 13 | function Pin({ size = 20 }) { 14 | return ( 15 | <svg height={size} viewBox="0 0 24 24" style={pinStyle}> 16 | <path d={ICON} /> 17 | </svg> 18 | ); 19 | } 20 | 21 | export default React.memo(Pin); 22 | -------------------------------------------------------------------------------- /public/src/components/common/ScheduleBar.jsx: -------------------------------------------------------------------------------- 1 | import { userAppStore } from "airbnb/store/store"; 2 | import React from "react"; 3 | import { HiOutlineSearch } from "react-icons/hi"; 4 | export default function ScheduleBar() { 5 | const { setShowScheduleBar } = userAppStore(); 6 | return ( 7 | <div 8 | className="flex items-center justify-center h-full" 9 | onClick={setShowScheduleBar} 10 | > 11 | <div className="flex items-center gap-5 border p-2 rounded-full w-max "> 12 | <span className="pl-5 cursor-pointer">Anywhere</span> 13 | <span className="cursor-pointer">Any week</span> 14 | <span className="cursor-pointer">Add guests</span> 15 | <span className=" bg-airbnb-theme-color flex items-center justify-center p-2 rounded-full cursor-pointer"> 16 | <HiOutlineSearch className="text-white" /> 17 | </span> 18 | </div> 19 | </div> 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /public/src/components/common/Spinner.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default function Spinner() { 4 | return ( 5 | <div className="h-[100vh] w-[100vw] flex justify-center items-center"> 6 | <svg 7 | aria-hidden="true" 8 | className="w-8 h-8 mr-2 text-gray-200 animate-spin fill-airbnb-theme-color" 9 | viewBox="0 0 100 101" 10 | fill="none" 11 | xmlns="http://www.w3.org/2000/svg" 12 | > 13 | <path 14 | d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z" 15 | fill="currentColor" 16 | /> 17 | <path 18 | d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z" 19 | fill="currentFill" 20 | /> 21 | </svg> 22 | <span className="sr-only">Loading...</span> 23 | </div> 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /public/src/components/footer/CompactFooter.jsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import React from "react"; 3 | import { PiCaretUpBold } from "react-icons/pi"; 4 | import { FiGlobe } from "react-icons/fi"; 5 | 6 | export default function CompactFooter() { 7 | const links = [ 8 | "privacy", 9 | "terms", 10 | "sitemap", 11 | "company details", 12 | "destinations", 13 | ]; 14 | return ( 15 | <div className=" px-20 border-t border-t-gray-200 py-4 flex justify-between w-full text-sm z-50 bg-white items-center"> 16 | <ul className="flex gap-3 font-normal "> 17 | <li>© {new Date().getFullYear()} Airbnb, Inc</li> 18 | {links.map((link) => ( 19 | <li key={link}> 20 | <Link href="#"> 21 | {link.charAt(0).toUpperCase() + link.slice(1).toLowerCase()} 22 | </Link> 23 | </li> 24 | ))} 25 | </ul> 26 | <ul className="flex gap-4 font-medium"> 27 | <li className="flex items-center gap-2 cursor-pointer"> 28 | <FiGlobe /> English (IN) 29 | </li> 30 | <li className="cursor-pointer">$ USD</li> 31 | <li className="flex items-center gap-2 cursor-pointer"> 32 | Support & resources <PiCaretUpBold /> 33 | </li> 34 | </ul> 35 | </div> 36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /public/src/components/footer/Footer.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default function Footer() { 4 | return <div>Footer</div>; 5 | } 6 | -------------------------------------------------------------------------------- /public/src/components/process/Description.jsx: -------------------------------------------------------------------------------- 1 | import { userAppStore } from "airbnb/store/store"; 2 | import React from "react"; 3 | 4 | export default function Description() { 5 | const { description, setDescription } = userAppStore(); 6 | return ( 7 | <div className="flex items-center justify-center h-full text-airbnb-light-black"> 8 | <div className="flex flex-col gap-5"> 9 | <div className="flex flex-col gap-2"> 10 | <h2 className="font-semibold text-4xl">Create your description</h2> 11 | <p>Share what makes your place special.</p> 12 | </div> 13 | <div className="flex flex-col gap-4"> 14 | <textarea 15 | className="border border-gray-400 h-56 w-[550px] rounded-lg active:border-gray-950 p-6 no-scrollbar text-lg " 16 | value={description} 17 | onChange={(e) => { 18 | if (e.target.value.length <= 500) { 19 | setDescription(e.target.value); 20 | } 21 | }} 22 | /> 23 | <span>{description.length}/500</span> 24 | </div> 25 | </div> 26 | </div> 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /public/src/components/process/FinishSetup.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default function FinishSetup() { 4 | return <div>FinishSetup</div>; 5 | } 6 | -------------------------------------------------------------------------------- /public/src/components/process/ListingTypeSelector.jsx: -------------------------------------------------------------------------------- 1 | import { listingTypes } from "airbnb/data/listingTypes"; 2 | import { userAppStore } from "airbnb/store/store"; 3 | import Image from "next/image"; 4 | import React, { useState } from "react"; 5 | 6 | export default function ListingTypeSelector() { 7 | const { locationType, setLocationType } = userAppStore(); 8 | const handleSelection = (type) => { 9 | setLocationType(type); 10 | }; 11 | 12 | return ( 13 | <div className="flex justify-center items-center max-h-[80vh] h-[80vh]"> 14 | <div> 15 | <h2 className="font-semibold text-4xl"> 16 | Which of these best describes your place? 17 | </h2> 18 | <div className="grid grid-cols-3 gap-5 max-h-[70vh] overflow-auto scroll no-scrollbar my-10 pb-5"> 19 | {listingTypes.map((type) => ( 20 | <button 21 | key={type.name} 22 | className={`flex flex-col font-semibold border border-gray-300 rounded-md p-3 hover:border-gray-950 transition-all duration-300 ${ 23 | type.name === locationType && "border-gray-950 bg-slate-100" 24 | }`} 25 | onClick={() => handleSelection(type.name)} 26 | > 27 | {type.svgPath} 28 | <span>{type.name}</span> 29 | </button> 30 | ))} 31 | </div> 32 | </div> 33 | </div> 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /public/src/components/process/Photos.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import React, { useState } from "react"; 3 | import ImageUpload from "../common/ImageUpload"; 4 | import { userAppStore } from "airbnb/store/store"; 5 | import { CldUploadButton } from "next-cloudinary"; 6 | import Image from "next/image"; 7 | 8 | export default function Photos() { 9 | const { photos, setPhotos } = userAppStore(); 10 | const handleUpload = (data) => { 11 | setPhotos([...photos, data.info.secure_url]); 12 | }; 13 | return ( 14 | <div className="flex gap-5 items-center justify-center flex-col h-full"> 15 | <h2 className="font-semibold text-4xl">Add some photos of your house</h2> 16 | <p> 17 | You'll need 5 photos to get started. You can add more or make changes 18 | later. 19 | </p> 20 | <CldUploadButton 21 | options={{ multiple: true }} 22 | onUpload={handleUpload} 23 | uploadPreset="lxgz9s60" 24 | > 25 | <span className="bg-airbnb-gradient py-3 mt-5 px-5 text-white text-base font-medium rounded-md cursor-pointer"> 26 | Upload 27 | </span> 28 | </CldUploadButton> 29 | 30 | <div className="grid grid-cols-3 gap-4 h-[55vh] overflow-auto pb-10 no-scrollbar"> 31 | {photos.map((photo) => ( 32 | <div className="relative h-36 w-[200px]" key={photo}> 33 | <Image src={photo} fill alt="upload" /> 34 | </div> 35 | ))} 36 | </div> 37 | </div> 38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /public/src/components/process/Price.jsx: -------------------------------------------------------------------------------- 1 | import { userAppStore } from "airbnb/store/store"; 2 | import React from "react"; 3 | 4 | export default function Price() { 5 | const { price, setPrice } = userAppStore(); 6 | return ( 7 | <div className="flex items-center justify-center h-full text-airbnb-light-black"> 8 | <div className="flex flex-col gap-5"> 9 | <div className="flex flex-col gap-2"> 10 | <h2 className="font-semibold text-4xl">Now, set your price</h2> 11 | <p>You can change it anytime.</p> 12 | </div> 13 | <div className="flex flex-col gap-4 relative"> 14 | <span className="absolute left-2 top-11 transform -translate-y-1/2 text-2xl text-black"> 15 | $ 16 | </span> 17 | <textarea 18 | className="border border-gray-400 h-20 w-[550px] rounded-lg active:border-gray-950 p-6 no-scrollbar text-3xl items-center flex justify-center pl-7" 19 | value={price} 20 | onChange={(e) => { 21 | if (e.target.value) { 22 | setPrice(parseInt(e.target.value)); 23 | } else setPrice(0); 24 | }} 25 | /> 26 | </div> 27 | </div> 28 | </div> 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /public/src/components/process/Review.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default function Review() { 4 | return <div>Review</div>; 5 | } 6 | -------------------------------------------------------------------------------- /public/src/components/process/StepOneStarter.jsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import React from "react"; 3 | 4 | export default function StepOneStarter() { 5 | return ( 6 | <div className="flex items-center h-full mx-20"> 7 | <div className="grid grid-cols-2 gap-10 items-center"> 8 | <div className="flex flex-col gap-4 text-airbnb-light-black"> 9 | <h3 className="text-2xl">Step 1</h3> 10 | <h1 className="text-4xl"> 11 | <strong>Tel us about your place</strong> 12 | </h1> 13 | <p className="text-xl"> 14 | In this step, we'll ask you which type of property you have and if 15 | guests will book the entire place or just a room. Then let us know 16 | the location and how many guests can stay. 17 | </p> 18 | </div> 19 | <div className=""> 20 | <video src="/home.mp4" autoPlay loop controls={false}></video> 21 | </div> 22 | </div> 23 | </div> 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /public/src/components/process/StepThreeStarter.jsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import React from "react"; 3 | 4 | export default function StepThreeStarter() { 5 | return ( 6 | <div className="flex items-center h-full mx-20"> 7 | <div className="grid grid-cols-2 gap-10 items-center"> 8 | <div className="flex flex-col gap-4 text-airbnb-light-black"> 9 | <h3 className="text-2xl">Step 3</h3> 10 | <h1 className="text-4xl"> 11 | <strong>Finish up and publish</strong> 12 | </h1> 13 | <p className="text-xl"> 14 | Finally, you’ll choose if you'd like to start with an experienced 15 | guest, then you'll set your nightly price. Answer a few quick 16 | questions and publish when you're ready. 17 | </p> 18 | </div> 19 | <div className=""> 20 | <video src="/home3.mp4" autoPlay loop controls={false}></video> 21 | </div> 22 | </div> 23 | </div> 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /public/src/components/process/StepTwoStarter.jsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import React from "react"; 3 | 4 | export default function StepTwoStarter() { 5 | return ( 6 | <div className="flex items-center h-full mx-20"> 7 | <div className="grid grid-cols-2 gap-10 items-center"> 8 | <div className="flex flex-col gap-4 text-airbnb-light-black"> 9 | <h3 className="text-2xl">Step 2</h3> 10 | <h1 className="text-4xl"> 11 | <strong>Make your place stand out</strong> 12 | </h1> 13 | <p className="text-xl"> 14 | In this step, you’ll add some of the amenities your place offers, 15 | plus 5 or more photos. Then you’ll create a title and description. 16 | </p> 17 | </div> 18 | <div className=""> 19 | <video src="/home2.mp4" autoPlay loop controls={false}></video> 20 | </div> 21 | </div> 22 | </div> 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /public/src/components/process/Title.jsx: -------------------------------------------------------------------------------- 1 | import { userAppStore } from "airbnb/store/store"; 2 | import React from "react"; 3 | 4 | export default function Title() { 5 | const { title, setTitle } = userAppStore(); 6 | return ( 7 | <div className="flex flex-col gap-5 items-center justify-center h-full text-airbnb-light-black"> 8 | <div className="flex flex-col gap-2"> 9 | <h2 className="font-semibold text-4xl"> 10 | Now, let's give your house a title 11 | </h2> 12 | <p> 13 | Short titles work best. Have fun with it – you can always change it 14 | later. 15 | </p> 16 | </div> 17 | <div className="flex flex-col gap-4"> 18 | <textarea 19 | className="border border-gray-400 h-40 w-[550px] rounded-lg active:border-gray-950 p-6 no-scrollbar text-4xl" 20 | value={title} 21 | onChange={(e) => { 22 | if (e.target.value.length <= 32) { 23 | setTitle(e.target.value); 24 | } 25 | }} 26 | /> 27 | <span>{title.length}/32</span> 28 | </div> 29 | </div> 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /public/src/components/views/ListView.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ListingCard from "../listingCard"; 3 | import { userAppStore } from "airbnb/store/store"; 4 | 5 | export default function ListView() { 6 | const { listings } = userAppStore(); 7 | return ( 8 | <div className="grid grid-cols-5 px-20 gap-10 py-10 justify-start items-start"> 9 | {listings?.map((listing, index) => ( 10 | <ListingCard data={listing} key={index} /> 11 | ))} 12 | </div> 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /public/src/components/views/ViewSwitchBadge.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import React from "react"; 3 | import { BsFillMapFill } from "react-icons/bs"; 4 | import { AiOutlineUnorderedList } from "react-icons/ai"; 5 | import { userAppStore } from "airbnb/store/store"; 6 | 7 | function ViewSwitchBadge() { 8 | const { isMapView, setMapView } = userAppStore(); 9 | 10 | return ( 11 | <div className="fixed flex justify-center items-center bottom-16 left-0 right-0 cursor-pointer"> 12 | <div className=" bg-black px-4 py-4 text-white rounded-full "> 13 | <span 14 | className="flex items-center gap-2 text-sm" 15 | onClick={() => setMapView()} 16 | > 17 | {!isMapView ? ( 18 | <> 19 | Show Map <BsFillMapFill /> 20 | </> 21 | ) : ( 22 | <> 23 | Show List <AiOutlineUnorderedList /> 24 | </> 25 | )} 26 | </span> 27 | </div> 28 | </div> 29 | ); 30 | } 31 | 32 | export default ViewSwitchBadge; 33 | -------------------------------------------------------------------------------- /public/src/hooks/useClickOutside.jsx: -------------------------------------------------------------------------------- 1 | import { userAppStore } from "airbnb/store/store"; 2 | import { useEffect, RefObject, useRef } from "react"; 3 | 4 | function useClickOutside(isScheduleBar = false) { 5 | const { setSelectionType, setShowScheduleBar } = userAppStore(); 6 | const containerRef = useRef(null); 7 | useEffect(() => { 8 | function handleClickOutside(event) { 9 | if ( 10 | containerRef.current && 11 | !containerRef.current.contains(event.target) 12 | ) { 13 | if (!isScheduleBar) { 14 | setSelectionType(undefined); 15 | } else { 16 | setShowScheduleBar(); 17 | } 18 | } 19 | } 20 | 21 | document.addEventListener("mousedown", handleClickOutside); 22 | 23 | return () => { 24 | document.removeEventListener("mousedown", handleClickOutside); 25 | }; 26 | }, [containerRef, setSelectionType]); 27 | return [containerRef]; 28 | } 29 | 30 | export default useClickOutside; 31 | -------------------------------------------------------------------------------- /public/src/lib/auth.js: -------------------------------------------------------------------------------- 1 | import { createUrl, get, isStoredJwt, post, setStoredJwt } from "./http"; 2 | 3 | export const me = async () => { 4 | return isStoredJwt() 5 | ? (await get(createUrl("/api/me")).catch(() => null))?.data 6 | : null; 7 | }; 8 | 9 | export const login = async (username, password) => { 10 | const result = ( 11 | await post(createUrl("/api/login"), { username, password }).catch( 12 | () => null 13 | ) 14 | )?.data; 15 | 16 | if (!result) { 17 | return alert("Could not login"); 18 | } 19 | setStoredJwt(result.accessToken); 20 | return me(); 21 | }; 22 | 23 | export const signup = async (username, password, firstName, lastName) => { 24 | const result = ( 25 | await post(createUrl("/api/signup"), { 26 | username, 27 | password, 28 | firstName, 29 | lastName, 30 | }).catch(() => null) 31 | )?.data; 32 | 33 | if (!result) { 34 | return alert("Could not sign up"); 35 | } 36 | setStoredJwt(result.accessToken); 37 | return me(); 38 | }; 39 | 40 | export const checkUser = async (email) => { 41 | const result = ( 42 | await post(createUrl("/api/check-user"), { email }).catch(() => null) 43 | )?.data; 44 | if (!result) return false; 45 | return true; 46 | }; 47 | -------------------------------------------------------------------------------- /public/src/lib/http.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | const apiUrl = "http://localhost:3000"; 4 | const jwtKey = "accessToken"; 5 | 6 | axios.interceptors.request.use( 7 | (config) => { 8 | const { origin } = new URL(config.url); 9 | const allowedOrigins = [apiUrl]; 10 | const accessToken = localStorage.getItem(jwtKey); 11 | if (allowedOrigins.includes(origin)) { 12 | config.headers.authorization = `Bearer ${accessToken}`; 13 | } 14 | return config; 15 | }, 16 | (error) => { 17 | return Promise.reject(error); 18 | } 19 | ); 20 | 21 | export const createUrl = (endpoint) => new URL(endpoint, apiUrl).href; 22 | export const isStoredJwt = () => Boolean(localStorage.getItem(jwtKey)); 23 | export const setStoredJwt = (accessToken) => 24 | localStorage.setItem(jwtKey, accessToken); 25 | 26 | export const get = axios.get; 27 | export const patch = axios.patch; 28 | export const post = axios.post; 29 | -------------------------------------------------------------------------------- /public/src/store/slices/AuthSlice.js: -------------------------------------------------------------------------------- 1 | export const createAuthSlice = (set, get) => ({ 2 | isAuthModalOpen: false, 3 | isLoggedIn: false, 4 | userInfo: null, 5 | setAuthModal: () => { 6 | set({ isAuthModalOpen: !get().isAuthModalOpen }); 7 | }, 8 | setIsLoggedIn: (status) => { 9 | set({ isLoggedIn: status }); 10 | }, 11 | setUserInfo: (userInfo) => { 12 | set({ userInfo }); 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /public/src/store/slices/ListingsSlice.js: -------------------------------------------------------------------------------- 1 | export const createLisitingsSlice = (set, get) => ({ 2 | wishLists: [], 3 | setWishLists: (wishLists) => set({ wishLists }), 4 | wishListsPage: [], 5 | setWishListsPage: (wishListsPage) => set({ wishListsPage }), 6 | addToWishListSlice: (id) => { 7 | const lists = get().wishLists; 8 | lists.push(id); 9 | set({ wishLists: lists }); 10 | }, 11 | removeFromWishList: () => {}, 12 | currentListing: undefined, 13 | setCurrentListing: (listing) => set({ currentListing: listing }), 14 | removeUserListing: (listing) => { 15 | const listings = get().userListings; 16 | const index = listings.findIndex((list) => list.id === listing); 17 | if (index !== -1) { 18 | listings.splice(index, 1); 19 | } 20 | set({ userListings: listings }); 21 | }, 22 | isMapView: false, 23 | listings: [], 24 | showScheduleBar: false, 25 | userListings: [], 26 | setUserListings: (userListings) => set({ userListings }), 27 | setMapView: () => { 28 | set({ isMapView: !get().isMapView }); 29 | }, 30 | setInitialView: () => { 31 | set({ isMapView: false }); 32 | }, 33 | setListings: (listings) => { 34 | set({ listings }); 35 | }, 36 | setShowScheduleBar: () => { 37 | set({ showScheduleBar: !get().showScheduleBar }); 38 | }, 39 | }); 40 | -------------------------------------------------------------------------------- /public/src/store/slices/ProcessSlice.js: -------------------------------------------------------------------------------- 1 | export const createProcessSlice = (set, get) => ({ 2 | resetNewListingData: () => 3 | set({ 4 | locationType: undefined, 5 | placetype: undefined, 6 | mapData: undefined, 7 | locationData: undefined, 8 | placeSpace: { bathrooms: 1, beds: 1, guests: 4 }, 9 | placeAmeneites: [], 10 | photos: [], 11 | title: "", 12 | description: "", 13 | price: 0, 14 | }), 15 | locationType: undefined, 16 | setLocationType: (locationType) => set({ locationType }), 17 | placetype: undefined, 18 | setPlaceType: (placetype) => set({ placetype }), 19 | mapData: undefined, 20 | setMapData: (mapData) => set({ mapData }), 21 | locationData: undefined, 22 | setLocationData: (locationData) => set({ locationData }), 23 | placeSpace: { bathrooms: 1, beds: 1, guests: 4 }, 24 | setPlaceSpace: (placeSpace) => set({ placeSpace }), 25 | placeAmeneites: [], 26 | setPlaceAmenities: (placeAmeneites) => set({ placeAmeneites }), 27 | photos: [], 28 | setPhotos: (photos) => set({ photos }), 29 | title: "", 30 | setTitle: (title) => set({ title }), 31 | description: "", 32 | setDescription: (description) => set({ description }), 33 | price: 0, 34 | setPrice: (price) => set({ price }), 35 | }); 36 | -------------------------------------------------------------------------------- /public/src/store/slices/SearchSlice.js: -------------------------------------------------------------------------------- 1 | export const createSearchSlice = (set, get) => ({ 2 | searchLocation: undefined, 3 | setSearchLocation: (location) => set({ searchLocation: location }), 4 | selectionType: undefined, 5 | searchPlaceSpace: { 6 | adults: 0, 7 | childrens: 0, 8 | infants: 0, 9 | }, 10 | dates: undefined, 11 | searchListings: [], 12 | setSearchListings: (searchListings) => set({ searchListings }), 13 | setSearchPlaceSpace: (searchPlaceSpace) => set({ searchPlaceSpace }), 14 | setSelectionType: (type) => set({ selectionType: type }), 15 | setDates: (dates) => set({ dates }), 16 | }); 17 | -------------------------------------------------------------------------------- /public/src/store/store.js: -------------------------------------------------------------------------------- 1 | import { create } from "zustand"; 2 | import { createLisitingsSlice } from "./slices/ListingsSlice"; 3 | import { createAuthSlice } from "./slices/AuthSlice"; 4 | import { createProcessSlice } from "./slices/ProcessSlice"; 5 | import { createSearchSlice } from "./slices/SearchSlice"; 6 | 7 | export const userAppStore = create()((...a) => ({ 8 | ...createLisitingsSlice(...a), 9 | ...createAuthSlice(...a), 10 | ...createProcessSlice(...a), 11 | ...createSearchSlice(...a), 12 | })); 13 | -------------------------------------------------------------------------------- /public/src/svg/ameneties/pool-table.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default function PoolTable() { 4 | return <div>PoolTable</div>; 5 | } 6 | -------------------------------------------------------------------------------- /public/src/svg/daimond.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default function Daimond() { 4 | return ( 5 | <svg 6 | viewBox="0 0 48 48" 7 | xmlns="http://www.w3.org/2000/svg" 8 | aria-hidden="true" 9 | role="presentation" 10 | focusable="false" 11 | style={{ 12 | display: "block", 13 | height: 48, 14 | width: 48, 15 | fill: "rgb(227, 28, 95)", 16 | stroke: "currentcolor", 17 | }} 18 | > 19 | <g stroke="none"> 20 | <path 21 | d="m32.62 6 9.526 11.114-18.146 23.921-18.147-23.921 9.526-11.114z" 22 | fillOpacity=".2" 23 | /> 24 | <path d="m34.4599349 2 12.8243129 14.9616983-23.2842478 30.6928721-23.28424779-30.6928721 12.82431289-14.9616983zm-17.9171827 16h-12.52799999l18.25899999 24.069zm27.441 0h-12.528l-5.73 24.069zm-14.583 0h-10.802l5.4012478 22.684zm-15.92-12.86-9.30799999 10.86h11.89399999zm19.253-1.141h-17.468l2.857 12.001h11.754zm1.784 1.141-2.586 10.86h11.894z" /> 25 | </g> 26 | </svg> 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /public/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", 5 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}", 6 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}", 7 | ], 8 | theme: { 9 | extend: { 10 | backgroundImage: { 11 | "airbnb-gradient": 12 | "linear-gradient(to right,#E61E4D 0%,#E31C5F 50%,#D70466 100%)", 13 | }, 14 | colors: { 15 | "airbnb-theme-color": "#FF385C", 16 | "airbnb-light-black": "#222222", 17 | "airbnb-light-gray": "#717171", 18 | }, 19 | gridTemplateRows: { 20 | "new-listing": "10vh 80vh 10vh", 21 | }, 22 | }, 23 | }, 24 | plugins: [], 25 | }; 26 | -------------------------------------------------------------------------------- /server/.dockerignore: -------------------------------------------------------------------------------- 1 | .dockerignore 2 | docker-compose.yml 3 | Dockerfile 4 | dist/ 5 | node_modules 6 | .env 7 | .gitignore 8 | .prettierignore -------------------------------------------------------------------------------- /server/.env: -------------------------------------------------------------------------------- 1 | BCRYPT_SALT=10 2 | COMPOSE_PROJECT_NAME=amp_cljejk0j20myvlu013m2efcap 3 | PORT=3000 4 | DB_URL=postgres://admin:admin@localhost:5432/my-db 5 | DB_USER=admin 6 | DB_PASSWORD=admin 7 | DB_PORT=5432 8 | DB_NAME=my-db 9 | JWT_SECRET_KEY=Change_ME!!! 10 | JWT_EXPIRATION=2d -------------------------------------------------------------------------------- /server/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | /node_modules 4 | /dist 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /server/.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | prisma/migrations/ 4 | package-lock.json 5 | coverage/ -------------------------------------------------------------------------------- /server/docker-compose.db.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | db: 4 | image: postgres:12 5 | ports: 6 | - ${DB_PORT}:5432 7 | environment: 8 | POSTGRES_USER: ${DB_USER} 9 | POSTGRES_PASSWORD: ${DB_PASSWORD} 10 | volumes: 11 | - postgres:/var/lib/postgresql/data 12 | volumes: 13 | postgres: ~ 14 | -------------------------------------------------------------------------------- /server/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | server: 4 | build: 5 | context: . 6 | args: 7 | NPM_LOG_LEVEL: notice 8 | ports: 9 | - ${PORT}:3000 10 | environment: 11 | BCRYPT_SALT: ${BCRYPT_SALT} 12 | JWT_SECRET_KEY: ${JWT_SECRET_KEY} 13 | JWT_EXPIRATION: ${JWT_EXPIRATION} 14 | DB_URL: postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME} 15 | depends_on: 16 | - migrate 17 | migrate: 18 | build: 19 | context: . 20 | args: 21 | NPM_LOG_LEVEL: notice 22 | command: npm run db:init 23 | working_dir: /app/server 24 | environment: 25 | BCRYPT_SALT: ${BCRYPT_SALT} 26 | DB_URL: postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME} 27 | depends_on: 28 | db: 29 | condition: service_healthy 30 | db: 31 | image: postgres:12 32 | ports: 33 | - ${DB_PORT}:5432 34 | environment: 35 | POSTGRES_USER: ${DB_USER} 36 | POSTGRES_PASSWORD: ${DB_PASSWORD} 37 | POSTGRES_DB: ${DB_NAME} 38 | volumes: 39 | - postgres:/var/lib/postgresql/data 40 | healthcheck: 41 | test: 42 | - CMD-SHELL 43 | - pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER} 44 | timeout: 45s 45 | interval: 10s 46 | retries: 10 47 | volumes: 48 | postgres: ~ 49 | -------------------------------------------------------------------------------- /server/nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "sourceRoot": "src", 3 | "compilerOptions": { 4 | "assets": ["swagger"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /server/scripts/customSeed.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from "@prisma/client"; 2 | 3 | export async function customSeed() { 4 | const client = new PrismaClient(); 5 | const username = "admin"; 6 | 7 | //replace this sample code to populate your database 8 | //with data that is required for your service to start 9 | await client.user.update({ 10 | where: { username: username }, 11 | data: { 12 | username, 13 | }, 14 | }); 15 | 16 | client.$disconnect(); 17 | } 18 | -------------------------------------------------------------------------------- /server/scripts/seed.ts: -------------------------------------------------------------------------------- 1 | import * as dotenv from "dotenv"; 2 | import { PrismaClient } from "@prisma/client"; 3 | import { customSeed } from "./customSeed"; 4 | import { Salt, parseSalt } from "../src/auth/password.service"; 5 | import { hash } from "bcrypt"; 6 | 7 | if (require.main === module) { 8 | dotenv.config(); 9 | 10 | const { BCRYPT_SALT } = process.env; 11 | 12 | if (!BCRYPT_SALT) { 13 | throw new Error("BCRYPT_SALT environment variable must be defined"); 14 | } 15 | const salt = parseSalt(BCRYPT_SALT); 16 | 17 | seed(salt).catch((error) => { 18 | console.error(error); 19 | process.exit(1); 20 | }); 21 | } 22 | 23 | async function seed(bcryptSalt: Salt) { 24 | console.info("Seeding database..."); 25 | 26 | const client = new PrismaClient(); 27 | 28 | const data = { 29 | username: "admin", 30 | password: await hash("admin", bcryptSalt), 31 | roles: ["user"], 32 | }; 33 | 34 | await client.user.upsert({ 35 | where: { 36 | username: data.username, 37 | }, 38 | 39 | update: {}, 40 | create: data, 41 | }); 42 | 43 | void client.$disconnect(); 44 | 45 | console.info("Seeding database with custom seed..."); 46 | customSeed(); 47 | 48 | console.info("Seeded database successfully"); 49 | } 50 | -------------------------------------------------------------------------------- /server/src/auth/Credentials.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from "@nestjs/swagger"; 2 | import { InputType, Field } from "@nestjs/graphql"; 3 | import { IsString } from "class-validator"; 4 | 5 | @InputType() 6 | export class Credentials { 7 | @ApiProperty({ 8 | required: true, 9 | type: String, 10 | }) 11 | @IsString() 12 | @Field(() => String, { nullable: false }) 13 | username!: string; 14 | @ApiProperty({ 15 | required: true, 16 | type: String, 17 | }) 18 | @IsString() 19 | @Field(() => String, { nullable: false }) 20 | password!: string; 21 | } 22 | 23 | @InputType() 24 | export class SignupCredentials { 25 | @ApiProperty({ 26 | required: true, 27 | type: String, 28 | }) 29 | @IsString() 30 | @Field(() => String, { nullable: false }) 31 | username!: string; 32 | @ApiProperty({ 33 | required: true, 34 | type: String, 35 | }) 36 | @IsString() 37 | @Field(() => String, { nullable: false }) 38 | password!: string; 39 | @ApiProperty({ 40 | required: true, 41 | type: String, 42 | }) 43 | @IsString() 44 | @Field(() => String, { nullable: false }) 45 | firstName!: string; 46 | @ApiProperty({ 47 | required: true, 48 | type: String, 49 | }) 50 | @IsString() 51 | @Field(() => String, { nullable: false }) 52 | lastName!: string; 53 | } 54 | 55 | @InputType() 56 | export class CheckUserValues { 57 | @ApiProperty({ 58 | required: true, 59 | type: String, 60 | }) 61 | @IsString() 62 | @Field(() => String, { nullable: false }) 63 | email!: string; 64 | } 65 | -------------------------------------------------------------------------------- /server/src/auth/IAuthStrategy.ts: -------------------------------------------------------------------------------- 1 | import { UserInfo } from "./UserInfo"; 2 | 3 | export interface IAuthStrategy { 4 | validate: (...any: any) => Promise<UserInfo>; 5 | } 6 | -------------------------------------------------------------------------------- /server/src/auth/ITokenService.ts: -------------------------------------------------------------------------------- 1 | export interface ITokenPayload { 2 | id: string; 3 | username: string; 4 | password: string; 5 | } 6 | 7 | export interface ITokenService { 8 | createToken: ({ id, username, password }: ITokenPayload) => Promise<string>; 9 | } 10 | -------------------------------------------------------------------------------- /server/src/auth/LoginArgs.ts: -------------------------------------------------------------------------------- 1 | import { ArgsType, Field } from "@nestjs/graphql"; 2 | import { CheckUserValues, Credentials, SignupCredentials } from "./Credentials"; 3 | 4 | @ArgsType() 5 | export class LoginArgs { 6 | @Field(() => Credentials, { nullable: false }) 7 | credentials!: Credentials; 8 | } 9 | 10 | @ArgsType() 11 | export class SignupArgs { 12 | @Field(() => Credentials, { nullable: false }) 13 | credentials!: SignupCredentials; 14 | } 15 | 16 | @ArgsType() 17 | export class CheckUserArgs { 18 | @Field(() => CheckUserValues, { nullable: false }) 19 | CheckUserValues!: CheckUserValues; 20 | } 21 | -------------------------------------------------------------------------------- /server/src/auth/UserInfo.ts: -------------------------------------------------------------------------------- 1 | import { Field, ObjectType } from "@nestjs/graphql"; 2 | import { User } from "../user/base/User"; 3 | 4 | @ObjectType() 5 | export class UserInfo implements Partial<User> { 6 | @Field(() => String) 7 | id!: string; 8 | @Field(() => String) 9 | username!: string; 10 | @Field(() => [String]) 11 | roles!: string[]; 12 | @Field(() => String, { nullable: true }) 13 | accessToken?: string; 14 | } 15 | -------------------------------------------------------------------------------- /server/src/auth/abac.util.ts: -------------------------------------------------------------------------------- 1 | import { Permission } from "accesscontrol"; 2 | 3 | /** 4 | * @returns attributes not allowed to appear on given data according to given 5 | * attributeMatchers 6 | */ 7 | export function getInvalidAttributes( 8 | permission: Permission, 9 | // eslint-disable-next-line @typescript-eslint/ban-types 10 | data: Object 11 | ): string[] { 12 | const filteredData = permission.filter(data); 13 | return Object.keys(data).filter((key) => !(key in filteredData)); 14 | } 15 | -------------------------------------------------------------------------------- /server/src/auth/acl.module.ts: -------------------------------------------------------------------------------- 1 | import { AccessControlModule, RolesBuilder } from "nest-access-control"; 2 | // @ts-ignore 3 | // eslint-disable-next-line import/no-unresolved 4 | import grants from "../grants.json"; 5 | 6 | // eslint-disable-next-line @typescript-eslint/naming-convention 7 | export const ACLModule = AccessControlModule.forRoles(new RolesBuilder(grants)); 8 | -------------------------------------------------------------------------------- /server/src/auth/auth.controller.ts: -------------------------------------------------------------------------------- 1 | import { Body, Controller, Get, Post, Req } from "@nestjs/common"; 2 | import { ApiBearerAuth, ApiOkResponse, ApiTags } from "@nestjs/swagger"; 3 | import { AuthService } from "./auth.service"; 4 | import { CheckUserValues, Credentials, SignupCredentials } from "./Credentials"; 5 | import { UserInfo } from "./UserInfo"; 6 | import { User } from "src/user/base/User"; 7 | import { Request } from "express"; 8 | 9 | @ApiTags("auth") 10 | @Controller() 11 | export class AuthController { 12 | constructor(private readonly authService: AuthService) {} 13 | @ApiBearerAuth() 14 | @ApiOkResponse({ type: User }) 15 | @Get("me") 16 | async me(@Req() request: Request): Promise<User> { 17 | return this.authService.me(request.headers.authorization); 18 | } 19 | @Post("check-user") 20 | async checkUser(@Body() body: CheckUserValues): Promise<User> { 21 | return this.authService.checkUser(body.email); 22 | } 23 | @Post("login") 24 | async login(@Body() body: Credentials): Promise<UserInfo> { 25 | return this.authService.login(body); 26 | } 27 | @Post("signup") 28 | async signup(@Body() body: SignupCredentials): Promise<UserInfo> { 29 | return this.authService.signup(body); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /server/src/auth/auth.resolver.ts: -------------------------------------------------------------------------------- 1 | import * as common from "@nestjs/common"; 2 | 3 | import * as gqlACGuard from "../auth/gqlAC.guard"; 4 | import { AuthService } from "./auth.service"; 5 | import { GqlDefaultAuthGuard } from "./gqlDefaultAuth.guard"; 6 | import { UserData } from "./userData.decorator"; 7 | import { CheckUserArgs, LoginArgs, SignupArgs } from "./LoginArgs"; 8 | import { UserInfo } from "./UserInfo"; 9 | import { User } from "src/user/base/User"; 10 | import { Args, Mutation, Query, Resolver, Context } from "@nestjs/graphql"; 11 | import { Request } from "express"; 12 | 13 | @Resolver(UserInfo) 14 | export class AuthResolver { 15 | constructor(private readonly authService: AuthService) {} 16 | @Mutation(() => UserInfo) 17 | async login(@Args() args: LoginArgs): Promise<UserInfo> { 18 | return this.authService.login(args.credentials); 19 | } 20 | @Mutation(() => UserInfo) 21 | async signup(@Args() args: SignupArgs): Promise<UserInfo> { 22 | return this.authService.signup(args.credentials); 23 | } 24 | @Query(() => User) 25 | async me(@Context("req") request: Request): Promise<User> { 26 | return this.authService.me(request.headers.authorization); 27 | } 28 | @Query(() => User) 29 | async checkUser(@Args() args: CheckUserArgs): Promise<User> { 30 | return this.authService.checkUser(args.CheckUserValues.email); 31 | } 32 | 33 | @Query(() => UserInfo) 34 | @common.UseGuards(GqlDefaultAuthGuard, gqlACGuard.GqlACGuard) 35 | async userInfo(@UserData() userInfo: UserInfo): Promise<UserInfo> { 36 | return userInfo; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /server/src/auth/base/token.service.base.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-unresolved */ 2 | import { Injectable } from "@nestjs/common"; 3 | import { JwtService } from "@nestjs/jwt"; 4 | import { INVALID_PASSWORD_ERROR, INVALID_USERNAME_ERROR } from "../constants"; 5 | import { ITokenService, ITokenPayload } from "../ITokenService"; 6 | /** 7 | * TokenServiceBase is a jwt bearer implementation of ITokenService 8 | */ 9 | @Injectable() 10 | export class TokenServiceBase implements ITokenService { 11 | constructor(protected readonly jwtService: JwtService) {} 12 | /** 13 | * 14 | * @object { id: String, username: String, password: String} 15 | * @returns a jwt token sign with the username and user id 16 | */ 17 | createToken({ id, username, password }: ITokenPayload): Promise<string> { 18 | if (!username) return Promise.reject(INVALID_USERNAME_ERROR); 19 | if (!password) return Promise.reject(INVALID_PASSWORD_ERROR); 20 | return this.jwtService.signAsync({ 21 | sub: id, 22 | username, 23 | }); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /server/src/auth/constants.ts: -------------------------------------------------------------------------------- 1 | export const INVALID_USERNAME_ERROR = "Invalid username"; 2 | export const INVALID_PASSWORD_ERROR = "Invalid password"; 3 | -------------------------------------------------------------------------------- /server/src/auth/defaultAuth.guard.ts: -------------------------------------------------------------------------------- 1 | import { Observable } from "rxjs"; 2 | import { ExecutionContext, Injectable } from "@nestjs/common"; 3 | import { Reflector } from "@nestjs/core"; 4 | import { IS_PUBLIC_KEY } from "../decorators/public.decorator"; 5 | import { JwtAuthGuard } from "./jwt/jwtAuth.guard"; 6 | 7 | @Injectable() 8 | export class DefaultAuthGuard extends JwtAuthGuard { 9 | constructor(private readonly reflector: Reflector) { 10 | super(); 11 | } 12 | 13 | canActivate( 14 | context: ExecutionContext 15 | ): boolean | Promise<boolean> | Observable<any> { 16 | const isPublic = this.reflector.get<boolean>( 17 | IS_PUBLIC_KEY, 18 | context.getHandler() 19 | ); 20 | 21 | if (isPublic) { 22 | return true; 23 | } 24 | 25 | return super.canActivate(context); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /server/src/auth/gqlAC.guard.ts: -------------------------------------------------------------------------------- 1 | import { ExecutionContext } from "@nestjs/common"; 2 | import { GqlExecutionContext } from "@nestjs/graphql"; 3 | import { ACGuard } from "nest-access-control"; 4 | 5 | export class GqlACGuard<User extends any = any> extends ACGuard<User> { 6 | async getUser(context: ExecutionContext): Promise<User> { 7 | const ctx = GqlExecutionContext.create(context); 8 | const request = ctx.getContext<{ req: { user: User } }>().req; 9 | return request.user; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /server/src/auth/gqlDefaultAuth.guard.ts: -------------------------------------------------------------------------------- 1 | import { ExecutionContext } from "@nestjs/common"; 2 | import { GqlExecutionContext } from "@nestjs/graphql"; 3 | import type { Request } from "express"; 4 | // @ts-ignore 5 | // eslint-disable-next-line 6 | import { DefaultAuthGuard } from "./defaultAuth.guard"; 7 | 8 | export class GqlDefaultAuthGuard extends DefaultAuthGuard { 9 | // This method is required for the interface - do not delete it. 10 | getRequest(context: ExecutionContext): Request { 11 | const ctx = GqlExecutionContext.create(context); 12 | return ctx.getContext<{ req: Request }>().req; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /server/src/auth/gqlUserRoles.decorator.ts: -------------------------------------------------------------------------------- 1 | import { createParamDecorator, ExecutionContext } from "@nestjs/common"; 2 | import { GqlExecutionContext } from "@nestjs/graphql"; 3 | 4 | /** 5 | * Access the user roles from the request object i.e `req.user.roles`. 6 | * 7 | * You can pass an optional property key to the decorator to get it from the user object 8 | * e.g `@UserRoles('permissions')` will return the `req.user.permissions` instead. 9 | */ 10 | export const UserRoles = createParamDecorator( 11 | (data: string, context: ExecutionContext) => { 12 | const ctx = GqlExecutionContext.create(context); 13 | const request = ctx.getContext<{ req: { user: any } }>().req; 14 | if (!request.user) { 15 | return null; 16 | } 17 | return data ? request.user[data] : request.user.roles; 18 | } 19 | ); 20 | -------------------------------------------------------------------------------- /server/src/auth/jwt/base/jwt.strategy.base.ts: -------------------------------------------------------------------------------- 1 | import { UnauthorizedException } from "@nestjs/common"; 2 | import { PassportStrategy } from "@nestjs/passport"; 3 | import { ExtractJwt, Strategy } from "passport-jwt"; 4 | import { IAuthStrategy } from "../../IAuthStrategy"; 5 | import { UserService } from "../../../user/user.service"; 6 | import { UserInfo } from "../../UserInfo"; 7 | 8 | export class JwtStrategyBase 9 | extends PassportStrategy(Strategy) 10 | implements IAuthStrategy 11 | { 12 | constructor( 13 | protected readonly userService: UserService, 14 | protected readonly secretOrKey: string 15 | ) { 16 | super({ 17 | jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), 18 | ignoreExpiration: false, 19 | secretOrKey, 20 | }); 21 | } 22 | 23 | async validate(payload: UserInfo): Promise<UserInfo> { 24 | const { username } = payload; 25 | const user = await this.userService.findOne({ 26 | where: { username }, 27 | }); 28 | if (!user) { 29 | throw new UnauthorizedException(); 30 | } 31 | if ( 32 | !Array.isArray(user.roles) || 33 | typeof user.roles !== "object" || 34 | user.roles === null 35 | ) { 36 | throw new Error("User roles is not a valid value"); 37 | } 38 | return { ...user, roles: user.roles as string[] }; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /server/src/auth/jwt/jwt.strategy.ts: -------------------------------------------------------------------------------- 1 | import { Inject, Injectable } from "@nestjs/common"; 2 | import { JWT_SECRET_KEY } from "../../constants"; 3 | import { UserService } from "../../user/user.service"; 4 | import { JwtStrategyBase } from "./base/jwt.strategy.base"; 5 | 6 | @Injectable() 7 | export class JwtStrategy extends JwtStrategyBase { 8 | constructor( 9 | protected readonly userService: UserService, 10 | @Inject(JWT_SECRET_KEY) secretOrKey: string 11 | ) { 12 | super(userService, secretOrKey); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /server/src/auth/jwt/jwtAuth.guard.ts: -------------------------------------------------------------------------------- 1 | import { AuthGuard } from "@nestjs/passport"; 2 | 3 | export class JwtAuthGuard extends AuthGuard("jwt") {} 4 | -------------------------------------------------------------------------------- /server/src/auth/jwt/jwtSecretFactory.ts: -------------------------------------------------------------------------------- 1 | import { JWT_SECRET_KEY } from "../../constants"; 2 | import { SecretsManagerService } from "../../providers/secrets/secretsManager.service"; 3 | 4 | export const jwtSecretFactory = { 5 | provide: JWT_SECRET_KEY, 6 | useFactory: async ( 7 | secretsService: SecretsManagerService 8 | ): Promise<string> => { 9 | const secret = await secretsService.getSecret<string>(JWT_SECRET_KEY); 10 | if (secret) { 11 | return secret; 12 | } 13 | throw new Error("jwtSecretFactory missing secret"); 14 | }, 15 | inject: [SecretsManagerService], 16 | }; 17 | -------------------------------------------------------------------------------- /server/src/auth/token.service.ts: -------------------------------------------------------------------------------- 1 | //@ts-ignore 2 | import { ITokenService } from "./ITokenService"; 3 | // eslint-disable-next-line import/no-unresolved 4 | //@ts-ignore 5 | import { TokenServiceBase } from "./base/token.service.base"; 6 | 7 | export class TokenService extends TokenServiceBase implements ITokenService { 8 | /** 9 | * @param bearer 10 | * @returns the username from a jwt token 11 | */ 12 | decodeToken(bearer: string): string { 13 | return this.jwtService.verify(bearer).username; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /server/src/auth/userData.decorator.ts: -------------------------------------------------------------------------------- 1 | import { createParamDecorator, ExecutionContext } from "@nestjs/common"; 2 | import { GqlContextType, GqlExecutionContext } from "@nestjs/graphql"; 3 | import { User } from "@prisma/client"; 4 | 5 | /** 6 | * Access the user data from the request object i.e `req.user`. 7 | */ 8 | function userFactory(ctx: ExecutionContext): User { 9 | const contextType = ctx.getType(); 10 | if (contextType === "http") { 11 | // do something that is only important in the context of regular HTTP requests (REST) 12 | const { user } = ctx.switchToHttp().getRequest(); 13 | return user; 14 | } else if (contextType === "rpc") { 15 | // do something that is only important in the context of Microservice requests 16 | throw new Error("Rpc context is not implemented yet"); 17 | } else if (contextType === "ws") { 18 | // do something that is only important in the context of Websockets requests 19 | throw new Error("Websockets context is not implemented yet"); 20 | } else if (ctx.getType<GqlContextType>() === "graphql") { 21 | // do something that is only important in the context of GraphQL requests 22 | const gqlExecutionContext = GqlExecutionContext.create(ctx); 23 | return gqlExecutionContext.getContext().req.user; 24 | } 25 | throw new Error("Invalid context"); 26 | } 27 | 28 | export const UserData = createParamDecorator<undefined, ExecutionContext, User>( 29 | (data, ctx: ExecutionContext) => userFactory(ctx) 30 | ); 31 | -------------------------------------------------------------------------------- /server/src/constants.ts: -------------------------------------------------------------------------------- 1 | export const JWT_SECRET_KEY = "JWT_SECRET_KEY"; 2 | export const JWT_EXPIRATION = "JWT_EXPIRATION"; 3 | -------------------------------------------------------------------------------- /server/src/decorators/public.decorator.ts: -------------------------------------------------------------------------------- 1 | import { applyDecorators, SetMetadata } from "@nestjs/common"; 2 | 3 | export const IS_PUBLIC_KEY = "isPublic"; 4 | 5 | const PublicAuthMiddleware = SetMetadata(IS_PUBLIC_KEY, true); 6 | const PublicAuthSwagger = SetMetadata("swagger/apiSecurity", ["isPublic"]); 7 | 8 | // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types 9 | export const Public = () => 10 | applyDecorators(PublicAuthMiddleware, PublicAuthSwagger); 11 | -------------------------------------------------------------------------------- /server/src/errors.ts: -------------------------------------------------------------------------------- 1 | import * as common from "@nestjs/common"; 2 | import { ApiProperty } from "@nestjs/swagger"; 3 | 4 | export class ForbiddenException extends common.ForbiddenException { 5 | @ApiProperty() 6 | statusCode!: number; 7 | @ApiProperty() 8 | message!: string; 9 | } 10 | 11 | export class NotFoundException extends common.NotFoundException { 12 | @ApiProperty() 13 | statusCode!: number; 14 | @ApiProperty() 15 | message!: string; 16 | } 17 | -------------------------------------------------------------------------------- /server/src/health/base/health.controller.base.ts: -------------------------------------------------------------------------------- 1 | import { Get, HttpStatus, Res } from "@nestjs/common"; 2 | import { Response } from "express"; 3 | import { HealthService } from "../health.service"; 4 | 5 | export class HealthControllerBase { 6 | constructor(protected readonly healthService: HealthService) {} 7 | @Get("live") 8 | healthLive(@Res() response: Response): Response<void> { 9 | return response.status(HttpStatus.NO_CONTENT).send(); 10 | } 11 | @Get("ready") 12 | async healthReady(@Res() response: Response): Promise<Response<void>> { 13 | const dbConnection = await this.healthService.isDbReady(); 14 | if (!dbConnection) { 15 | return response.status(HttpStatus.NOT_FOUND).send(); 16 | } 17 | return response.status(HttpStatus.NO_CONTENT).send(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/src/health/base/health.service.base.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@nestjs/common"; 2 | import { PrismaService } from "../../prisma/prisma.service"; 3 | 4 | @Injectable() 5 | export class HealthServiceBase { 6 | constructor(protected readonly prisma: PrismaService) {} 7 | async isDbReady(): Promise<boolean> { 8 | try { 9 | await this.prisma.$queryRaw`SELECT 1`; 10 | return true; 11 | } catch (error) { 12 | return false; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /server/src/health/health.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from "@nestjs/common"; 2 | import { HealthControllerBase } from "./base/health.controller.base"; 3 | import { HealthService } from "./health.service"; 4 | 5 | @Controller("_health") 6 | export class HealthController extends HealthControllerBase { 7 | constructor(protected readonly healthService: HealthService) { 8 | super(healthService); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /server/src/health/health.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from "@nestjs/common"; 2 | import { HealthController } from "./health.controller"; 3 | import { HealthService } from "./health.service"; 4 | 5 | @Module({ 6 | controllers: [HealthController], 7 | providers: [HealthService], 8 | exports: [HealthService], 9 | }) 10 | export class HealthModule {} 11 | -------------------------------------------------------------------------------- /server/src/health/health.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@nestjs/common"; 2 | import { PrismaService } from "../prisma/prisma.service"; 3 | import { HealthServiceBase } from "./base/health.service.base"; 4 | 5 | @Injectable() 6 | export class HealthService extends HealthServiceBase { 7 | constructor(protected readonly prisma: PrismaService) { 8 | super(prisma); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /server/src/interceptors/aclFilterResponse.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CallHandler, 3 | ExecutionContext, 4 | Injectable, 5 | NestInterceptor, 6 | } from "@nestjs/common"; 7 | import { Observable } from "rxjs"; 8 | import { map } from "rxjs/operators"; 9 | import { InjectRolesBuilder, RolesBuilder } from "nest-access-control"; 10 | import { Reflector } from "@nestjs/core"; 11 | 12 | @Injectable() 13 | export class AclFilterResponseInterceptor implements NestInterceptor { 14 | constructor( 15 | @InjectRolesBuilder() private readonly rolesBuilder: RolesBuilder, 16 | private readonly reflector: Reflector 17 | ) {} 18 | 19 | intercept(context: ExecutionContext, next: CallHandler): Observable<any> { 20 | const [permissionsRoles]: any = this.reflector.getAllAndMerge<string[]>( 21 | "roles", 22 | [context.getHandler(), context.getClass()] 23 | ); 24 | 25 | const permission = this.rolesBuilder.permission({ 26 | role: permissionsRoles.role, 27 | action: permissionsRoles.action, 28 | possession: permissionsRoles.possession, 29 | resource: permissionsRoles.resource, 30 | }); 31 | 32 | return next.handle().pipe( 33 | map((data) => { 34 | if (Array.isArray(data)) { 35 | return data.map((results: any) => permission.filter(results)); 36 | } else { 37 | return permission.filter(data); 38 | } 39 | }) 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /server/src/listing/base/CreateListingArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { ListingCreateInput } from "./ListingCreateInput"; 15 | import { ValidateNested } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | 18 | @ArgsType() 19 | class CreateListingArgs { 20 | @ApiProperty({ 21 | required: true, 22 | type: () => ListingCreateInput, 23 | }) 24 | @ValidateNested() 25 | @Type(() => ListingCreateInput) 26 | @Field(() => ListingCreateInput, { nullable: false }) 27 | data!: ListingCreateInput; 28 | } 29 | 30 | export { CreateListingArgs as CreateListingArgs }; 31 | -------------------------------------------------------------------------------- /server/src/listing/base/DeleteListingArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { ListingWhereUniqueInput } from "./ListingWhereUniqueInput"; 15 | import { ValidateNested } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | 18 | @ArgsType() 19 | class DeleteListingArgs { 20 | @ApiProperty({ 21 | required: true, 22 | type: () => ListingWhereUniqueInput, 23 | }) 24 | @ValidateNested() 25 | @Type(() => ListingWhereUniqueInput) 26 | @Field(() => ListingWhereUniqueInput, { nullable: false }) 27 | where!: ListingWhereUniqueInput; 28 | } 29 | 30 | export { DeleteListingArgs as DeleteListingArgs }; 31 | -------------------------------------------------------------------------------- /server/src/listing/base/ListingCountArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { ListingWhereInput } from "./ListingWhereInput"; 15 | import { Type } from "class-transformer"; 16 | 17 | @ArgsType() 18 | class ListingCountArgs { 19 | @ApiProperty({ 20 | required: false, 21 | type: () => ListingWhereInput, 22 | }) 23 | @Field(() => ListingWhereInput, { nullable: true }) 24 | @Type(() => ListingWhereInput) 25 | where?: ListingWhereInput; 26 | } 27 | 28 | export { ListingCountArgs as ListingCountArgs }; 29 | -------------------------------------------------------------------------------- /server/src/listing/base/ListingFindUniqueArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { ListingWhereUniqueInput } from "./ListingWhereUniqueInput"; 15 | import { ValidateNested } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | 18 | @ArgsType() 19 | class ListingFindUniqueArgs { 20 | @ApiProperty({ 21 | required: true, 22 | type: () => ListingWhereUniqueInput, 23 | }) 24 | @ValidateNested() 25 | @Type(() => ListingWhereUniqueInput) 26 | @Field(() => ListingWhereUniqueInput, { nullable: false }) 27 | where!: ListingWhereUniqueInput; 28 | } 29 | 30 | export { ListingFindUniqueArgs as ListingFindUniqueArgs }; 31 | -------------------------------------------------------------------------------- /server/src/listing/base/ListingWhereUniqueInput.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { InputType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { IsString } from "class-validator"; 15 | 16 | @InputType() 17 | class ListingWhereUniqueInput { 18 | @ApiProperty({ 19 | required: true, 20 | type: String, 21 | }) 22 | @IsString() 23 | @Field(() => String) 24 | id!: string; 25 | } 26 | 27 | export { ListingWhereUniqueInput as ListingWhereUniqueInput }; 28 | -------------------------------------------------------------------------------- /server/src/listing/base/TripCreateNestedManyWithoutListingsInput.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { InputType, Field } from "@nestjs/graphql"; 13 | import { TripWhereUniqueInput } from "../../trip/base/TripWhereUniqueInput"; 14 | import { ApiProperty } from "@nestjs/swagger"; 15 | 16 | @InputType() 17 | class TripCreateNestedManyWithoutListingsInput { 18 | @Field(() => [TripWhereUniqueInput], { 19 | nullable: true, 20 | }) 21 | @ApiProperty({ 22 | required: false, 23 | type: () => [TripWhereUniqueInput], 24 | }) 25 | connect?: Array<TripWhereUniqueInput>; 26 | } 27 | 28 | export { TripCreateNestedManyWithoutListingsInput as TripCreateNestedManyWithoutListingsInput }; 29 | -------------------------------------------------------------------------------- /server/src/listing/base/TripUpdateManyWithoutListingsInput.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { InputType, Field } from "@nestjs/graphql"; 13 | import { TripWhereUniqueInput } from "../../trip/base/TripWhereUniqueInput"; 14 | import { ApiProperty } from "@nestjs/swagger"; 15 | 16 | @InputType() 17 | class TripUpdateManyWithoutListingsInput { 18 | @Field(() => [TripWhereUniqueInput], { 19 | nullable: true, 20 | }) 21 | @ApiProperty({ 22 | required: false, 23 | type: () => [TripWhereUniqueInput], 24 | }) 25 | connect?: Array<TripWhereUniqueInput>; 26 | 27 | @Field(() => [TripWhereUniqueInput], { 28 | nullable: true, 29 | }) 30 | @ApiProperty({ 31 | required: false, 32 | type: () => [TripWhereUniqueInput], 33 | }) 34 | disconnect?: Array<TripWhereUniqueInput>; 35 | 36 | @Field(() => [TripWhereUniqueInput], { 37 | nullable: true, 38 | }) 39 | @ApiProperty({ 40 | required: false, 41 | type: () => [TripWhereUniqueInput], 42 | }) 43 | set?: Array<TripWhereUniqueInput>; 44 | } 45 | 46 | export { TripUpdateManyWithoutListingsInput as TripUpdateManyWithoutListingsInput }; 47 | -------------------------------------------------------------------------------- /server/src/listing/base/UpdateListingArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { ListingWhereUniqueInput } from "./ListingWhereUniqueInput"; 15 | import { ValidateNested } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | import { ListingUpdateInput } from "./ListingUpdateInput"; 18 | 19 | @ArgsType() 20 | class UpdateListingArgs { 21 | @ApiProperty({ 22 | required: true, 23 | type: () => ListingWhereUniqueInput, 24 | }) 25 | @ValidateNested() 26 | @Type(() => ListingWhereUniqueInput) 27 | @Field(() => ListingWhereUniqueInput, { nullable: false }) 28 | where!: ListingWhereUniqueInput; 29 | 30 | @ApiProperty({ 31 | required: true, 32 | type: () => ListingUpdateInput, 33 | }) 34 | @ValidateNested() 35 | @Type(() => ListingUpdateInput) 36 | @Field(() => ListingUpdateInput, { nullable: false }) 37 | data!: ListingUpdateInput; 38 | } 39 | 40 | export { UpdateListingArgs as UpdateListingArgs }; 41 | -------------------------------------------------------------------------------- /server/src/listing/base/WishlistCreateNestedManyWithoutListingsInput.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { InputType, Field } from "@nestjs/graphql"; 13 | import { WishlistWhereUniqueInput } from "../../wishlist/base/WishlistWhereUniqueInput"; 14 | import { ApiProperty } from "@nestjs/swagger"; 15 | 16 | @InputType() 17 | class WishlistCreateNestedManyWithoutListingsInput { 18 | @Field(() => [WishlistWhereUniqueInput], { 19 | nullable: true, 20 | }) 21 | @ApiProperty({ 22 | required: false, 23 | type: () => [WishlistWhereUniqueInput], 24 | }) 25 | connect?: Array<WishlistWhereUniqueInput>; 26 | } 27 | 28 | export { WishlistCreateNestedManyWithoutListingsInput as WishlistCreateNestedManyWithoutListingsInput }; 29 | -------------------------------------------------------------------------------- /server/src/listing/base/WishlistUpdateManyWithoutListingsInput.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { InputType, Field } from "@nestjs/graphql"; 13 | import { WishlistWhereUniqueInput } from "../../wishlist/base/WishlistWhereUniqueInput"; 14 | import { ApiProperty } from "@nestjs/swagger"; 15 | 16 | @InputType() 17 | class WishlistUpdateManyWithoutListingsInput { 18 | @Field(() => [WishlistWhereUniqueInput], { 19 | nullable: true, 20 | }) 21 | @ApiProperty({ 22 | required: false, 23 | type: () => [WishlistWhereUniqueInput], 24 | }) 25 | connect?: Array<WishlistWhereUniqueInput>; 26 | 27 | @Field(() => [WishlistWhereUniqueInput], { 28 | nullable: true, 29 | }) 30 | @ApiProperty({ 31 | required: false, 32 | type: () => [WishlistWhereUniqueInput], 33 | }) 34 | disconnect?: Array<WishlistWhereUniqueInput>; 35 | 36 | @Field(() => [WishlistWhereUniqueInput], { 37 | nullable: true, 38 | }) 39 | @ApiProperty({ 40 | required: false, 41 | type: () => [WishlistWhereUniqueInput], 42 | }) 43 | set?: Array<WishlistWhereUniqueInput>; 44 | } 45 | 46 | export { WishlistUpdateManyWithoutListingsInput as WishlistUpdateManyWithoutListingsInput }; 47 | -------------------------------------------------------------------------------- /server/src/listing/base/listing.module.base.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { Module, forwardRef } from "@nestjs/common"; 13 | import { MorganModule } from "nest-morgan"; 14 | import { ACLModule } from "../../auth/acl.module"; 15 | import { AuthModule } from "../../auth/auth.module"; 16 | @Module({ 17 | imports: [ACLModule, forwardRef(() => AuthModule), MorganModule], 18 | exports: [ACLModule, AuthModule, MorganModule], 19 | }) 20 | export class ListingModuleBase {} 21 | -------------------------------------------------------------------------------- /server/src/listing/listing.controller.ts: -------------------------------------------------------------------------------- 1 | import * as common from "@nestjs/common"; 2 | import * as swagger from "@nestjs/swagger"; 3 | import * as nestAccessControl from "nest-access-control"; 4 | import { ListingService } from "./listing.service"; 5 | import { ListingControllerBase } from "./base/listing.controller.base"; 6 | 7 | @swagger.ApiTags("listings") 8 | @common.Controller("listings") 9 | export class ListingController extends ListingControllerBase { 10 | constructor( 11 | protected readonly service: ListingService, 12 | @nestAccessControl.InjectRolesBuilder() 13 | protected readonly rolesBuilder: nestAccessControl.RolesBuilder 14 | ) { 15 | super(service, rolesBuilder); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /server/src/listing/listing.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from "@nestjs/common"; 2 | import { ListingModuleBase } from "./base/listing.module.base"; 3 | import { ListingService } from "./listing.service"; 4 | import { ListingController } from "./listing.controller"; 5 | import { ListingResolver } from "./listing.resolver"; 6 | 7 | @Module({ 8 | imports: [ListingModuleBase], 9 | controllers: [ListingController], 10 | providers: [ListingService, ListingResolver], 11 | exports: [ListingService], 12 | }) 13 | export class ListingModule {} 14 | -------------------------------------------------------------------------------- /server/src/listing/listing.resolver.ts: -------------------------------------------------------------------------------- 1 | import * as graphql from "@nestjs/graphql"; 2 | import * as nestAccessControl from "nest-access-control"; 3 | import * as gqlACGuard from "../auth/gqlAC.guard"; 4 | import { GqlDefaultAuthGuard } from "../auth/gqlDefaultAuth.guard"; 5 | import * as common from "@nestjs/common"; 6 | import { ListingResolverBase } from "./base/listing.resolver.base"; 7 | import { Listing } from "./base/Listing"; 8 | import { ListingService } from "./listing.service"; 9 | 10 | @common.UseGuards(GqlDefaultAuthGuard, gqlACGuard.GqlACGuard) 11 | @graphql.Resolver(() => Listing) 12 | export class ListingResolver extends ListingResolverBase { 13 | constructor( 14 | protected readonly service: ListingService, 15 | @nestAccessControl.InjectRolesBuilder() 16 | protected readonly rolesBuilder: nestAccessControl.RolesBuilder 17 | ) { 18 | super(service, rolesBuilder); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /server/src/listing/listing.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@nestjs/common"; 2 | import { PrismaService } from "../prisma/prisma.service"; 3 | import { ListingServiceBase } from "./base/listing.service.base"; 4 | 5 | @Injectable() 6 | export class ListingService extends ListingServiceBase { 7 | constructor(protected readonly prisma: PrismaService) { 8 | super(prisma); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /server/src/prisma.util.spec.ts: -------------------------------------------------------------------------------- 1 | import { 2 | isRecordNotFoundError, 3 | PRISMA_QUERY_INTERPRETATION_ERROR, 4 | } from "./prisma.util"; 5 | 6 | describe("isRecordNotFoundError", () => { 7 | test("returns true for record not found error", () => { 8 | expect( 9 | isRecordNotFoundError( 10 | Object.assign( 11 | new Error(`Error occurred during query execution: 12 | InterpretationError("Error for binding '0': RecordNotFound("Record to update not found.")")`), 13 | { 14 | code: PRISMA_QUERY_INTERPRETATION_ERROR, 15 | } 16 | ) 17 | ) 18 | ).toBe(true); 19 | }); 20 | test("returns false for any other error", () => { 21 | expect(isRecordNotFoundError(new Error())).toBe(false); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /server/src/prisma.util.ts: -------------------------------------------------------------------------------- 1 | export const PRISMA_QUERY_INTERPRETATION_ERROR = "P2016"; 2 | export const PRISMA_RECORD_NOT_FOUND = "RecordNotFound"; 3 | 4 | export function isRecordNotFoundError( 5 | error: Error & { code?: string } 6 | ): boolean { 7 | return ( 8 | "code" in error && 9 | error.code === PRISMA_QUERY_INTERPRETATION_ERROR && 10 | error.message.includes(PRISMA_RECORD_NOT_FOUND) 11 | ); 12 | } 13 | 14 | export async function transformStringFieldUpdateInput< 15 | T extends undefined | string | { set?: string } 16 | >(input: T, transform: (input: string) => Promise<string>): Promise<T> { 17 | if (typeof input === "object" && typeof input?.set === "string") { 18 | return { set: await transform(input.set) } as T; 19 | } 20 | if (typeof input === "object") { 21 | if (typeof input.set === "string") { 22 | return { set: await transform(input.set) } as T; 23 | } 24 | return input; 25 | } 26 | if (typeof input === "string") { 27 | return (await transform(input)) as T; 28 | } 29 | return input; 30 | } 31 | -------------------------------------------------------------------------------- /server/src/prisma/prisma.module.ts: -------------------------------------------------------------------------------- 1 | import { Global, Module } from "@nestjs/common"; 2 | import { PrismaService } from "./prisma.service"; 3 | 4 | @Global() 5 | @Module({ 6 | providers: [PrismaService], 7 | exports: [PrismaService], 8 | }) 9 | export class PrismaModule {} 10 | -------------------------------------------------------------------------------- /server/src/prisma/prisma.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, OnModuleInit, INestApplication } from "@nestjs/common"; 2 | import { PrismaClient } from "@prisma/client"; 3 | 4 | @Injectable() 5 | export class PrismaService extends PrismaClient implements OnModuleInit { 6 | async onModuleInit() { 7 | await this.$connect(); 8 | } 9 | 10 | async enableShutdownHooks(app: INestApplication) { 11 | this.$on("beforeExit", async () => { 12 | await app.close(); 13 | }); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /server/src/providers/secrets/base/secretsManager.service.base.spec.ts: -------------------------------------------------------------------------------- 1 | import { ConfigService } from "@nestjs/config"; 2 | import { mock } from "jest-mock-extended"; 3 | import { SecretsManagerServiceBase } from "./secretsManager.service.base"; 4 | 5 | describe("Testing the secrets manager base class", () => { 6 | const SECRET_KEY = "SECRET_KEY"; 7 | const SECRET_VALUE = "SECRET_VALUE"; 8 | const configService = mock<ConfigService>(); 9 | const secretsManagerServiceBase = new SecretsManagerServiceBase( 10 | configService 11 | ); 12 | beforeEach(() => { 13 | configService.get.mockClear(); 14 | }); 15 | it("should return value from env", async () => { 16 | //ARRANGE 17 | configService.get.mockReturnValue(SECRET_VALUE); 18 | //ACT 19 | const result = await secretsManagerServiceBase.getSecret(SECRET_KEY); 20 | //ASSERT 21 | expect(result).toBe(SECRET_VALUE); 22 | }); 23 | it("should return null for unknown keys", async () => { 24 | //ARRANGE 25 | configService.get.mockReturnValue(undefined); 26 | //ACT 27 | const result = await secretsManagerServiceBase.getSecret(SECRET_KEY); 28 | //ASSERT 29 | expect(result).toBeNull(); 30 | }); 31 | it("should throw error if dont get key", () => { 32 | return expect(secretsManagerServiceBase.getSecret("")).rejects.toThrow(); 33 | }); 34 | it("should throw an exeption if getting null key", () => { 35 | return expect( 36 | secretsManagerServiceBase.getSecret(null as unknown as string) 37 | ).rejects.toThrow(); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /server/src/providers/secrets/base/secretsManager.service.base.ts: -------------------------------------------------------------------------------- 1 | import { ConfigService } from "@nestjs/config"; 2 | 3 | export interface ISecretsManager { 4 | getSecret: (key: string) => Promise<any | null>; 5 | } 6 | 7 | export class SecretsManagerServiceBase implements ISecretsManager { 8 | constructor(protected readonly configService: ConfigService) {} 9 | async getSecret<T>(key: string): Promise<T | null> { 10 | if (!key) { 11 | throw new Error("Didn't got the key"); 12 | } 13 | const value = this.configService.get(key); 14 | if (value) { 15 | return value; 16 | } 17 | return null; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/src/providers/secrets/secretsManager.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from "@nestjs/common"; 2 | import { SecretsManagerService } from "./secretsManager.service"; 3 | 4 | @Module({ 5 | providers: [SecretsManagerService], 6 | exports: [SecretsManagerService], 7 | }) 8 | export class SecretsManagerModule {} 9 | -------------------------------------------------------------------------------- /server/src/providers/secrets/secretsManager.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@nestjs/common"; 2 | import { ConfigService } from "@nestjs/config"; 3 | import { SecretsManagerServiceBase } from "./base/secretsManager.service.base"; 4 | 5 | @Injectable() 6 | export class SecretsManagerService extends SecretsManagerServiceBase { 7 | constructor(protected readonly configService: ConfigService) { 8 | super(configService); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /server/src/serveStaticOptions.service.ts: -------------------------------------------------------------------------------- 1 | import * as path from "path"; 2 | import { Injectable, Logger } from "@nestjs/common"; 3 | import { ConfigService } from "@nestjs/config"; 4 | import { 5 | ServeStaticModuleOptions, 6 | ServeStaticModuleOptionsFactory, 7 | } from "@nestjs/serve-static"; 8 | 9 | const SERVE_STATIC_ROOT_PATH_VAR = "SERVE_STATIC_ROOT_PATH"; 10 | const DEFAULT_STATIC_MODULE_OPTIONS_LIST: ServeStaticModuleOptions[] = [ 11 | { 12 | serveRoot: "/swagger", 13 | rootPath: path.join(__dirname, "swagger"), 14 | }, 15 | ]; 16 | 17 | @Injectable() 18 | export class ServeStaticOptionsService 19 | implements ServeStaticModuleOptionsFactory 20 | { 21 | private readonly logger = new Logger(ServeStaticOptionsService.name); 22 | 23 | constructor(private readonly configService: ConfigService) {} 24 | 25 | createLoggerOptions(): ServeStaticModuleOptions[] { 26 | const serveStaticRootPath = this.configService.get( 27 | SERVE_STATIC_ROOT_PATH_VAR 28 | ); 29 | if (serveStaticRootPath) { 30 | const resolvedPath = path.resolve(serveStaticRootPath); 31 | this.logger.log(`Serving static files from ${resolvedPath}`); 32 | return [ 33 | ...DEFAULT_STATIC_MODULE_OPTIONS_LIST, 34 | { rootPath: resolvedPath, exclude: ["/api*", "/graphql"] }, 35 | ]; 36 | } 37 | return DEFAULT_STATIC_MODULE_OPTIONS_LIST; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /server/src/swagger.ts: -------------------------------------------------------------------------------- 1 | import { DocumentBuilder, SwaggerCustomOptions } from "@nestjs/swagger"; 2 | 3 | export const swaggerPath = "api"; 4 | 5 | export const swaggerDocumentOptions = new DocumentBuilder() 6 | .setTitle("airbnb-server") 7 | .setDescription( 8 | '\n\n## Congratulations! Your service resource is ready.\n \nPlease note that all endpoints are secured with JWT Bearer authentication.\nBy default, your service resource comes with one user with the username "admin" and password "admin".\nLearn more in [our docs](https://docs.amplication.com)' 9 | ) 10 | .addBearerAuth() 11 | .build(); 12 | 13 | export const swaggerSetupOptions: SwaggerCustomOptions = { 14 | swaggerOptions: { 15 | persistAuthorization: true, 16 | }, 17 | customCssUrl: "../swagger/swagger.css", 18 | customfavIcon: "../swagger/favicon.png", 19 | customSiteTitle: "airbnb-server", 20 | }; 21 | -------------------------------------------------------------------------------- /server/src/swagger/favicon.png: -------------------------------------------------------------------------------- 1 | �PNG 2 |  3 | ��� IHDR��� ��� ���szz���4IDATXG��Kl]W�����}�L�����n�NR�IP%P)��U�� 2�$D�1c cF���PU��"!J�J�#����#%�[Ǿ���й��sI�(q�'G:���k�Oq�eM��P�����q ޏ�!%�$z��~��,-��A�/����L����/(�ɬ�|��o����zs��52r>�hn�KqGU�ݠ!)앴�ũ��%%~��Wk��}�H��@��<�M��� !^���������� TAi�� �##�/�|�ܶ��R� �tP�}�;Ph�n2~���� ����WE8(9`���1�J�M� 1��J6��~n�J=����,-td,��b؏�q�àA�����y ��B|Y�� ��&�͂��l� _6�M/�g�[��93q��L��cc�} W�nn�k�1R�0pDЇh5�/^�������?Ԕ�%;?_q>��ήkU�|��[K ])d�!�� ?d�/qP��~T�M��:g{�j�U��䮾�UZ�~�|Ǽl�{_��&��)���g C�vI����N2`����� y�����yI��ڶ��ԟ��J<�y�S��� �)@�{�� ���/J��xMo�[���_.�}�_AGm���v�)��0�Z���J�:��� ���$�0V;�|��])�d�J“rx��b��)x �SȃFMZ ����(��e�3�N��:{z��z%?Bv&�o#����K�Ǟ���쏁]2���-����ð^������G�wb J��!�o����^2� }�sg�~g�.��b�V��c������]�-���Vks��,[\������y'o�jyK��3s�Z�!k�yǖ+0>:����l#um=���J���z ��7/ 7���s�P��� ����*���'<���)��PP:�Ԯ��^���?�<���!= :*�ѱe�O=6�Qn��<=j��2-�%E��6�l�>�<���s}��� �L���o�6?������h��p����#�]��$b�B����5����:�/O&F\�m�����!���2�i٠ĽQ�1�����"Ձ�!��}@w�X�"2JE�N���� nC/Jkm��>O=�VS[�����R��Gz�M�~�ǐ;@%��~v���#��պٰ(���33m� ��Y5�P{���ń�.����R�X�q9Oա_�5l�L�7�XR��~�:�ԯ�'`��¶Ɩ���z��'H�sjޤp� 4 | q�j��8����F�� 5 | �ٰ\�W�a�%Qw� c8v�L77��lj��߯�Rm����;�R�)���D��F���:�kad \1, ������n�/U����IEND�B`� -------------------------------------------------------------------------------- /server/src/tests/auth/constants.ts: -------------------------------------------------------------------------------- 1 | import { Credentials } from "../../auth/Credentials"; 2 | import { UserInfo } from "../../auth/UserInfo"; 3 | 4 | export const VALID_ID = "1"; 5 | 6 | export const TEST_USER: UserInfo = { 7 | id: "cl7qmjh4h0000tothyjqapgj5", 8 | roles: ["User"], 9 | username: "ofek", 10 | }; 11 | export const SIGN_TOKEN = "SIGN_TOKEN"; 12 | export const VALID_CREDENTIALS: Credentials = { 13 | username: "Valid User", 14 | password: "Valid User Password", 15 | }; 16 | export const INVALID_CREDENTIALS: Credentials = { 17 | username: "Invalid User", 18 | password: "Invalid User Password", 19 | }; 20 | -------------------------------------------------------------------------------- /server/src/tests/auth/jwt/jwt.strategy.spec.ts: -------------------------------------------------------------------------------- 1 | import { UnauthorizedException } from "@nestjs/common"; 2 | import { mock } from "jest-mock-extended"; 3 | import { JwtStrategyBase } from "../../../auth/jwt/base/jwt.strategy.base"; 4 | import { UserService } from "../../../user/user.service"; 5 | import { TEST_USER } from "../constants"; 6 | 7 | describe("Testing the jwtStrategyBase.validate()", () => { 8 | const userService = mock<UserService>(); 9 | const jwtStrategy = new JwtStrategyBase(userService, "Secrete"); 10 | beforeEach(() => { 11 | userService.findOne.mockClear(); 12 | }); 13 | it("should throw UnauthorizedException where there is no user", async () => { 14 | //ARRANGE 15 | userService.findOne 16 | .calledWith({ where: { username: TEST_USER.username } }) 17 | .mockReturnValue(Promise.resolve(null)); 18 | //ACT 19 | const result = jwtStrategy.validate({ 20 | id: TEST_USER.id, 21 | username: TEST_USER.username, 22 | roles: TEST_USER.roles, 23 | }); 24 | //ASSERT 25 | return expect(result).rejects.toThrowError(UnauthorizedException); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /server/src/tests/health/health.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { mock } from "jest-mock-extended"; 2 | import { PrismaService } from "../../prisma/prisma.service"; 3 | import { HealthServiceBase } from "../../health/base/health.service.base"; 4 | 5 | describe("Testing the HealthServiceBase", () => { 6 | //ARRANGE 7 | let prismaService: PrismaService; 8 | let healthServiceBase: HealthServiceBase; 9 | 10 | describe("Testing the isDbReady function in HealthServiceBase class", () => { 11 | beforeEach(() => { 12 | prismaService = mock<PrismaService>(); 13 | healthServiceBase = new HealthServiceBase(prismaService); 14 | }); 15 | it("should return true if allow connection to db", async () => { 16 | //ARRANGE 17 | (prismaService.$queryRaw as jest.Mock).mockReturnValue( 18 | Promise.resolve(true) 19 | ); 20 | //ACT 21 | const response = await healthServiceBase.isDbReady(); 22 | //ASSERT 23 | expect(response).toBe(true); 24 | }); 25 | it("should return false if db is not available", async () => { 26 | //ARRANGE 27 | (prismaService.$queryRaw as jest.Mock).mockReturnValue( 28 | Promise.reject(false) 29 | ); 30 | //ACT 31 | const response = await healthServiceBase.isDbReady(); 32 | //ASSERT 33 | expect(response).toBe(false); 34 | }); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /server/src/trip/base/CreateTripArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { TripCreateInput } from "./TripCreateInput"; 15 | import { ValidateNested } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | 18 | @ArgsType() 19 | class CreateTripArgs { 20 | @ApiProperty({ 21 | required: true, 22 | type: () => TripCreateInput, 23 | }) 24 | @ValidateNested() 25 | @Type(() => TripCreateInput) 26 | @Field(() => TripCreateInput, { nullable: false }) 27 | data!: TripCreateInput; 28 | } 29 | 30 | export { CreateTripArgs as CreateTripArgs }; 31 | -------------------------------------------------------------------------------- /server/src/trip/base/DeleteTripArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { TripWhereUniqueInput } from "./TripWhereUniqueInput"; 15 | import { ValidateNested } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | 18 | @ArgsType() 19 | class DeleteTripArgs { 20 | @ApiProperty({ 21 | required: true, 22 | type: () => TripWhereUniqueInput, 23 | }) 24 | @ValidateNested() 25 | @Type(() => TripWhereUniqueInput) 26 | @Field(() => TripWhereUniqueInput, { nullable: false }) 27 | where!: TripWhereUniqueInput; 28 | } 29 | 30 | export { DeleteTripArgs as DeleteTripArgs }; 31 | -------------------------------------------------------------------------------- /server/src/trip/base/TripCountArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { TripWhereInput } from "./TripWhereInput"; 15 | import { Type } from "class-transformer"; 16 | 17 | @ArgsType() 18 | class TripCountArgs { 19 | @ApiProperty({ 20 | required: false, 21 | type: () => TripWhereInput, 22 | }) 23 | @Field(() => TripWhereInput, { nullable: true }) 24 | @Type(() => TripWhereInput) 25 | where?: TripWhereInput; 26 | } 27 | 28 | export { TripCountArgs as TripCountArgs }; 29 | -------------------------------------------------------------------------------- /server/src/trip/base/TripFindUniqueArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { TripWhereUniqueInput } from "./TripWhereUniqueInput"; 15 | import { ValidateNested } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | 18 | @ArgsType() 19 | class TripFindUniqueArgs { 20 | @ApiProperty({ 21 | required: true, 22 | type: () => TripWhereUniqueInput, 23 | }) 24 | @ValidateNested() 25 | @Type(() => TripWhereUniqueInput) 26 | @Field(() => TripWhereUniqueInput, { nullable: false }) 27 | where!: TripWhereUniqueInput; 28 | } 29 | 30 | export { TripFindUniqueArgs as TripFindUniqueArgs }; 31 | -------------------------------------------------------------------------------- /server/src/trip/base/TripWhereUniqueInput.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { InputType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { IsString } from "class-validator"; 15 | 16 | @InputType() 17 | class TripWhereUniqueInput { 18 | @ApiProperty({ 19 | required: true, 20 | type: String, 21 | }) 22 | @IsString() 23 | @Field(() => String) 24 | id!: string; 25 | } 26 | 27 | export { TripWhereUniqueInput as TripWhereUniqueInput }; 28 | -------------------------------------------------------------------------------- /server/src/trip/base/UpdateTripArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { TripWhereUniqueInput } from "./TripWhereUniqueInput"; 15 | import { ValidateNested } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | import { TripUpdateInput } from "./TripUpdateInput"; 18 | 19 | @ArgsType() 20 | class UpdateTripArgs { 21 | @ApiProperty({ 22 | required: true, 23 | type: () => TripWhereUniqueInput, 24 | }) 25 | @ValidateNested() 26 | @Type(() => TripWhereUniqueInput) 27 | @Field(() => TripWhereUniqueInput, { nullable: false }) 28 | where!: TripWhereUniqueInput; 29 | 30 | @ApiProperty({ 31 | required: true, 32 | type: () => TripUpdateInput, 33 | }) 34 | @ValidateNested() 35 | @Type(() => TripUpdateInput) 36 | @Field(() => TripUpdateInput, { nullable: false }) 37 | data!: TripUpdateInput; 38 | } 39 | 40 | export { UpdateTripArgs as UpdateTripArgs }; 41 | -------------------------------------------------------------------------------- /server/src/trip/base/trip.module.base.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { Module, forwardRef } from "@nestjs/common"; 13 | import { MorganModule } from "nest-morgan"; 14 | import { ACLModule } from "../../auth/acl.module"; 15 | import { AuthModule } from "../../auth/auth.module"; 16 | @Module({ 17 | imports: [ACLModule, forwardRef(() => AuthModule), MorganModule], 18 | exports: [ACLModule, AuthModule, MorganModule], 19 | }) 20 | export class TripModuleBase {} 21 | -------------------------------------------------------------------------------- /server/src/trip/trip.controller.ts: -------------------------------------------------------------------------------- 1 | import * as common from "@nestjs/common"; 2 | import * as swagger from "@nestjs/swagger"; 3 | import * as nestAccessControl from "nest-access-control"; 4 | import { TripService } from "./trip.service"; 5 | import { TripControllerBase } from "./base/trip.controller.base"; 6 | 7 | @swagger.ApiTags("trips") 8 | @common.Controller("trips") 9 | export class TripController extends TripControllerBase { 10 | constructor( 11 | protected readonly service: TripService, 12 | @nestAccessControl.InjectRolesBuilder() 13 | protected readonly rolesBuilder: nestAccessControl.RolesBuilder 14 | ) { 15 | super(service, rolesBuilder); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /server/src/trip/trip.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from "@nestjs/common"; 2 | import { TripModuleBase } from "./base/trip.module.base"; 3 | import { TripService } from "./trip.service"; 4 | import { TripController } from "./trip.controller"; 5 | import { TripResolver } from "./trip.resolver"; 6 | 7 | @Module({ 8 | imports: [TripModuleBase], 9 | controllers: [TripController], 10 | providers: [TripService, TripResolver], 11 | exports: [TripService], 12 | }) 13 | export class TripModule {} 14 | -------------------------------------------------------------------------------- /server/src/trip/trip.resolver.ts: -------------------------------------------------------------------------------- 1 | import * as graphql from "@nestjs/graphql"; 2 | import * as nestAccessControl from "nest-access-control"; 3 | import * as gqlACGuard from "../auth/gqlAC.guard"; 4 | import { GqlDefaultAuthGuard } from "../auth/gqlDefaultAuth.guard"; 5 | import * as common from "@nestjs/common"; 6 | import { TripResolverBase } from "./base/trip.resolver.base"; 7 | import { Trip } from "./base/Trip"; 8 | import { TripService } from "./trip.service"; 9 | 10 | @common.UseGuards(GqlDefaultAuthGuard, gqlACGuard.GqlACGuard) 11 | @graphql.Resolver(() => Trip) 12 | export class TripResolver extends TripResolverBase { 13 | constructor( 14 | protected readonly service: TripService, 15 | @nestAccessControl.InjectRolesBuilder() 16 | protected readonly rolesBuilder: nestAccessControl.RolesBuilder 17 | ) { 18 | super(service, rolesBuilder); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /server/src/trip/trip.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@nestjs/common"; 2 | import { PrismaService } from "../prisma/prisma.service"; 3 | import { TripServiceBase } from "./base/trip.service.base"; 4 | 5 | @Injectable() 6 | export class TripService extends TripServiceBase { 7 | constructor(protected readonly prisma: PrismaService) { 8 | super(prisma); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /server/src/types.ts: -------------------------------------------------------------------------------- 1 | import type { JsonValue } from "type-fest"; 2 | 3 | export type InputJsonValue = Omit<JsonValue, "null">; 4 | -------------------------------------------------------------------------------- /server/src/user/base/CreateUserArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { UserCreateInput } from "./UserCreateInput"; 15 | import { ValidateNested } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | 18 | @ArgsType() 19 | class CreateUserArgs { 20 | @ApiProperty({ 21 | required: true, 22 | type: () => UserCreateInput, 23 | }) 24 | @ValidateNested() 25 | @Type(() => UserCreateInput) 26 | @Field(() => UserCreateInput, { nullable: false }) 27 | data!: UserCreateInput; 28 | } 29 | 30 | export { CreateUserArgs as CreateUserArgs }; 31 | -------------------------------------------------------------------------------- /server/src/user/base/DeleteUserArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { UserWhereUniqueInput } from "./UserWhereUniqueInput"; 15 | import { ValidateNested } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | 18 | @ArgsType() 19 | class DeleteUserArgs { 20 | @ApiProperty({ 21 | required: true, 22 | type: () => UserWhereUniqueInput, 23 | }) 24 | @ValidateNested() 25 | @Type(() => UserWhereUniqueInput) 26 | @Field(() => UserWhereUniqueInput, { nullable: false }) 27 | where!: UserWhereUniqueInput; 28 | } 29 | 30 | export { DeleteUserArgs as DeleteUserArgs }; 31 | -------------------------------------------------------------------------------- /server/src/user/base/ListingCreateNestedManyWithoutUsersInput.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { InputType, Field } from "@nestjs/graphql"; 13 | import { ListingWhereUniqueInput } from "../../listing/base/ListingWhereUniqueInput"; 14 | import { ApiProperty } from "@nestjs/swagger"; 15 | 16 | @InputType() 17 | class ListingCreateNestedManyWithoutUsersInput { 18 | @Field(() => [ListingWhereUniqueInput], { 19 | nullable: true, 20 | }) 21 | @ApiProperty({ 22 | required: false, 23 | type: () => [ListingWhereUniqueInput], 24 | }) 25 | connect?: Array<ListingWhereUniqueInput>; 26 | } 27 | 28 | export { ListingCreateNestedManyWithoutUsersInput as ListingCreateNestedManyWithoutUsersInput }; 29 | -------------------------------------------------------------------------------- /server/src/user/base/ListingUpdateManyWithoutUsersInput.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { InputType, Field } from "@nestjs/graphql"; 13 | import { ListingWhereUniqueInput } from "../../listing/base/ListingWhereUniqueInput"; 14 | import { ApiProperty } from "@nestjs/swagger"; 15 | 16 | @InputType() 17 | class ListingUpdateManyWithoutUsersInput { 18 | @Field(() => [ListingWhereUniqueInput], { 19 | nullable: true, 20 | }) 21 | @ApiProperty({ 22 | required: false, 23 | type: () => [ListingWhereUniqueInput], 24 | }) 25 | connect?: Array<ListingWhereUniqueInput>; 26 | 27 | @Field(() => [ListingWhereUniqueInput], { 28 | nullable: true, 29 | }) 30 | @ApiProperty({ 31 | required: false, 32 | type: () => [ListingWhereUniqueInput], 33 | }) 34 | disconnect?: Array<ListingWhereUniqueInput>; 35 | 36 | @Field(() => [ListingWhereUniqueInput], { 37 | nullable: true, 38 | }) 39 | @ApiProperty({ 40 | required: false, 41 | type: () => [ListingWhereUniqueInput], 42 | }) 43 | set?: Array<ListingWhereUniqueInput>; 44 | } 45 | 46 | export { ListingUpdateManyWithoutUsersInput as ListingUpdateManyWithoutUsersInput }; 47 | -------------------------------------------------------------------------------- /server/src/user/base/TripCreateNestedManyWithoutUsersInput.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { InputType, Field } from "@nestjs/graphql"; 13 | import { TripWhereUniqueInput } from "../../trip/base/TripWhereUniqueInput"; 14 | import { ApiProperty } from "@nestjs/swagger"; 15 | 16 | @InputType() 17 | class TripCreateNestedManyWithoutUsersInput { 18 | @Field(() => [TripWhereUniqueInput], { 19 | nullable: true, 20 | }) 21 | @ApiProperty({ 22 | required: false, 23 | type: () => [TripWhereUniqueInput], 24 | }) 25 | connect?: Array<TripWhereUniqueInput>; 26 | } 27 | 28 | export { TripCreateNestedManyWithoutUsersInput as TripCreateNestedManyWithoutUsersInput }; 29 | -------------------------------------------------------------------------------- /server/src/user/base/TripUpdateManyWithoutUsersInput.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { InputType, Field } from "@nestjs/graphql"; 13 | import { TripWhereUniqueInput } from "../../trip/base/TripWhereUniqueInput"; 14 | import { ApiProperty } from "@nestjs/swagger"; 15 | 16 | @InputType() 17 | class TripUpdateManyWithoutUsersInput { 18 | @Field(() => [TripWhereUniqueInput], { 19 | nullable: true, 20 | }) 21 | @ApiProperty({ 22 | required: false, 23 | type: () => [TripWhereUniqueInput], 24 | }) 25 | connect?: Array<TripWhereUniqueInput>; 26 | 27 | @Field(() => [TripWhereUniqueInput], { 28 | nullable: true, 29 | }) 30 | @ApiProperty({ 31 | required: false, 32 | type: () => [TripWhereUniqueInput], 33 | }) 34 | disconnect?: Array<TripWhereUniqueInput>; 35 | 36 | @Field(() => [TripWhereUniqueInput], { 37 | nullable: true, 38 | }) 39 | @ApiProperty({ 40 | required: false, 41 | type: () => [TripWhereUniqueInput], 42 | }) 43 | set?: Array<TripWhereUniqueInput>; 44 | } 45 | 46 | export { TripUpdateManyWithoutUsersInput as TripUpdateManyWithoutUsersInput }; 47 | -------------------------------------------------------------------------------- /server/src/user/base/UpdateUserArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { UserWhereUniqueInput } from "./UserWhereUniqueInput"; 15 | import { ValidateNested } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | import { UserUpdateInput } from "./UserUpdateInput"; 18 | 19 | @ArgsType() 20 | class UpdateUserArgs { 21 | @ApiProperty({ 22 | required: true, 23 | type: () => UserWhereUniqueInput, 24 | }) 25 | @ValidateNested() 26 | @Type(() => UserWhereUniqueInput) 27 | @Field(() => UserWhereUniqueInput, { nullable: false }) 28 | where!: UserWhereUniqueInput; 29 | 30 | @ApiProperty({ 31 | required: true, 32 | type: () => UserUpdateInput, 33 | }) 34 | @ValidateNested() 35 | @Type(() => UserUpdateInput) 36 | @Field(() => UserUpdateInput, { nullable: false }) 37 | data!: UserUpdateInput; 38 | } 39 | 40 | export { UpdateUserArgs as UpdateUserArgs }; 41 | -------------------------------------------------------------------------------- /server/src/user/base/UserCountArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { UserWhereInput } from "./UserWhereInput"; 15 | import { Type } from "class-transformer"; 16 | 17 | @ArgsType() 18 | class UserCountArgs { 19 | @ApiProperty({ 20 | required: false, 21 | type: () => UserWhereInput, 22 | }) 23 | @Field(() => UserWhereInput, { nullable: true }) 24 | @Type(() => UserWhereInput) 25 | where?: UserWhereInput; 26 | } 27 | 28 | export { UserCountArgs as UserCountArgs }; 29 | -------------------------------------------------------------------------------- /server/src/user/base/UserFindUniqueArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { UserWhereUniqueInput } from "./UserWhereUniqueInput"; 15 | import { ValidateNested } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | 18 | @ArgsType() 19 | class UserFindUniqueArgs { 20 | @ApiProperty({ 21 | required: true, 22 | type: () => UserWhereUniqueInput, 23 | }) 24 | @ValidateNested() 25 | @Type(() => UserWhereUniqueInput) 26 | @Field(() => UserWhereUniqueInput, { nullable: false }) 27 | where!: UserWhereUniqueInput; 28 | } 29 | 30 | export { UserFindUniqueArgs as UserFindUniqueArgs }; 31 | -------------------------------------------------------------------------------- /server/src/user/base/UserWhereUniqueInput.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { InputType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { IsString } from "class-validator"; 15 | 16 | @InputType() 17 | class UserWhereUniqueInput { 18 | @ApiProperty({ 19 | required: true, 20 | type: String, 21 | }) 22 | @IsString() 23 | @Field(() => String) 24 | id!: string; 25 | } 26 | 27 | export { UserWhereUniqueInput as UserWhereUniqueInput }; 28 | -------------------------------------------------------------------------------- /server/src/user/base/WishlistCreateNestedManyWithoutUsersInput.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { InputType, Field } from "@nestjs/graphql"; 13 | import { WishlistWhereUniqueInput } from "../../wishlist/base/WishlistWhereUniqueInput"; 14 | import { ApiProperty } from "@nestjs/swagger"; 15 | 16 | @InputType() 17 | class WishlistCreateNestedManyWithoutUsersInput { 18 | @Field(() => [WishlistWhereUniqueInput], { 19 | nullable: true, 20 | }) 21 | @ApiProperty({ 22 | required: false, 23 | type: () => [WishlistWhereUniqueInput], 24 | }) 25 | connect?: Array<WishlistWhereUniqueInput>; 26 | } 27 | 28 | export { WishlistCreateNestedManyWithoutUsersInput as WishlistCreateNestedManyWithoutUsersInput }; 29 | -------------------------------------------------------------------------------- /server/src/user/base/WishlistUpdateManyWithoutUsersInput.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { InputType, Field } from "@nestjs/graphql"; 13 | import { WishlistWhereUniqueInput } from "../../wishlist/base/WishlistWhereUniqueInput"; 14 | import { ApiProperty } from "@nestjs/swagger"; 15 | 16 | @InputType() 17 | class WishlistUpdateManyWithoutUsersInput { 18 | @Field(() => [WishlistWhereUniqueInput], { 19 | nullable: true, 20 | }) 21 | @ApiProperty({ 22 | required: false, 23 | type: () => [WishlistWhereUniqueInput], 24 | }) 25 | connect?: Array<WishlistWhereUniqueInput>; 26 | 27 | @Field(() => [WishlistWhereUniqueInput], { 28 | nullable: true, 29 | }) 30 | @ApiProperty({ 31 | required: false, 32 | type: () => [WishlistWhereUniqueInput], 33 | }) 34 | disconnect?: Array<WishlistWhereUniqueInput>; 35 | 36 | @Field(() => [WishlistWhereUniqueInput], { 37 | nullable: true, 38 | }) 39 | @ApiProperty({ 40 | required: false, 41 | type: () => [WishlistWhereUniqueInput], 42 | }) 43 | set?: Array<WishlistWhereUniqueInput>; 44 | } 45 | 46 | export { WishlistUpdateManyWithoutUsersInput as WishlistUpdateManyWithoutUsersInput }; 47 | -------------------------------------------------------------------------------- /server/src/user/base/user.module.base.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { Module, forwardRef } from "@nestjs/common"; 13 | import { MorganModule } from "nest-morgan"; 14 | import { ACLModule } from "../../auth/acl.module"; 15 | import { AuthModule } from "../../auth/auth.module"; 16 | @Module({ 17 | imports: [ACLModule, forwardRef(() => AuthModule), MorganModule], 18 | exports: [ACLModule, AuthModule, MorganModule], 19 | }) 20 | export class UserModuleBase {} 21 | -------------------------------------------------------------------------------- /server/src/user/user.controller.ts: -------------------------------------------------------------------------------- 1 | import * as common from "@nestjs/common"; 2 | import * as swagger from "@nestjs/swagger"; 3 | import * as nestAccessControl from "nest-access-control"; 4 | import { UserService } from "./user.service"; 5 | import { UserControllerBase } from "./base/user.controller.base"; 6 | 7 | @swagger.ApiTags("users") 8 | @common.Controller("users") 9 | export class UserController extends UserControllerBase { 10 | constructor( 11 | protected readonly service: UserService, 12 | @nestAccessControl.InjectRolesBuilder() 13 | protected readonly rolesBuilder: nestAccessControl.RolesBuilder 14 | ) { 15 | super(service, rolesBuilder); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /server/src/user/user.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from "@nestjs/common"; 2 | import { UserModuleBase } from "./base/user.module.base"; 3 | import { UserService } from "./user.service"; 4 | import { UserController } from "./user.controller"; 5 | import { UserResolver } from "./user.resolver"; 6 | 7 | @Module({ 8 | imports: [UserModuleBase], 9 | controllers: [UserController], 10 | providers: [UserService, UserResolver], 11 | exports: [UserService], 12 | }) 13 | export class UserModule {} 14 | -------------------------------------------------------------------------------- /server/src/user/user.resolver.ts: -------------------------------------------------------------------------------- 1 | import * as graphql from "@nestjs/graphql"; 2 | import * as nestAccessControl from "nest-access-control"; 3 | import * as gqlACGuard from "../auth/gqlAC.guard"; 4 | import { GqlDefaultAuthGuard } from "../auth/gqlDefaultAuth.guard"; 5 | import * as common from "@nestjs/common"; 6 | import { UserResolverBase } from "./base/user.resolver.base"; 7 | import { User } from "./base/User"; 8 | import { UserService } from "./user.service"; 9 | 10 | @common.UseGuards(GqlDefaultAuthGuard, gqlACGuard.GqlACGuard) 11 | @graphql.Resolver(() => User) 12 | export class UserResolver extends UserResolverBase { 13 | constructor( 14 | protected readonly service: UserService, 15 | @nestAccessControl.InjectRolesBuilder() 16 | protected readonly rolesBuilder: nestAccessControl.RolesBuilder 17 | ) { 18 | super(service, rolesBuilder); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /server/src/user/user.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@nestjs/common"; 2 | import { PrismaService } from "../prisma/prisma.service"; 3 | import { PasswordService } from "../auth/password.service"; 4 | import { UserServiceBase } from "./base/user.service.base"; 5 | 6 | @Injectable() 7 | export class UserService extends UserServiceBase { 8 | constructor( 9 | protected readonly prisma: PrismaService, 10 | protected readonly passwordService: PasswordService 11 | ) { 12 | super(prisma, passwordService); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /server/src/util/BooleanFilter.ts: -------------------------------------------------------------------------------- 1 | import { Field, InputType } from "@nestjs/graphql"; 2 | import { ApiProperty } from "@nestjs/swagger"; 3 | import { IsOptional } from "class-validator"; 4 | import { Type } from "class-transformer"; 5 | 6 | @InputType({ 7 | isAbstract: true, 8 | description: undefined, 9 | }) 10 | export class BooleanFilter { 11 | @ApiProperty({ 12 | required: false, 13 | type: Boolean, 14 | }) 15 | @IsOptional() 16 | @Field(() => Boolean, { 17 | nullable: true, 18 | }) 19 | @Type(() => Boolean) 20 | equals?: boolean; 21 | 22 | @ApiProperty({ 23 | required: false, 24 | type: Boolean, 25 | }) 26 | @IsOptional() 27 | @Field(() => Boolean, { 28 | nullable: true, 29 | }) 30 | @Type(() => Boolean) 31 | not?: boolean; 32 | } 33 | -------------------------------------------------------------------------------- /server/src/util/BooleanNullableFilter.ts: -------------------------------------------------------------------------------- 1 | import { Field, InputType } from "@nestjs/graphql"; 2 | import { ApiProperty } from "@nestjs/swagger"; 3 | import { IsOptional } from "class-validator"; 4 | import { Type } from "class-transformer"; 5 | @InputType({ 6 | isAbstract: true, 7 | description: undefined, 8 | }) 9 | export class BooleanNullableFilter { 10 | @ApiProperty({ 11 | required: false, 12 | type: Boolean, 13 | }) 14 | @IsOptional() 15 | @Field(() => Boolean, { 16 | nullable: true, 17 | }) 18 | @Type(() => Boolean) 19 | equals?: boolean | null; 20 | 21 | @ApiProperty({ 22 | required: false, 23 | type: Boolean, 24 | }) 25 | @IsOptional() 26 | @Field(() => Boolean, { 27 | nullable: true, 28 | }) 29 | @Type(() => Boolean) 30 | not?: boolean | null; 31 | } 32 | -------------------------------------------------------------------------------- /server/src/util/JsonFilter.ts: -------------------------------------------------------------------------------- 1 | import { Field, InputType } from "@nestjs/graphql"; 2 | import { ApiProperty } from "@nestjs/swagger"; 3 | import { IsOptional } from "class-validator"; 4 | import { GraphQLJSONObject } from "graphql-type-json"; 5 | import { InputJsonValue } from "../types"; 6 | 7 | @InputType({ 8 | isAbstract: true, 9 | description: undefined, 10 | }) 11 | export class JsonFilter { 12 | @ApiProperty({ 13 | required: false, 14 | type: GraphQLJSONObject, 15 | }) 16 | @IsOptional() 17 | @Field(() => GraphQLJSONObject, { 18 | nullable: true, 19 | }) 20 | equals?: InputJsonValue; 21 | 22 | @ApiProperty({ 23 | required: false, 24 | type: GraphQLJSONObject, 25 | }) 26 | @IsOptional() 27 | @Field(() => GraphQLJSONObject, { 28 | nullable: true, 29 | }) 30 | not?: InputJsonValue; 31 | } 32 | -------------------------------------------------------------------------------- /server/src/util/JsonNullableFilter.ts: -------------------------------------------------------------------------------- 1 | import type { JsonValue } from "type-fest"; 2 | import { Field, InputType } from "@nestjs/graphql"; 3 | import { ApiProperty } from "@nestjs/swagger"; 4 | import { IsOptional } from "class-validator"; 5 | import { GraphQLJSONObject } from "graphql-type-json"; 6 | 7 | @InputType({ 8 | isAbstract: true, 9 | description: undefined, 10 | }) 11 | export class JsonNullableFilter { 12 | @ApiProperty({ 13 | required: false, 14 | type: GraphQLJSONObject, 15 | }) 16 | @IsOptional() 17 | @Field(() => GraphQLJSONObject, { 18 | nullable: true, 19 | }) 20 | equals?: JsonValue; 21 | 22 | @ApiProperty({ 23 | required: false, 24 | type: GraphQLJSONObject, 25 | }) 26 | @IsOptional() 27 | @Field(() => GraphQLJSONObject, { 28 | nullable: true, 29 | }) 30 | not?: JsonValue; 31 | } 32 | -------------------------------------------------------------------------------- /server/src/util/MetaQueryPayload.ts: -------------------------------------------------------------------------------- 1 | import { ObjectType, Field } from "@nestjs/graphql"; 2 | import { ApiProperty } from "@nestjs/swagger"; 3 | 4 | @ObjectType() 5 | class MetaQueryPayload { 6 | @ApiProperty({ 7 | required: true, 8 | type: [Number], 9 | }) 10 | @Field(() => Number) 11 | count!: number; 12 | } 13 | export { MetaQueryPayload }; 14 | -------------------------------------------------------------------------------- /server/src/util/QueryMode.ts: -------------------------------------------------------------------------------- 1 | import { registerEnumType } from "@nestjs/graphql"; 2 | 3 | export enum QueryMode { 4 | Default = "default", 5 | Insensitive = "insensitive", 6 | } 7 | registerEnumType(QueryMode, { 8 | name: "QueryMode", 9 | description: undefined, 10 | }); 11 | -------------------------------------------------------------------------------- /server/src/util/SortOrder.ts: -------------------------------------------------------------------------------- 1 | import { registerEnumType } from "@nestjs/graphql"; 2 | 3 | export enum SortOrder { 4 | Asc = "asc", 5 | Desc = "desc", 6 | } 7 | registerEnumType(SortOrder, { 8 | name: "SortOrder", 9 | description: undefined, 10 | }); 11 | -------------------------------------------------------------------------------- /server/src/validators/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./is-json-value-validator"; 2 | -------------------------------------------------------------------------------- /server/src/validators/is-json-value-validator.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ValidationArguments, 3 | registerDecorator, 4 | ValidationOptions, 5 | } from "class-validator"; 6 | import isJSONValidator from "validator/lib/isJSON"; 7 | 8 | export function IsJSONValue(validationOptions?: ValidationOptions) { 9 | return function (object: Object, propertyName: string) { 10 | registerDecorator({ 11 | name: "IsJSONValue", 12 | target: object.constructor, 13 | propertyName: propertyName, 14 | options: validationOptions, 15 | validator: { 16 | validate(value: any, args: ValidationArguments) { 17 | if (typeof value === "string") { 18 | return isJSONValidator(value); 19 | } 20 | 21 | return isJSONValidator(JSON.stringify(value)); 22 | }, 23 | defaultMessage(args: ValidationArguments): string { 24 | return `${args.property} must be a valid json`; 25 | }, 26 | }, 27 | }); 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /server/src/wishlist/base/CreateWishlistArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { WishlistCreateInput } from "./WishlistCreateInput"; 15 | import { ValidateNested } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | 18 | @ArgsType() 19 | class CreateWishlistArgs { 20 | @ApiProperty({ 21 | required: true, 22 | type: () => WishlistCreateInput, 23 | }) 24 | @ValidateNested() 25 | @Type(() => WishlistCreateInput) 26 | @Field(() => WishlistCreateInput, { nullable: false }) 27 | data!: WishlistCreateInput; 28 | } 29 | 30 | export { CreateWishlistArgs as CreateWishlistArgs }; 31 | -------------------------------------------------------------------------------- /server/src/wishlist/base/DeleteWishlistArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { WishlistWhereUniqueInput } from "./WishlistWhereUniqueInput"; 15 | import { ValidateNested } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | 18 | @ArgsType() 19 | class DeleteWishlistArgs { 20 | @ApiProperty({ 21 | required: true, 22 | type: () => WishlistWhereUniqueInput, 23 | }) 24 | @ValidateNested() 25 | @Type(() => WishlistWhereUniqueInput) 26 | @Field(() => WishlistWhereUniqueInput, { nullable: false }) 27 | where!: WishlistWhereUniqueInput; 28 | } 29 | 30 | export { DeleteWishlistArgs as DeleteWishlistArgs }; 31 | -------------------------------------------------------------------------------- /server/src/wishlist/base/UpdateWishlistArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { WishlistWhereUniqueInput } from "./WishlistWhereUniqueInput"; 15 | import { ValidateNested } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | import { WishlistUpdateInput } from "./WishlistUpdateInput"; 18 | 19 | @ArgsType() 20 | class UpdateWishlistArgs { 21 | @ApiProperty({ 22 | required: true, 23 | type: () => WishlistWhereUniqueInput, 24 | }) 25 | @ValidateNested() 26 | @Type(() => WishlistWhereUniqueInput) 27 | @Field(() => WishlistWhereUniqueInput, { nullable: false }) 28 | where!: WishlistWhereUniqueInput; 29 | 30 | @ApiProperty({ 31 | required: true, 32 | type: () => WishlistUpdateInput, 33 | }) 34 | @ValidateNested() 35 | @Type(() => WishlistUpdateInput) 36 | @Field(() => WishlistUpdateInput, { nullable: false }) 37 | data!: WishlistUpdateInput; 38 | } 39 | 40 | export { UpdateWishlistArgs as UpdateWishlistArgs }; 41 | -------------------------------------------------------------------------------- /server/src/wishlist/base/Wishlist.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ObjectType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { IsDate, IsString, ValidateNested } from "class-validator"; 15 | import { Type } from "class-transformer"; 16 | import { Listing } from "../../listing/base/Listing"; 17 | import { User } from "../../user/base/User"; 18 | 19 | @ObjectType() 20 | class Wishlist { 21 | @ApiProperty({ 22 | required: true, 23 | }) 24 | @IsDate() 25 | @Type(() => Date) 26 | @Field(() => Date) 27 | createdAt!: Date; 28 | 29 | @ApiProperty({ 30 | required: true, 31 | type: String, 32 | }) 33 | @IsString() 34 | @Field(() => String) 35 | id!: string; 36 | 37 | @ApiProperty({ 38 | required: true, 39 | type: () => Listing, 40 | }) 41 | @ValidateNested() 42 | @Type(() => Listing) 43 | listing?: Listing; 44 | 45 | @ApiProperty({ 46 | required: true, 47 | }) 48 | @IsDate() 49 | @Type(() => Date) 50 | @Field(() => Date) 51 | updatedAt!: Date; 52 | 53 | @ApiProperty({ 54 | required: true, 55 | type: () => User, 56 | }) 57 | @ValidateNested() 58 | @Type(() => User) 59 | user?: User; 60 | } 61 | 62 | export { Wishlist as Wishlist }; 63 | -------------------------------------------------------------------------------- /server/src/wishlist/base/WishlistCountArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { WishlistWhereInput } from "./WishlistWhereInput"; 15 | import { Type } from "class-transformer"; 16 | 17 | @ArgsType() 18 | class WishlistCountArgs { 19 | @ApiProperty({ 20 | required: false, 21 | type: () => WishlistWhereInput, 22 | }) 23 | @Field(() => WishlistWhereInput, { nullable: true }) 24 | @Type(() => WishlistWhereInput) 25 | where?: WishlistWhereInput; 26 | } 27 | 28 | export { WishlistCountArgs as WishlistCountArgs }; 29 | -------------------------------------------------------------------------------- /server/src/wishlist/base/WishlistCreateInput.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { InputType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { ListingWhereUniqueInput } from "../../listing/base/ListingWhereUniqueInput"; 15 | import { ValidateNested } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | import { UserWhereUniqueInput } from "../../user/base/UserWhereUniqueInput"; 18 | 19 | @InputType() 20 | class WishlistCreateInput { 21 | @ApiProperty({ 22 | required: true, 23 | type: () => ListingWhereUniqueInput, 24 | }) 25 | @ValidateNested() 26 | @Type(() => ListingWhereUniqueInput) 27 | @Field(() => ListingWhereUniqueInput) 28 | listing!: ListingWhereUniqueInput; 29 | 30 | @ApiProperty({ 31 | required: true, 32 | type: () => UserWhereUniqueInput, 33 | }) 34 | @ValidateNested() 35 | @Type(() => UserWhereUniqueInput) 36 | @Field(() => UserWhereUniqueInput) 37 | user!: UserWhereUniqueInput; 38 | } 39 | 40 | export { WishlistCreateInput as WishlistCreateInput }; 41 | -------------------------------------------------------------------------------- /server/src/wishlist/base/WishlistFindUniqueArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { WishlistWhereUniqueInput } from "./WishlistWhereUniqueInput"; 15 | import { ValidateNested } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | 18 | @ArgsType() 19 | class WishlistFindUniqueArgs { 20 | @ApiProperty({ 21 | required: true, 22 | type: () => WishlistWhereUniqueInput, 23 | }) 24 | @ValidateNested() 25 | @Type(() => WishlistWhereUniqueInput) 26 | @Field(() => WishlistWhereUniqueInput, { nullable: false }) 27 | where!: WishlistWhereUniqueInput; 28 | } 29 | 30 | export { WishlistFindUniqueArgs as WishlistFindUniqueArgs }; 31 | -------------------------------------------------------------------------------- /server/src/wishlist/base/WishlistUpdateInput.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { InputType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { ListingWhereUniqueInput } from "../../listing/base/ListingWhereUniqueInput"; 15 | import { ValidateNested, IsOptional } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | import { UserWhereUniqueInput } from "../../user/base/UserWhereUniqueInput"; 18 | 19 | @InputType() 20 | class WishlistUpdateInput { 21 | @ApiProperty({ 22 | required: false, 23 | type: () => ListingWhereUniqueInput, 24 | }) 25 | @ValidateNested() 26 | @Type(() => ListingWhereUniqueInput) 27 | @IsOptional() 28 | @Field(() => ListingWhereUniqueInput, { 29 | nullable: true, 30 | }) 31 | listing?: ListingWhereUniqueInput; 32 | 33 | @ApiProperty({ 34 | required: false, 35 | type: () => UserWhereUniqueInput, 36 | }) 37 | @ValidateNested() 38 | @Type(() => UserWhereUniqueInput) 39 | @IsOptional() 40 | @Field(() => UserWhereUniqueInput, { 41 | nullable: true, 42 | }) 43 | user?: UserWhereUniqueInput; 44 | } 45 | 46 | export { WishlistUpdateInput as WishlistUpdateInput }; 47 | -------------------------------------------------------------------------------- /server/src/wishlist/base/WishlistWhereUniqueInput.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { InputType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { IsString } from "class-validator"; 15 | 16 | @InputType() 17 | class WishlistWhereUniqueInput { 18 | @ApiProperty({ 19 | required: true, 20 | type: String, 21 | }) 22 | @IsString() 23 | @Field(() => String) 24 | id!: string; 25 | } 26 | 27 | export { WishlistWhereUniqueInput as WishlistWhereUniqueInput }; 28 | -------------------------------------------------------------------------------- /server/src/wishlist/base/wishlist.module.base.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { Module, forwardRef } from "@nestjs/common"; 13 | import { MorganModule } from "nest-morgan"; 14 | import { ACLModule } from "../../auth/acl.module"; 15 | import { AuthModule } from "../../auth/auth.module"; 16 | @Module({ 17 | imports: [ACLModule, forwardRef(() => AuthModule), MorganModule], 18 | exports: [ACLModule, AuthModule, MorganModule], 19 | }) 20 | export class WishlistModuleBase {} 21 | -------------------------------------------------------------------------------- /server/src/wishlist/wishlist.controller.ts: -------------------------------------------------------------------------------- 1 | import * as common from "@nestjs/common"; 2 | import * as swagger from "@nestjs/swagger"; 3 | import * as nestAccessControl from "nest-access-control"; 4 | import { WishlistService } from "./wishlist.service"; 5 | import { WishlistControllerBase } from "./base/wishlist.controller.base"; 6 | 7 | @swagger.ApiTags("wishlists") 8 | @common.Controller("wishlists") 9 | export class WishlistController extends WishlistControllerBase { 10 | constructor( 11 | protected readonly service: WishlistService, 12 | @nestAccessControl.InjectRolesBuilder() 13 | protected readonly rolesBuilder: nestAccessControl.RolesBuilder 14 | ) { 15 | super(service, rolesBuilder); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /server/src/wishlist/wishlist.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from "@nestjs/common"; 2 | import { WishlistModuleBase } from "./base/wishlist.module.base"; 3 | import { WishlistService } from "./wishlist.service"; 4 | import { WishlistController } from "./wishlist.controller"; 5 | import { WishlistResolver } from "./wishlist.resolver"; 6 | 7 | @Module({ 8 | imports: [WishlistModuleBase], 9 | controllers: [WishlistController], 10 | providers: [WishlistService, WishlistResolver], 11 | exports: [WishlistService], 12 | }) 13 | export class WishlistModule {} 14 | -------------------------------------------------------------------------------- /server/src/wishlist/wishlist.resolver.ts: -------------------------------------------------------------------------------- 1 | import * as graphql from "@nestjs/graphql"; 2 | import * as nestAccessControl from "nest-access-control"; 3 | import * as gqlACGuard from "../auth/gqlAC.guard"; 4 | import { GqlDefaultAuthGuard } from "../auth/gqlDefaultAuth.guard"; 5 | import * as common from "@nestjs/common"; 6 | import { WishlistResolverBase } from "./base/wishlist.resolver.base"; 7 | import { Wishlist } from "./base/Wishlist"; 8 | import { WishlistService } from "./wishlist.service"; 9 | 10 | @common.UseGuards(GqlDefaultAuthGuard, gqlACGuard.GqlACGuard) 11 | @graphql.Resolver(() => Wishlist) 12 | export class WishlistResolver extends WishlistResolverBase { 13 | constructor( 14 | protected readonly service: WishlistService, 15 | @nestAccessControl.InjectRolesBuilder() 16 | protected readonly rolesBuilder: nestAccessControl.RolesBuilder 17 | ) { 18 | super(service, rolesBuilder); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /server/src/wishlist/wishlist.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@nestjs/common"; 2 | import { PrismaService } from "../prisma/prisma.service"; 3 | import { WishlistServiceBase } from "./base/wishlist.service.base"; 4 | 5 | @Injectable() 6 | export class WishlistService extends WishlistServiceBase { 7 | constructor(protected readonly prisma: PrismaService) { 8 | super(prisma); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /server/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "prisma", "test", "dist", "**/*spec.ts", "admin"] 4 | } 5 | -------------------------------------------------------------------------------- /server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./", 4 | "module": "commonjs", 5 | "declaration": false, 6 | "removeComments": true, 7 | "emitDecoratorMetadata": true, 8 | "experimentalDecorators": true, 9 | "target": "es2017", 10 | "lib": ["ES2020"], 11 | "sourceMap": true, 12 | "outDir": "./dist", 13 | "incremental": true, 14 | "esModuleInterop": true, 15 | "allowSyntheticDefaultImports": true, 16 | "resolveJsonModule": true, 17 | "skipLibCheck": true, 18 | "strict": true, 19 | "paths": { 20 | "@app/custom-validators": ["src/validators"] 21 | } 22 | }, 23 | "include": ["src"] 24 | } 25 | --------------------------------------------------------------------------------