From 23079666d453cf1fe9df3d613cc6b7f8ce9f8000 Mon Sep 17 00:00:00 2001 From: behnamrhp Date: Tue, 23 May 2023 15:38:16 +0300 Subject: [PATCH] [FEAT]: add qr code --- .env.development | 3 +- .env.production | 10 ++++ docker-compose.prod.yml | 13 +++- dockerfile.prod | 2 +- package-lock.json | 5 ++ package.json | 1 + public/assets/icons/qrcode.png | Bin 0 -> 10481 bytes .../core/places/common/entity/placeEntity.ts | 9 ++- .../core/places/common/model/placesModel.ts | 19 +++++- .../usecase/__test__/getPlacesUsecase.test.ts | 22 +++---- src/driven/utils/components/modal/Modal.tsx | 42 +++++++++++++ src/driven/utils/configs/appConfig.ts | 1 + src/driven/utils/constants/assertUrls.ts | 1 + src/driven/utils/constants/envs.ts | 1 + src/driven/utils/constants/staticMessages.ts | 4 ++ .../core/common/table-row/infra/protocols.ts | 4 +- .../core/common/table-row/view/TableRow.tsx | 5 +- .../core/common/table-row/view/protocols.ts | 4 +- .../view/table-row-item/view/RowItem.tsx | 51 ++++++++++++++-- .../core/places-list/infra/PlacesList.tsx | 56 +++++++++++++++++- .../core/places-list/view/PlacesListView.tsx | 3 +- src/driving/main/pages/index.tsx | 1 + 22 files changed, 230 insertions(+), 27 deletions(-) create mode 100644 .env.production create mode 100644 public/assets/icons/qrcode.png create mode 100644 src/driven/utils/components/modal/Modal.tsx diff --git a/.env.development b/.env.development index 233e901..50eda8e 100644 --- a/.env.development +++ b/.env.development @@ -7,4 +7,5 @@ VITE_API_CREATE_MEMBER = /user_place/members VITE_API_PLACES = /place VITE_API_USERS = /profile VITE_API_USERS_ACCOUNT = /account -VITE_API_USERS_PROFILE = /profile \ No newline at end of file +VITE_API_USERS_PROFILE = /profile +VITE_API_QR = /qr_code \ No newline at end of file diff --git a/.env.production b/.env.production new file mode 100644 index 0000000..233e901 --- /dev/null +++ b/.env.production @@ -0,0 +1,10 @@ +VITE_API_ORIGIN = https://admin.dev.dipal.ru/api/v1 +VITE_API_AUTH_ORIGIN = https://auth.dev.dipal.ru/api/v1/auth +VITE_API_AUTH_PHONENUMBER = /start-challenge +VITE_API_AUTH_LOGIN = /login +VITE_API_AUTH_REFRESH = /refresh-token +VITE_API_CREATE_MEMBER = /user_place/members +VITE_API_PLACES = /place +VITE_API_USERS = /profile +VITE_API_USERS_ACCOUNT = /account +VITE_API_USERS_PROFILE = /profile \ No newline at end of file diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 55ebaaa..a430b0b 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -1,8 +1,19 @@ version: "3.3" services: - dipal-admin-prod: + dipal-admin: ports: - 8000:80 + environment: + - VITE_API_ORIGIN=https://admin.dev.dipal.ru/api/v1 + - VITE_API_AUTH_ORIGIN=https://auth.dev.dipal.ru/api/v1/auth + - VITE_API_AUTH_PHONENUMBER=/start-challenge + - VITE_API_AUTH_LOGIN=/login + - VITE_API_AUTH_REFRESH=/refresh-token + - VITE_API_CREATE_MEMBER=/user_place/members + - VITE_API_PLACES=/place + - VITE_API_USERS=/profile + - VITE_API_USERS_ACCOUNT=/account + - VITE_API_USERS_PROFILE=/profile build: context: . dockerfile: dockerfile.prod \ No newline at end of file diff --git a/dockerfile.prod b/dockerfile.prod index 493cdd5..a2122da 100644 --- a/dockerfile.prod +++ b/dockerfile.prod @@ -18,4 +18,4 @@ USER nginx EXPOSE 80 -CMD ["nginx", "-g", "daemon off;"] +CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 78b9b10..4ca65cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7110,6 +7110,11 @@ "integrity": "sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==", "dev": true }, + "qrcode.react": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/qrcode.react/-/qrcode.react-3.1.0.tgz", + "integrity": "sha512-oyF+Urr3oAMUG/OiOuONL3HXM+53wvuH3mtIWQrYmsXoAq0DkvZp2RYUWFSMFtbdOpuS++9v+WAkzNVkMlNW6Q==" + }, "querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", diff --git a/package.json b/package.json index 223f7a6..fea4397 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "dependencies": { "@reduxjs/toolkit": "^1.9.3", "axios": "^1.3.4", + "qrcode.react": "^3.1.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-redux": "^8.0.5", diff --git a/public/assets/icons/qrcode.png b/public/assets/icons/qrcode.png new file mode 100644 index 0000000000000000000000000000000000000000..9e40ac1dac0899e2ad9521d140b98ba1aea6b8d2 GIT binary patch literal 10481 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 T@-L`85x}0eFsk|kclZAQffrDG literal 0 HcmV?d00001 diff --git a/src/business-logic/core/places/common/entity/placeEntity.ts b/src/business-logic/core/places/common/entity/placeEntity.ts index b171c44..d6cb290 100644 --- a/src/business-logic/core/places/common/entity/placeEntity.ts +++ b/src/business-logic/core/places/common/entity/placeEntity.ts @@ -1,7 +1,14 @@ +export type QrPlace = { + oneTime: false; + placeId: string; + userId: string; + id: string; +}; + type Places = { placeType: string; name: string; - qr: null | string; + qr: null | QrPlace; id: string; parentId: string | null; }; diff --git a/src/business-logic/core/places/common/model/placesModel.ts b/src/business-logic/core/places/common/model/placesModel.ts index 9b73779..bb96839 100644 --- a/src/business-logic/core/places/common/model/placesModel.ts +++ b/src/business-logic/core/places/common/model/placesModel.ts @@ -1,4 +1,4 @@ -import Places from '../entity/placeEntity'; +import Places, { QrPlace } from '../entity/placeEntity'; class PlacesModel { private placesList: Places[]; @@ -16,6 +16,23 @@ class PlacesModel { getTitle(): string { return this.modelTitle; } + + private updatePlaces(updatedPlaces: Places[]) { + this.placesList = updatedPlaces; + } + + setQrFor(qrCodesToAdd: QrPlace[]): Places[] { + const updatedPlaces = this.placesList.map((place) => { + const relatedQrCode = qrCodesToAdd.find((qrItem) => qrItem.placeId === place.id); + + return { + ...place, + qr: relatedQrCode || null, + }; + }); + this.updatePlaces(updatedPlaces); + return updatedPlaces; + } } export default PlacesModel; diff --git a/src/business-logic/core/places/get-places/usecase/__test__/getPlacesUsecase.test.ts b/src/business-logic/core/places/get-places/usecase/__test__/getPlacesUsecase.test.ts index 947836c..cc702b1 100644 --- a/src/business-logic/core/places/get-places/usecase/__test__/getPlacesUsecase.test.ts +++ b/src/business-logic/core/places/get-places/usecase/__test__/getPlacesUsecase.test.ts @@ -3,16 +3,18 @@ import IGetPlacesRepo from '../../data/repository/IGetPlacesRepo'; import { GetPlacesRO } from '../../data/response-object/protocols'; import GettingPlacesUsecase from '../getPlaceUsecase'; -const mockedRO: GetPlacesRO = { - availableServices: [''], - createdAt: 'createdAt', - id: 'id', - name: 'name', - parentId: null, - placeType: 'continent', - updatedAt: 'updatedTime', - qr: null, -}; +const mockedRO: GetPlacesRO = [ + { + availableServices: [''], + createdAt: 'createdAt', + id: 'id', + name: 'name', + parentId: null, + placeType: 'continent', + updatedAt: 'updatedTime', + qr: null, + }, +]; const model = new PlacesModel(mockedRO); const mockedRepo: IGetPlacesRepo = jest.fn().mockImplementation(async () => model); diff --git a/src/driven/utils/components/modal/Modal.tsx b/src/driven/utils/components/modal/Modal.tsx new file mode 100644 index 0000000..11740d9 --- /dev/null +++ b/src/driven/utils/components/modal/Modal.tsx @@ -0,0 +1,42 @@ +/* eslint-disable no-unused-expressions */ +import React from 'react'; +import ReactDOM from 'react-dom'; + +export interface ModalInterface { + onCloseCallback?: () => unknown; +} +export default function Modal({ onCloseCallback, children }: ModalInterface & React.PropsWithChildren) { + const modalRef = React.useRef(null); + const el = React.useRef(document.createElement('div')); + + const closeModal = () => { + el.current.remove(); + if (typeof onCloseCallback !== 'undefined') onCloseCallback(); + }; + React.useEffect(() => { + const portal = document.getElementById('root'); + portal?.appendChild(el.current); + + return () => { + if (typeof onCloseCallback !== 'undefined') onCloseCallback(); + return el.current?.remove(); + }; + }, []); + return ReactDOM.createPortal( +
+
{ + e.stopPropagation(); + }} + > + {children} +
+
, + el.current, + ); +} diff --git a/src/driven/utils/configs/appConfig.ts b/src/driven/utils/configs/appConfig.ts index 93bd570..b4742ae 100644 --- a/src/driven/utils/configs/appConfig.ts +++ b/src/driven/utils/configs/appConfig.ts @@ -30,6 +30,7 @@ export const apiUrls = { core: { getPlaces: `${baseApiUrl}${ENVs.apiGetPlaces}`, getUsers: `${baseApiUrl}${ENVs.apiGetUsers}`, + getQrs: `${baseApiUrl}${ENVs.apiQr}`, createUserAccount: `${baseApiUrl}${ENVs.apiCreateUserAccount}`, createUserProfile: `${baseApiUrl}${ENVs.apiCreateUserProfile}`, createMember: `${baseApiUrl}${ENVs.apiCreateMember}`, diff --git a/src/driven/utils/constants/assertUrls.ts b/src/driven/utils/constants/assertUrls.ts index 66c35ac..938b105 100644 --- a/src/driven/utils/constants/assertUrls.ts +++ b/src/driven/utils/constants/assertUrls.ts @@ -5,4 +5,5 @@ export const icons = { logoBlack: `${baseIconsUrl}logo-black.svg`, users: `${baseIconsUrl}users.svg`, createUser: `${baseIconsUrl}createuser.svg`, + qrcode: `${baseIconsUrl}qrcode.png`, }; diff --git a/src/driven/utils/constants/envs.ts b/src/driven/utils/constants/envs.ts index 102978c..428a48c 100644 --- a/src/driven/utils/constants/envs.ts +++ b/src/driven/utils/constants/envs.ts @@ -7,6 +7,7 @@ export const ENVs = { apiGetPlaces: process.env.VITE_API_PLACES, apiGetUsers: process.env.VITE_API_USERS, apiCreateUserAccount: process.env.VITE_API_USERS_ACCOUNT, + apiQr: process.env.VITE_API_QR, apiCreateUserProfile: process.env.VITE_API_USERS_PROFILE, apiCreateMember: process.env.VITE_API_CREATE_MEMBER, }; diff --git a/src/driven/utils/constants/staticMessages.ts b/src/driven/utils/constants/staticMessages.ts index 3acc2a0..a666000 100644 --- a/src/driven/utils/constants/staticMessages.ts +++ b/src/driven/utils/constants/staticMessages.ts @@ -24,6 +24,10 @@ export const staticMessages = { createUser: 'user created successfully', createMember: 'member created successfully', }, + and: 'and', + canUseFor: 'can use for', + oneTime: 'one time', + multipleTimes: 'multiple times', }, service: { errors: { diff --git a/src/driving/application/core/common/table-row/infra/protocols.ts b/src/driving/application/core/common/table-row/infra/protocols.ts index c2a94de..df9424d 100644 --- a/src/driving/application/core/common/table-row/infra/protocols.ts +++ b/src/driving/application/core/common/table-row/infra/protocols.ts @@ -1,7 +1,9 @@ +import { QrPlace } from '~/business-logic/core/places/common/entity/placeEntity'; + export interface ITableRowInfra { selectedRowId: string; rowData: { - rowItemsTitle: (string | null)[]; + rowItemsTitle: (string | null | QrPlace)[]; rowId: string; }; setSelectedRowId: React.Dispatch>; diff --git a/src/driving/application/core/common/table-row/view/TableRow.tsx b/src/driving/application/core/common/table-row/view/TableRow.tsx index 5156de4..7185fbc 100644 --- a/src/driving/application/core/common/table-row/view/TableRow.tsx +++ b/src/driving/application/core/common/table-row/view/TableRow.tsx @@ -9,10 +9,11 @@ export default function TableRowView(props: ITableRowProps) { const columns = rowItemsTitle.map((rowItemTitle, index) => { return ( ); }); diff --git a/src/driving/application/core/common/table-row/view/protocols.ts b/src/driving/application/core/common/table-row/view/protocols.ts index eeba1fb..e9c9058 100644 --- a/src/driving/application/core/common/table-row/view/protocols.ts +++ b/src/driving/application/core/common/table-row/view/protocols.ts @@ -1,7 +1,9 @@ +import { QrPlace } from '~/business-logic/core/places/common/entity/placeEntity'; + export interface ITableRowProps { isSelected: boolean; rowData: { - rowItemsTitle: (string | null)[]; + rowItemsTitle: (string | null | QrPlace)[]; rowId: string; }; setSelectedRowId: React.Dispatch>; diff --git a/src/driving/application/core/common/table-row/view/table-row-item/view/RowItem.tsx b/src/driving/application/core/common/table-row/view/table-row-item/view/RowItem.tsx index e15e71b..d01181f 100644 --- a/src/driving/application/core/common/table-row/view/table-row-item/view/RowItem.tsx +++ b/src/driving/application/core/common/table-row/view/table-row-item/view/RowItem.tsx @@ -1,13 +1,56 @@ -import React from 'react'; +/* eslint-disable import/no-extraneous-dependencies */ +import React, { useState } from 'react'; +import { QrPlace } from '~/business-logic/core/places/common/entity/placeEntity'; +import Modal from '~/driven/utils/components/modal/Modal'; +import { icons } from '~/driven/utils/constants/assertUrls'; +import { QRCodeCanvas } from 'qrcode.react'; +import { staticMessages } from '~/driven/utils/constants/staticMessages'; +import { ITableRowInfra } from '../../../infra/protocols'; interface IRowItemProp { - title: string | null; + title: string | null | QrPlace; hasCheckbox: boolean; isSelected: boolean; + rowData: ITableRowInfra['rowData']; +} + +function QrCodeReader(props: { QrCodeData: QrPlace; rowData: ITableRowInfra['rowData'] }) { + const { QrCodeData, rowData } = props; + const placeType = rowData.rowItemsTitle[1]; + const placeTitle = rowData.rowItemsTitle[0]; + const isOneTime = !QrCodeData.oneTime ? staticMessages.global.multipleTimes : staticMessages.global.oneTime; + + const message = `${placeType} ${placeTitle} ${staticMessages.global.and} ${staticMessages.global.canUseFor} ${isOneTime}`; + return ; +} + +function QrCodeButton(props: { QrCodeData: QrPlace; rowData: ITableRowInfra['rowData'] }) { + const { QrCodeData, rowData } = props; + const [isShowModal, setIsShowModal] = useState(false); + + if (!QrCodeData) return null; + return ( + <> + {isShowModal && ( + setIsShowModal(false)}> + + + )} + + + ); } export default function RowItem(props: IRowItemProp) { - const { title, hasCheckbox, isSelected } = props; + const { title, hasCheckbox, isSelected, rowData } = props; return (
@@ -20,7 +63,7 @@ export default function RowItem(props: IRowItemProp) { )} - {title} + {typeof title === 'string' ? title : }
); diff --git a/src/driving/application/core/places-list/infra/PlacesList.tsx b/src/driving/application/core/places-list/infra/PlacesList.tsx index b3d0422..571b93f 100644 --- a/src/driving/application/core/places-list/infra/PlacesList.tsx +++ b/src/driving/application/core/places-list/infra/PlacesList.tsx @@ -1,3 +1,5 @@ +/* eslint-disable consistent-return */ +/* eslint-disable no-underscore-dangle */ import React from 'react'; import getPlaces from '~/business-logic/core/places/get-places'; import getPlacesAdapter from '~/driven/adapters/get-places-adapter/getPlacesAdapter'; @@ -5,10 +7,29 @@ import PlacesModel from '~/business-logic/core/places/common/model/placesModel'; import { prepareStateManagementForVM } from '~/driven/utils/helpers/globalHelpers'; import useGetNavigatorAndTokenUpdater from '~/driven/utils/helpers/hooks/getNavigatorAndAccessTokenUpdator'; import AdminUserModel from '~/business-logic/generic/admin-user/common/data/model/adminUserModel'; +import { apiUrls } from '~/driven/utils/configs/appConfig'; +import { HttpOptionsType } from '~/driven/boundaries/http-boundary/protocols'; +import { HTTPPovider } from '~/driven/boundaries/http-boundary/httpBoundary'; +import { QrPlace } from '~/business-logic/core/places/common/entity/placeEntity'; import PlacesListView from '../view/PlacesListView'; import usePlacesListVM from '../viewmodel/placesListVM'; import placesListModel from '../model/placesListModel'; +type QrCodeResponse = { + one_time: false; + place_id: string; + user_id: string; + _id: string; +}[]; +const QrCodeRO = (response: QrCodeResponse): QrPlace[] => { + return response.map((qrCode) => ({ + id: qrCode._id, + oneTime: qrCode.one_time, + placeId: qrCode.place_id, + userId: qrCode.user_id, + })); +}; + const prepareTheLogicForModel = () => { const { accessTokenUpdateHandler, notLoginAuth, userData } = useGetNavigatorAndTokenUpdater(); @@ -26,12 +47,43 @@ export interface IPlacesListProps { export default function PlacessList(props: IPlacesListProps) { const { selectedRowId, setSelectedRowId } = props; - const { getingPlacesLogic, url } = prepareTheLogicForModel(); - const placesModel = async () => await placesListModel(getingPlacesLogic); + const { accessTokenUpdateHandler, notLoginAuth, userData } = useGetNavigatorAndTokenUpdater(); + const getQrCodes = async () => { + try { + // url + const apiUrl = apiUrls.core.getQrs; + // options + const apiOptions: HttpOptionsType = { + url: apiUrl, + }; + // request + const userToken = { + accessToken: userData.user?.adminUserData.accessToken || null, + refreshToken: userData.user?.adminUserData.refreshToken || null, + }; + const httpProvider = new HTTPPovider(userToken, accessTokenUpdateHandler, notLoginAuth); + const response = await httpProvider.request(apiOptions); + const qrCodeList = QrCodeRO(response); + return qrCodeList; + // update the query + } catch (error) { + console.log(error); + } + }; + + const { getingPlacesLogic, url } = prepareTheLogicForModel(); + const getPlacesWithQrLogic = async () => { + const placesList = await getingPlacesLogic(); + const qrCodesList = await getQrCodes(); + placesList.setQrFor(qrCodesList || []); + return placesList; + }; + const placesModel = async () => await placesListModel(getPlacesWithQrLogic); const useGetPlacesList = prepareStateManagementForVM(url, placesModel); const { placesData } = usePlacesListVM({ useGetPlacesList, }); + return ; } diff --git a/src/driving/application/core/places-list/view/PlacesListView.tsx b/src/driving/application/core/places-list/view/PlacesListView.tsx index 8258417..8409eb1 100644 --- a/src/driving/application/core/places-list/view/PlacesListView.tsx +++ b/src/driving/application/core/places-list/view/PlacesListView.tsx @@ -1,7 +1,6 @@ import React, { useMemo } from 'react'; import { staticMessages } from '~/driven/utils/constants/staticMessages'; import Loading from '~/driven/utils/components/loading/Loading'; -import Places from '~/business-logic/core/places/common/entity/placeEntity'; import TableRow from '../../common/table-row'; import { IPlacesListProps } from './protocols'; @@ -33,7 +32,7 @@ export default function UsersListView(props: IPlacesListProps) { ); - const tableTitles: Pick = { + const tableTitles = { name: staticMessages.global.title, placeType: staticMessages.global.placeType, qr: staticMessages.global.qrCode, diff --git a/src/driving/main/pages/index.tsx b/src/driving/main/pages/index.tsx index 8573c65..f2edfca 100644 --- a/src/driving/main/pages/index.tsx +++ b/src/driving/main/pages/index.tsx @@ -17,6 +17,7 @@ export default function index() { const { accessTokenUpdateHandler, notLoginAuth, userData } = useGetNavigatorAndTokenUpdater(); const [error, setError] = useState<{ message: string; type: 'error' | 'success' }>({ message: '', type: 'error' }); const [isSubmitDisabled, setIsSubmitDisabled] = useState(true); + const onSubmitMember = async (e: React.FormEvent) => { e.preventDefault(); try { -- 2.39.5