Skip to content

Commit

Permalink
Add sort fields, new filters and update QR scanner lib
Browse files Browse the repository at this point in the history
Signed-off-by: Tomás Castillo <[email protected]>
  • Loading branch information
tomrndom committed Jul 23, 2024
1 parent aa54656 commit d0799c7
Show file tree
Hide file tree
Showing 9 changed files with 549 additions and 73 deletions.
17 changes: 9 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
"path-browserify": "^1.0.1",
"postcss-loader": "^6.2.1",
"process": "^0.11.10",
"qr-scanner": "^1.4.2",
"react": "^16.13.1",
"react-accessible-treeview": "^2.6.2",
"react-bootstrap": "^0.31.5",
Expand Down Expand Up @@ -126,31 +127,31 @@
"xlsx": "^0.17.0"
},
"devDependencies": {
"@babel/eslint-parser": "^7.24.7",
"@testing-library/dom": "^10.1.0",
"@testing-library/jest-dom": "^6.4.2",
"@testing-library/react": "12.1.5",
"@testing-library/user-event": "^14.5.2",
"@types/react-dom": "16.9.24",
"babel-jest": "^29.7.0",
"css-minimizer-webpack-plugin": "^4.2.2",
"flush-promises": "^1.0.2",
"identity-obj-proxy": "^3.0.0",
"jest": "^29.7.0",
"jest-environment-jsdom": "^28.1.0",
"jest-transform-stub": "^2.0.0",
"redux-mock-store": "^1.5.4",
"eslint": "8.2.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.25.3",
"eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-only-warn": "^1.1.0",
"eslint-plugin-react": "^7.28.0",
"eslint-plugin-react-hooks": "^4.3.0",
"flush-promises": "^1.0.2",
"husky": "^9.0.11",
"identity-obj-proxy": "^3.0.0",
"jest": "^29.7.0",
"jest-environment-jsdom": "^28.1.0",
"jest-transform-stub": "^2.0.0",
"lint-staged": "^15.1.0",
"prettier": "^2.0.5",
"eslint-config-prettier": "^9.1.0",
"@babel/eslint-parser": "^7.24.7"
"redux-mock-store": "^1.5.4"
},
"jest": {
"collectCoverageFrom": [
Expand Down
106 changes: 82 additions & 24 deletions src/actions/order-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
* */

import T from "i18n-react/dist/i18n-react";
import history from "../history";
import {
getRequest,
putRequest,
Expand All @@ -26,10 +25,22 @@ import {
authErrorHandler,
escapeFilterValue
} from "openstack-uicore-foundation/lib/utils/actions";
import { getAccessTokenSafely } from "../utils/methods";

import URI from "urijs";

import history from "../history";
import {
checkOrFilter,
getAccessTokenSafely,
parseDateRangeFilter
} from "../utils/methods";

import {
DEFAULT_CURRENT_PAGE,
DEFAULT_EXTRA_QUESTIONS_PER_PAGE,
DEFAULT_ORDER_DIR,
DEFAULT_PER_PAGE
} from "../utils/constants";

export const REQUEST_ORDER_EXTRA_QUESTIONS = "REQUEST_ORDER_EXTRA_QUESTIONS";
export const RECEIVE_ORDER_EXTRA_QUESTIONS = "RECEIVE_ORDER_EXTRA_QUESTIONS";
export const RECEIVE_ORDER_EXTRA_QUESTION = "RECEIVE_ORDER_EXTRA_QUESTION";
Expand Down Expand Up @@ -84,7 +95,7 @@ export const ORDER_EXTRA_QUESTION_SUB_QUESTION_DELETED =
export const ORDER_EXTRA_QUESTION_SUB_QUESTION_ORDER_UPDATED =
"ORDER_EXTRA_QUESTION_SUB_QUESTION_ORDER_UPDATED";

/*********************** ORDER EXTRA QUESTIONS *******************************************/
/** ********************* ORDER EXTRA QUESTIONS ****************************************** */

export const getOrderExtraQuestionMeta = () => async (dispatch, getState) => {
const { currentSummitState } = getState();
Expand Down Expand Up @@ -137,16 +148,16 @@ export const getMainOrderExtraQuestions = () => async (dispatch, getState) => {

dispatch(startLoading());

let apiUrl = URI(
const apiUrl = URI(
`${window.API_BASE_URL}/api/v1/summits/${currentSummit.id}/order-extra-questions`
);
apiUrl.addQuery("filter[]", "class==MainQuestion");
apiUrl.addQuery("filter[]", "usage==Ticket");
apiUrl.addQuery("expand", "*sub_question_rules,*sub_question,*values");
apiUrl.addQuery("access_token", accessToken);
apiUrl.addQuery("order", "order");
apiUrl.addQuery("page", 1);
apiUrl.addQuery("per_page", 100);
apiUrl.addQuery("page", DEFAULT_CURRENT_PAGE);
apiUrl.addQuery("per_page", DEFAULT_EXTRA_QUESTIONS_PER_PAGE);

return getRequest(
null,
Expand Down Expand Up @@ -400,44 +411,91 @@ const normalizeQuestion = (entity) => {
return normalizedEntity;
};

/*************************** PURCHASE ORDERS ******************************/
const parsePurchaseOrdersFilters = (filters, term = null) => {
const filter = [];

if (filters.amount_paid_filter) {
filter.push(
`amount${filters.amount_paid_filter === "free" ? "==0" : ">0"}`
);
}

if (
filters.hasOwnProperty("company_filter") &&

Check warning on line 424 in src/actions/order-actions.js

View workflow job for this annotation

GitHub Actions / build

Do not access Object.prototype method 'hasOwnProperty' from target object

Check warning on line 424 in src/actions/order-actions.js

View workflow job for this annotation

GitHub Actions / build

Do not access Object.prototype method 'hasOwnProperty' from target object
Array.isArray(filters.company_filter) &&
filters.company_filter.length > 0
) {
filter.push(
`owner_company==${filters.company_filter
.map((c) => escapeFilterValue(c.name))
.join("||")}`
);
}

if (filters.purchase_date_filter) {
parseDateRangeFilter(filter, filters.purchase_date_filter, "created");
}

if (filters.payment_method_filter) {
filter.push(`payment_method==${filters.payment_method_filter}`);
}

if (term) {
const escapedTerm = escapeFilterValue(term);

const searchString =
`ticket_number=@${escapedTerm},` +
`ticket_owner_email=@${escapedTerm},` +
`ticket_owner_name=@${escapedTerm},` +
`number=@${escapedTerm},` +
`owner_name=@${escapedTerm},` +
`owner_email=@${escapedTerm},`;

filter.push(searchString);
}

return checkOrFilter(filters, filter);
};

/** ************************ PURCHASE ORDERS ***************************** */

export const getPurchaseOrders =
(term = null, page = 1, perPage = 10, order = "id", orderDir = 1) =>
(
term = null,
page = DEFAULT_CURRENT_PAGE,
perPage = DEFAULT_PER_PAGE,
order = "id",
orderDir = DEFAULT_ORDER_DIR,
filters = {}
) =>
async (dispatch, getState) => {
const { currentSummitState } = getState();
const accessToken = await getAccessTokenSafely();
const { currentSummit } = currentSummitState;
const filter = [];
const summitTZ = currentSummit.time_zone.name;

dispatch(startLoading());

if (term) {
const escapedTerm = escapeFilterValue(term);
filter.push(
`ticket_number=@${escapedTerm},ticket_owner_email=@${escapedTerm},ticket_owner_name=@${escapedTerm},number=@${escapedTerm},owner_name=@${escapedTerm},owner_email=@${escapedTerm},owner_company=@${escapedTerm}`
);
}

const params = {
expand: "tickets",
page: page,
page,
per_page: perPage,
access_token: accessToken
};

const filter = parsePurchaseOrdersFilters(filters, term);

if (filter.length > 0) {
params["filter[]"] = filter;
}

// order
if (order != null && orderDir != null) {
const orderDirSign = orderDir === 1 ? "+" : "-";
const orderDirSign = orderDir === DEFAULT_ORDER_DIR ? "+" : "-";
let tempOrder = order;
// order translation
if (tempOrder === "bought_date") tempOrder = "created";
params["order"] = `${orderDirSign}${tempOrder}`;
params.order = `${orderDirSign}${tempOrder}`;
}

return getRequest(
Expand Down Expand Up @@ -550,7 +608,7 @@ export const addTicketsToOrder =
"extra_questions, tickets, tickets.owner, tickets.owner.member, tickets.ticket_type"
};

let payload = {
const payload = {
ticket_type_id: typeId,
ticket_qty: qty
};
Expand Down Expand Up @@ -691,12 +749,12 @@ const normalizePurchaseOrder = (entity) => {
return normalizedEntity;
};

/*************************** Sub Questions Rules ******************************/
/** ************************* Sub Questions Rules ***************************** */

const normalizeSubRule = (entity) => {
const normalizedEntity = { ...entity };

if (entity.id === 0) delete normalizedEntity["id"];
if (entity.id === 0) delete normalizedEntity.id;

return normalizedEntity;
};
Expand Down
72 changes: 72 additions & 0 deletions src/components/qr-reader/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import * as React from "react";
import { useEffect, useRef, useState } from "react";
import QrScanner from "qr-scanner";
import styles from "./index.module.less";

const QrReader = function ({ onError, onScan }) {
// QR States
const scanner = useRef(null);
const videoEl = useRef(null);
const [qrOn, setQrOn] = useState(true);

// Success
const onScanSuccess = (result) => {
// 🖨 Print the "result" to browser console.
console.log(result);
// ✅ Handle success.
onScan(result?.data);
};

// Fail
const onScanFail = (err) => {
// 🖨 Print the "err" to browser console.
console.log(err);

// onError(err);
};

useEffect(() => {
if (videoEl?.current && !scanner.current) {
// 👉 Instantiate the QR Scanner
scanner.current = new QrScanner(videoEl?.current, onScanSuccess, {
onDecodeError: onScanFail,
preferredCamera: "environment",
highlightScanRegion: true,
highlightCodeOutline: true
});

// 🚀 Start QR Scanner
scanner?.current
?.start()
.then(() => setQrOn(true))
.catch((err) => {
if (err) setQrOn(false);
});
}

// 🧹 Clean up on unmount.
// 🚨 This removes the QR Scanner from rendering and using camera when it is closed or removed from the UI.
return () => {
if (!videoEl?.current) {
scanner?.current?.stop();
}
};
}, []);

useEffect(() => {
if (!qrOn)
alert(
"Camera is blocked or not accessible. Please allow camera in your browser permissions and Reload."
);
}, [qrOn]);

return (
<div className={styles.qrReader}>
{/* QR */}
{/* eslint-disable-next-line jsx-a11y/media-has-caption */}
<video ref={videoEl} />
</div>
);
};

export default QrReader;
32 changes: 32 additions & 0 deletions src/components/qr-reader/index.module.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
.qr-reader {
width: 300px;
height: 300px;
margin: 0 auto;
position: relative;
}

.qr-reader video {
width: 100%;
height: 100%;
object-fit: cover;
}

.qr-reader .qr-box {
width: 100% !important;
left: 0 !important;
}

.qr-reader .qr-frame {
position: absolute;
fill: none;
left: 50%;
top: 50%;
transform: translateX(-50%) translateY(-50%);
}

/* Media Queries for mobile screens */
@media (max-width: 426px) {
.qr-reader {
width: 100%;
}
}
10 changes: 8 additions & 2 deletions src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -2026,16 +2026,22 @@
"owner_id": "Owner Id",
"company": "Company",
"bought_date": "Purchase Date",
"price": "Price",
"price": "Amount Paid",
"payment_method": "Payment Method",
"status": "Status",
"tickets": "Tickets",
"paid_amount": "Amount Paid",
"no_purchase_orders": "No purchase orders found.",
"wrong_qr_title": "QR Code Error",
"wrong_qr_text": "Wrong format on QR code.",
"apply_filters": "Apply Filters",
"scan_qr": "Scan QR",
"placeholders": {
"search_orders": "Search by Order #, Buyer, Attendee "
"search_orders": "Search by Order #, Buyer, Attendee ",
"company_filter": "Filter by Company",
"payment_method_filter": "Filter by Payment Method",
"purchased_from": "Filter Purchased Date from",
"purchased_to": "Filter Purchased Date to"
}
},
"edit_purchase_order": {
Expand Down
Loading

0 comments on commit d0799c7

Please sign in to comment.