From 3ba695ce5f0c5a679faa6f2ae51d3cd5040fb496 Mon Sep 17 00:00:00 2001 From: Britney Wang Date: Tue, 16 Jan 2024 15:05:18 -0800 Subject: [PATCH 1/7] Implement /cred/shop/ in flask view --- static/js/src/advantage/credentials/app.tsx | 2 - .../components/CredExamShop/CredExamShop.tsx | 265 ------------------ .../components/CredExamShop/index.tsx | 1 - templates/credentials/shop/index.html | 210 +++++++++++++- webapp/shop/cred/exams.json | 35 +++ webapp/shop/cred/views.py | 14 +- 6 files changed, 252 insertions(+), 275 deletions(-) delete mode 100644 static/js/src/advantage/credentials/components/CredExamShop/CredExamShop.tsx delete mode 100644 static/js/src/advantage/credentials/components/CredExamShop/index.tsx create mode 100644 webapp/shop/cred/exams.json diff --git a/static/js/src/advantage/credentials/app.tsx b/static/js/src/advantage/credentials/app.tsx index dabde0d9954..9eef87ed2ce 100644 --- a/static/js/src/advantage/credentials/app.tsx +++ b/static/js/src/advantage/credentials/app.tsx @@ -6,7 +6,6 @@ import { Integrations } from "@sentry/tracing"; import { ReactQueryDevtools } from "react-query/devtools"; import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; import CredManage from "./components/CredManage"; -import CredExamShop from "./components/CredExamShop/CredExamShop"; const oneHour = 1000 * 60 * 60; const queryClient = new QueryClient({ @@ -37,7 +36,6 @@ function App() { - } /> } /> diff --git a/static/js/src/advantage/credentials/components/CredExamShop/CredExamShop.tsx b/static/js/src/advantage/credentials/components/CredExamShop/CredExamShop.tsx deleted file mode 100644 index 9b7be466f41..00000000000 --- a/static/js/src/advantage/credentials/components/CredExamShop/CredExamShop.tsx +++ /dev/null @@ -1,265 +0,0 @@ -import React, { useEffect, useState } from "react"; -import { - Button, - Col, - RadioInput, - Row, - Strip, - Spinner, -} from "@canonical/react-components"; -import classNames from "classnames"; -import { currencyFormatter } from "advantage/react/utils"; -import { Product } from "advantage/credentials/utils/utils"; -import { useQuery } from "react-query"; -import { getExamProducts } from "advantage/credentials/api/keys"; - -const CredExamShop = () => { - const ExamProductDescriptions: Product[] = [ - { - id: "cue-linux-essentials", - name: "CUE.01 Linux", - displayName: "CUE.01 Linux", - metadata: [ - { - key: "description", - value: - "Prove your basic operational knowledge of Linux by demonstrating your ability to secure, operate and maintain basic system resources. Topics include user and group management, file and filesystem navigation, and logs and installation tasks related to system maintenance.", - }, - { - key: "originalPrice", - value: "10000", - }, - ], - }, - { - id: "cue-02-desktop", - name: "CUE.02 Desktop", - displayName: "CUE.02 Desktop", - metadata: [ - { - key: "description", - value: - "Demonstrate your knowledge of Ubuntu Desktop administrative essentials. Topics include package management, system installation, data gathering, and managing printing and displays.", - }, - ], - }, - { - id: "cue-03-server", - name: "CUE.03 Server", - displayName: "CUE.03 Server", - metadata: [ - { - key: "description", - value: - "Illustrate your knowledge of common Ubuntu Server administrative tasks and troubleshooting. Topics include job control, performance tuning, services management, and Bash scripting.", - }, - ], - }, - ]; - const { isLoading, data: ExamData } = useQuery(["ExamProducts"], async () => { - return getExamProducts(); - }); - const [ExamProducts, setExamProducts] = useState([]); - const [exam, setExam] = useState(0); - const handleChange = (event: React.ChangeEvent) => { - setExam(parseInt(event.target.value)); - localStorage.setItem("exam-selector", JSON.stringify(event?.target.value)); - }; - const handleSubmit = ( - event: - | React.FormEvent - | React.MouseEvent - ) => { - event.preventDefault(); - localStorage.setItem( - "shop-checkout-data", - JSON.stringify({ - product: ExamProducts[exam], - quantity: 1, - action: "purchase", - }) - ); - location.href = "/account/checkout"; - }; - useEffect(() => { - if (ExamData === undefined) { - return; - } - for (const examDescription of ExamProductDescriptions) { - for (const exam of ExamData) { - if (exam.id === examDescription.id) { - Object.assign(examDescription, exam); - } - } - } - setExamProducts(ExamProductDescriptions); - }, [ExamData]); - if (isLoading) { - return ; - } - - return ( - <> - - -

Select an exam to purchase

-
- - {ExamProducts.map((examElement, examIndex) => { - return ( - -
- -
- - ); - })} -
- {ExamProducts.map((examElement, examIndex) => { - return ( - -
- - -

- {examElement.metadata[0].value} -

-
- -
- {examElement.price === undefined ? ( - Coming Soon - ) : ( - - Price:{" "} - {examElement.metadata[1].value !== undefined ? ( - - {currencyFormatter.format( - parseInt(examElement.metadata[1].value) / 100 - )} - - ) : ( - <> - )}{" "} - {currencyFormatter.format( - examElement.price.value / 100 - )} - - )} -
-
-
- - ); - })} -
-
- - - Your Order - - - - -

- {ExamProducts[exam]?.displayName} -

- - -

- {currencyFormatter.format( - (ExamProducts[exam]?.price?.value ?? 0) / 100 ?? 0 - )} -

- - - - -
-
- - ); -}; -export default CredExamShop; diff --git a/static/js/src/advantage/credentials/components/CredExamShop/index.tsx b/static/js/src/advantage/credentials/components/CredExamShop/index.tsx deleted file mode 100644 index 5a773316687..00000000000 --- a/static/js/src/advantage/credentials/components/CredExamShop/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./CredExamShop"; diff --git a/templates/credentials/shop/index.html b/templates/credentials/shop/index.html index 032b7e4e8b3..fe3a9ba4efd 100644 --- a/templates/credentials/shop/index.html +++ b/templates/credentials/shop/index.html @@ -20,10 +20,208 @@ - - +
+
+
+

Select an exam to purchase

+
+
+ {% for exam in exams %} +
+
+ +
+
+ {% endfor %} +
+ + + {% for exam in exams %} +
+
+ + +

+ {{ exam.metadata[0].value }} +

+
+ +
+ {% if exam.price is not defined %} + Coming Soon! + {% else %} + Price: {{ exam.price.currency }} {{ exam.price.value }} + {% endif %} +
+
+
+
+ {% endfor %} +
+ +
+
+
+ Your Order +
+
+
+
+

+ {{ exams[exam_index].name }} +

+
+
+

+ Price: {{ exams[exam_index].price.currency }} {{ exams[exam_index].price.value }} +

+
+
+ + +
+
+
+
+ + + {% endblock %} diff --git a/webapp/shop/cred/exams.json b/webapp/shop/cred/exams.json new file mode 100644 index 00000000000..33193793e58 --- /dev/null +++ b/webapp/shop/cred/exams.json @@ -0,0 +1,35 @@ +[ + { + "id": "cue-linux-essentials", + "name": "CUE Linux Essentials", + "metadata": [ + { + "key": "description", + "value": + "Prove your basic operational knowledge of Linux by demonstrating your ability to secure, operate and maintain basic system resources. Topics include user and group management, file and filesystem navigation, and logs and installation tasks related to system maintenance." + } + ] + }, + { + "id": "cue-02-desktop", + "name": "CUE Desktop", + "metadata": [ + { + "key": "description", + "value": + "Demonstrate your knowledge of Ubuntu Desktop administrative essentials. Topics include package management, system installation, data gathering, and managing printing and displays." + } + ] + }, + { + "id": "cue-03-server", + "name": "CUE Server", + "metadata": [ + { + "key": "description", + "value": + "Illustrate your knowledge of common Ubuntu Server administrative tasks and troubleshooting. Topics include job control, performance tuning, services management, and Bash scripting." + } + ] + } +] \ No newline at end of file diff --git a/webapp/shop/cred/views.py b/webapp/shop/cred/views.py index 952e33131a5..e4519e99412 100644 --- a/webapp/shop/cred/views.py +++ b/webapp/shop/cred/views.py @@ -856,7 +856,19 @@ def cred_submit_form(**_): @shop_decorator(area="cube", permission="user", response="html") @canonical_staff() def cred_shop(**kwargs): - return flask.render_template("credentials/shop/index.html") + exams_file = open("webapp/shop/cred/exams.json", "r") + exams = json.load(exams_file) + cue_products = get_cue_products(type="exam").json + + for exam in exams: + id = exam["id"] + for product in cue_products: + if id == product["id"]: + exam["price"] = product["price"] + + return flask.render_template( + "credentials/shop/index.html", exams=exams, exam_index=0 + ) @shop_decorator(area="cube", permission="user", response="html") From ae959d164d7fc64eab9a77024212b89b9d2f7606 Mon Sep 17 00:00:00 2001 From: Britney Wang Date: Wed, 17 Jan 2024 11:14:15 -0800 Subject: [PATCH 2/7] Remove unused functions --- static/js/src/advantage/credentials/api/keys.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/static/js/src/advantage/credentials/api/keys.js b/static/js/src/advantage/credentials/api/keys.js index 1364c78a17e..4fe67291884 100644 --- a/static/js/src/advantage/credentials/api/keys.js +++ b/static/js/src/advantage/credentials/api/keys.js @@ -24,15 +24,3 @@ export async function rotateKey(activationKey) { const data = await response.json(); return data; } - -export async function getExamProducts() { - let response = await fetch("/credentials/exam/products", { - method: "GET", - headers: { - Accept: "application/json", - "Content-Type": "application/json", - }, - }); - const data = await response.json(); - return data; -} From 3491216aca6b45bcadc19c7aedfba566c5555330 Mon Sep 17 00:00:00 2001 From: Britney Wang Date: Wed, 17 Jan 2024 15:57:37 -0800 Subject: [PATCH 3/7] Remove react scripts --- templates/credentials/shop/index.html | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/templates/credentials/shop/index.html b/templates/credentials/shop/index.html index fe3a9ba4efd..6ce3c3ee1ea 100644 --- a/templates/credentials/shop/index.html +++ b/templates/credentials/shop/index.html @@ -8,18 +8,6 @@ {% endblock meta_description %} {% block content %} -
-
-
-
-
- Loading… -
-
-
-
-
-
@@ -132,7 +120,6 @@

- @@ -141,9 +128,9 @@
- + + const handleSubmit = () => { + event.preventDefault(); + const index = document.querySelector('input[name="exam-radio"]:checked').value; + + localStorage.setItem( + "shop-checkout-data", + JSON.stringify({ + product: exams[index], + quantity: 1, + action: "purchase", + }) + ); + location.href = "/account/checkout"; + }; + {% endblock %} From 4e0c918f6cdece8e1e00a225475ecf052f744ab4 Mon Sep 17 00:00:00 2001 From: usamabinnadeem-10 Date: Tue, 11 Jun 2024 23:56:21 +0500 Subject: [PATCH 6/7] remove javascript dependency for template --- templates/credentials/shop/index.html | 167 +++++++++++--------------- webapp/shop/cred/exams.json | 11 +- webapp/shop/cred/views.py | 12 +- 3 files changed, 81 insertions(+), 109 deletions(-) diff --git a/templates/credentials/shop/index.html b/templates/credentials/shop/index.html index 8be35f04002..d531c609ca9 100644 --- a/templates/credentials/shop/index.html +++ b/templates/credentials/shop/index.html @@ -15,24 +15,39 @@

Select an exam to purchase

{% for exam in exams %} + {% set metadata_length = exam.metadata|length %} + {% set is_disabled = metadata_length == 1 %} + {% set is_discounted = True if metadata_length > 1 and exam.metadata[1].discountPrice else False %}
-
+
@@ -43,23 +58,36 @@

Select an exam to purchase

{% for exam in exams %} + {% set metadata_length = exam.metadata|length %} + {% set is_disabled = metadata_length == 1 %} + {% set is_discounted = True if metadata_length > 1 and exam.metadata[1].discountPrice else False %}

{{ exam.metadata[0].value }}

-
- +
+ {% if is_disabled %} + Coming Soon! + {% else %} + Price: + {% if is_discounted %}{{ exam.metadata[1].currency }}{{ exam.metadata[1].originalPrice }}{% endif %} + {{ exam.metadata[1].currency }} + {% if is_discounted %} + {{ exam.metadata[1].discountPrice }} + {% else %} + {{ exam.metadata[1].originalPrice }} + {% endif %} + + {% endif %}
@@ -73,14 +101,22 @@

Select an exam to purchase

Your Order
+ {% set exam = exams[exam_index] %} + {% set is_discounted = True if exam.metadata|length > 1 and exam.metadata[1].discountPrice else False %}

+ style="margin-block: auto">{{ exam.displayName }}

- + + {% if is_discounted %} + {{ exam.metadata[1].currency }}{{ exam.metadata[1].discountPrice }} + {% else %} + {{ exam.metadata[1].currency }}{{ exam.metadata[1].originalPrice }} + {% endif %} +

@@ -93,106 +129,37 @@

Select an exam to purchase