diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 588b91c..791f285 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,7 @@ on: pull_request: concurrency: - group: develop-sa_ecom-${{ github.event.number }} + group: develop-ls_shop-${{ github.event.number }} cancel-in-progress: true jobs: @@ -90,7 +90,7 @@ jobs: - name: Install working-directory: /home/runner/frappe-bench run: | - bench get-app sa_ecom $GITHUB_WORKSPACE + bench get-app ls_shop $GITHUB_WORKSPACE bench setup requirements --dev bench get-app erpnext bench get-app payments @@ -98,7 +98,7 @@ jobs: bench new-site --db-root-password root --admin-password admin test_site bench --site test_site install-app erpnext bench --site test_site install-app webshop - bench --site test_site install-app sa_ecom + bench --site test_site install-app ls_shop bench build env: CI: 'Yes' @@ -107,6 +107,6 @@ jobs: working-directory: /home/runner/frappe-bench run: | bench --site test_site set-config allow_tests true - bench --site test_site run-tests --app sa_ecom + bench --site test_site run-tests --app ls_shop env: TYPE: server diff --git a/.gitignore b/.gitignore index b50d332..adbec9d 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,5 @@ jspm_packages/ # Aider AI Chat .aider* + +ls_shop/public/css/tailwind.css diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 59bf4cf..60d19e6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,22 +1,4 @@ -exclude: 'node_modules|.git' -default_stages: [pre-commit] -fail_fast: false - - repos: - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 - hooks: - - id: trailing-whitespace - files: "ls_shop.*" - exclude: ".*json$|.*txt$|.*csv|.*md|.*svg" - - id: check-merge-conflict - - id: check-ast - - id: check-json - - id: check-toml - - id: check-yaml - - id: debug-statements - - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.8.1 hooks: @@ -30,40 +12,8 @@ repos: - id: ruff-format name: "Run ruff formatter" - - repo: https://github.com/pre-commit/mirrors-prettier - rev: v2.7.1 - hooks: - - id: prettier - types_or: [javascript, vue, scss] - # Ignore any files that might contain jinja / bundles - exclude: | - (?x)^( - ls_shop/public/dist/.*| - .*node_modules.*| - .*boilerplate.*| - ls_shop/templates/includes/.*| - ls_shop/public/js/lib/.* - )$ - - - - repo: https://github.com/pre-commit/mirrors-eslint - rev: v8.44.0 - hooks: - - id: eslint - types_or: [javascript] - args: ['--quiet'] - # Ignore any files that might contain jinja / bundles - exclude: | - (?x)^( - ls_shop/public/dist/.*| - cypress/.*| - .*node_modules.*| - .*boilerplate.*| - ls_shop/templates/includes/.*| - ls_shop/public/js/lib/.* - )$ - -ci: - autoupdate_schedule: weekly - skip: [] - submodules: false + - repo: https://github.com/biomejs/pre-commit + rev: "v0.1.0" # Use the sha / tag you want to point at + hooks: + - id: biome-check + additional_dependencies: ["@biomejs/biome@1.4.1"] \ No newline at end of file diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..0f73058 --- /dev/null +++ b/biome.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", + "vcs": { + "enabled": false, + "clientKind": "git", + "useIgnoreFile": false + }, + "files": { + "ignoreUnknown": false, + "ignore": ["public/js/vendor/**/*"] + }, + "formatter": { + "enabled": true, + "indentStyle": "tab" + }, + "organizeImports": { + "enabled": true + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true + } + }, + "javascript": { + "formatter": { + "quoteStyle": "single" + } + } +} diff --git a/commitlint.config.js b/commitlint.config.js new file mode 100644 index 0000000..6f8bb17 --- /dev/null +++ b/commitlint.config.js @@ -0,0 +1,27 @@ +module.exports = { + parserPreset: 'conventional-changelog-conventionalcommits', + rules: { + 'subject-empty': [2, 'never'], + 'type-case': [2, 'always', 'lower-case'], + 'type-empty': [2, 'never'], + 'type-enum': [ + 2, + 'always', + [ + 'build', + 'chore', + 'ci', + 'docs', + 'feat', + 'fix', + 'perf', + 'refactor', + 'revert', + 'style', + 'test', + 'deprecate', // deprecation decision + 'wip', + ], + ], + }, +}; diff --git a/ls_shop/api/cart.py b/ls_shop/api/cart.py index 6aae310..9a371d3 100644 --- a/ls_shop/api/cart.py +++ b/ls_shop/api/cart.py @@ -12,15 +12,11 @@ @frappe.whitelist(allow_guest=True) def get_detail_for_cart_items(items): stock_data = {} - warehouse = frappe.get_cached_value( - "Lifestyle Settings", "Lifestyle Settings", "ecommerce_warehouse" - ) + warehouse = frappe.get_cached_value("Lifestyle Settings", "Lifestyle Settings", "ecommerce_warehouse") default_price_list = frappe.get_cached_value( "Lifestyle Settings", "Lifestyle Settings", "default_price_list" ) - sale_price_list = frappe.get_cached_value( - "Lifestyle Settings", "Lifestyle Settings", "sale_price_list" - ) + sale_price_list = frappe.get_cached_value("Lifestyle Settings", "Lifestyle Settings", "sale_price_list") default_price = 0 sale_price = 0 for entry in items: @@ -61,9 +57,7 @@ def get_detail_for_cart_items(items): @frappe.whitelist(allow_guest=True) def validate_cart_stock(items): errors = [] - warehouse = frappe.get_cached_value( - "Lifestyle Settings", "Lifestyle Settings", "ecommerce_warehouse" - ) + warehouse = frappe.get_cached_value("Lifestyle Settings", "Lifestyle Settings", "ecommerce_warehouse") for entry in items: item_code = entry.get("variant", {}).get("item_code") item_name = entry.get("item", {}).get("display_name") diff --git a/ls_shop/api/payments.py b/ls_shop/api/payments.py index bc99167..6e5e5a9 100644 --- a/ls_shop/api/payments.py +++ b/ls_shop/api/payments.py @@ -22,9 +22,7 @@ class PaymentMode(StrEnum): @frappe.whitelist() def initiate_checkout_with_mode(payment_mode: PaymentMode): lifestyle_settings = frappe.get_cached_doc("Lifestyle Settings") - if payment_mode not in set(PaymentMode) or not lifestyle_settings.get( - f"{payment_mode}_enabled" - ): + if payment_mode not in set(PaymentMode) or not lifestyle_settings.get(f"{payment_mode}_enabled"): frappe.throw(frappe._("Please select a valid payment mode.")) quotation = _get_cart_quotation() @@ -47,9 +45,7 @@ def initiate_checkout_with_mode(payment_mode: PaymentMode): { "doctype": "Telr Payment Request", "amount": quotation.rounded_total, - "currency_code": "SAR" - if frappe.conf.developer_mode - else quotation.currency, + "currency_code": "SAR" if frappe.conf.developer_mode else quotation.currency, "ref_doctype": quotation.doctype, "ref_docname": quotation.name, "customer_ref": quotation.party_name, @@ -66,9 +62,7 @@ def initiate_checkout_with_mode(payment_mode: PaymentMode): { "doctype": "Tabby Payment Request", "amount": quotation.rounded_total, - "currency_code": "SAR" - if frappe.conf.developer_mode - else quotation.currency, + "currency_code": "SAR" if frappe.conf.developer_mode else quotation.currency, "ref_doctype": quotation.doctype, "ref_docname": quotation.name, "customer_ref": quotation.party_name, @@ -93,9 +87,7 @@ def generate_quotation_for_cart(cart: dict): def get_quotation_for_cart(cart: dict): unsaved_quotation_doc = _get_cart_quotation() - sale_price_list = frappe.get_cached_value( - "Lifestyle Settings", "Lifestyle Settings", "sale_price_list" - ) + sale_price_list = frappe.get_cached_value("Lifestyle Settings", "Lifestyle Settings", "sale_price_list") ecommerce_warehouse = frappe.get_cached_value( "Lifestyle Settings", "Lifestyle Settings", "ecommerce_warehouse" ) @@ -119,9 +111,7 @@ def get_quotation_for_cart(cart: dict): def set_charges(quotation): - shipping_rule = frappe.get_cached_value( - "Lifestyle Settings", "Lifestyle Settings", "shipping_rule" - ) + shipping_rule = frappe.get_cached_value("Lifestyle Settings", "Lifestyle Settings", "shipping_rule") if shipping_rule: quotation.shipping_rule = shipping_rule quotation.run_method("apply_shipping_rule") @@ -130,9 +120,7 @@ def set_charges(quotation): def set_cod_charges(quotation): cod_charges_applicable_below, cod_charge = get_cod_configuration() - account_head = frappe.get_cached_value( - "Lifestyle Settings", "Lifestyle Settings", "charge_account_head" - ) + account_head = frappe.get_cached_value("Lifestyle Settings", "Lifestyle Settings", "charge_account_head") if not cod_charges_applicable_below or not cod_charge: return if cod_charges_applicable_below < quotation.rounded_total: @@ -224,23 +212,17 @@ def confirm_payment(payment_mode: PaymentMode, reference_id: str): if payment_request.status == "Paid": quote_name = payment_request.ref_docname - submit_quotation_and_create_order( - quote_name, payment_mode, payment_request.telr_order_ref - ) + submit_quotation_and_create_order(quote_name, payment_mode, payment_request.telr_order_ref) return payment_request if payment_mode == PaymentMode.TABBY: - payment_request = frappe.get_doc( - "Tabby Payment Request", {"tabby_payment_id": reference_id} - ) + payment_request = frappe.get_doc("Tabby Payment Request", {"tabby_payment_id": reference_id}) payment_request.sync_status() if payment_request.status == "AUTHORIZED": quote_name = payment_request.ref_docname payment_request.capture_payment() - submit_quotation_and_create_order( - quote_name, payment_mode, payment_request.tabby_order_ref - ) + submit_quotation_and_create_order(quote_name, payment_mode, payment_request.tabby_order_ref) return payment_request @@ -257,9 +239,7 @@ def submit_quotation_and_create_order( so = _make_sales_order(quote_name, ignore_permissions=True) so.custom_ecommerce_payment_mode = ( - payment_mode.title() - if not payment_mode == payment_mode.COD - else payment_mode.upper() + payment_mode.title() if not payment_mode == payment_mode.COD else payment_mode.upper() ) so.flags.ignore_permissions = True so.insert() @@ -267,13 +247,9 @@ def submit_quotation_and_create_order( if payment_mode != payment_mode.COD: so.submit() payment_request_doctype = ( - "Telr Payment Request" - if payment_mode == PaymentMode.TELR - else "Tabby Payment Request" - ) - payment_order_ref_field = ( - "telr_order_ref" if payment_mode == PaymentMode.TELR else "tabby_order_ref" + "Telr Payment Request" if payment_mode == PaymentMode.TELR else "Tabby Payment Request" ) + payment_order_ref_field = "telr_order_ref" if payment_mode == PaymentMode.TELR else "tabby_order_ref" payment_request = frappe.get_doc( payment_request_doctype, {payment_order_ref_field: payment_reference} ) @@ -282,9 +258,7 @@ def submit_quotation_and_create_order( payment_request.ref_doctype = "Sales Order" payment_request.save() frappe.set_user("Administrator") - pe = get_payment_entry( - "Sales Order", so.name, reference_date=frappe.utils.today() - ) + pe = get_payment_entry("Sales Order", so.name, reference_date=frappe.utils.today()) pe.flags.ignore_permissions = True pe.mode_of_payment = payment_mode.title() pe.reference_no = payment_reference @@ -297,9 +271,7 @@ def apply_coupon_code(applied_code): quotation = True if not applied_code: frappe.throw(frappe._("Please enter a coupon code")) - coupon_name = frappe.db.get_value( - "Coupon Code", {"coupon_code": applied_code}, "name" - ) + coupon_name = frappe.db.get_value("Coupon Code", {"coupon_code": applied_code}, "name") if not coupon_name: frappe.throw(frappe._("Please enter a valid coupon code")) validate_coupon_code(coupon_name) diff --git a/ls_shop/api/return.py b/ls_shop/api/return.py index 54e8b8f..76727a8 100644 --- a/ls_shop/api/return.py +++ b/ls_shop/api/return.py @@ -45,11 +45,7 @@ def return_items(sales_order_id, items): ) if not return_dn.items: - frappe.throw( - frappe._( - "None of the items to return were found in the original Delivery Note." - ) - ) + frappe.throw(frappe._("None of the items to return were found in the original Delivery Note.")) return_dn.insert(ignore_permissions=True) @@ -95,9 +91,7 @@ def get_returned_items(sales_order_id): returned_draft_item_codes = [item.item_code for item in returned_items_in_draft] - is_fully_returned = all( - item_code in returned_item_codes for item_code in ordered_item_codes - ) + is_fully_returned = all(item_code in returned_item_codes for item_code in ordered_item_codes) partially_returned = len(returned_item_codes) > 0 and not is_fully_returned return { diff --git a/ls_shop/api/translation.py b/ls_shop/api/translation.py index 5b17bbe..99aeacc 100644 --- a/ls_shop/api/translation.py +++ b/ls_shop/api/translation.py @@ -3,9 +3,7 @@ @frappe.whitelist(allow_guest=True) def get_translations(): - translations = frappe.db.get_all( - "Translation", fields=["language", "source_text", "translated_text"] - ) + translations = frappe.db.get_all("Translation", fields=["language", "source_text", "translated_text"]) translation_dict = {} @@ -17,8 +15,6 @@ def get_translations(): if lang not in translation_dict: translation_dict[lang] = {} for i in range(min(len(source_texts), len(translated_texts))): - translation_dict[lang][source_texts[i].strip()] = translated_texts[ - i - ].strip() + translation_dict[lang][source_texts[i].strip()] = translated_texts[i].strip() return translation_dict diff --git a/ls_shop/api/utils.py b/ls_shop/api/utils.py index 4564bcc..c0931cd 100644 --- a/ls_shop/api/utils.py +++ b/ls_shop/api/utils.py @@ -51,22 +51,14 @@ def get_product_detail(item_code): context = website_item.get_context(context) recommended_items = [] for item in website_item.get("recommended_items"): - recommended_website_item = frappe.get_doc( - "Website Item", {"name": item.get("website_item")} - ) + recommended_website_item = frappe.get_doc("Website Item", {"name": item.get("website_item")}) recommended_item_context = frappe._dict( - { - "route": recommended_website_item.route - or recommended_website_item.make_route() - } - ) - recommended_item_context = recommended_website_item.get_context( - recommended_item_context + {"route": recommended_website_item.route or recommended_website_item.make_route()} ) + recommended_item_context = recommended_website_item.get_context(recommended_item_context) recommended_item_info = { "id": recommended_website_item.get("name"), - "brand": recommended_website_item.get("brand") - or recommended_website_item.get("item_group"), + "brand": recommended_website_item.get("brand") or recommended_website_item.get("item_group"), "product_name": recommended_website_item.get("item_name") or recommended_website_item.get("web_item_name"), "item_code": recommended_website_item.get("item_code"), @@ -82,8 +74,7 @@ def get_product_detail(item_code): product = { "id": website_item.get("name"), "brand": website_item.get("brand") or website_item.get("item_group"), - "product_name": website_item.get("item_name") - or website_item.get("web_item_name"), + "product_name": website_item.get("item_name") or website_item.get("web_item_name"), "item_code": website_item.get("item_code"), "has_variants": website_item.get("has_variants"), "description": website_item.get("description"), @@ -94,12 +85,8 @@ def get_product_detail(item_code): "recommended_items": recommended_items, "offers": website_item.get("offers"), "slides": context.get("slides"), - "price_info": context.get("shopping_cart") - .get("product_info", {}) - .get("price", {}), - "stock_qty": context.get("shopping_cart") - .get("product_info", {}) - .get("stock_qty", {}), + "price_info": context.get("shopping_cart").get("product_info", {}).get("price", {}), + "stock_qty": context.get("shopping_cart").get("product_info", {}).get("stock_qty", {}), # "attributes": get_attributes_and_values() } product["attributes"] = get_attributes_and_values(product["product_name"]) @@ -129,9 +116,7 @@ def get_whitelist_transaction_list( order_by="modified", custom=False, ): - return get_transaction_list( - doctype, txt, filters, limit_start, limit_page_length, order_by, custom - ) + return get_transaction_list(doctype, txt, filters, limit_start, limit_page_length, order_by, custom) @frappe.whitelist(allow_guest=True) @@ -148,22 +133,14 @@ def get_item_details(items): recommended_items = [] for item in items: - recommended_website_item = frappe.get_doc( - "Website Item", {"name": item.get("website_item")} - ) + recommended_website_item = frappe.get_doc("Website Item", {"name": item.get("website_item")}) recommended_item_context = frappe._dict( - { - "route": recommended_website_item.route - or recommended_website_item.make_route() - } - ) - recommended_item_context = recommended_website_item.get_context( - recommended_item_context + {"route": recommended_website_item.route or recommended_website_item.make_route()} ) + recommended_item_context = recommended_website_item.get_context(recommended_item_context) recommended_item_info = { "id": recommended_website_item.get("name"), - "brand": recommended_website_item.get("brand") - or recommended_website_item.get("item_group"), + "brand": recommended_website_item.get("brand") or recommended_website_item.get("item_group"), "product_name": recommended_website_item.get("item_name") or recommended_website_item.get("web_item_name"), "item_code": recommended_website_item.get("item_code"), diff --git a/ls_shop/core.py b/ls_shop/core.py index 9c11496..610f075 100644 --- a/ls_shop/core.py +++ b/ls_shop/core.py @@ -19,6 +19,4 @@ def send_otp(email): print(f"OTP for {email}: {otp}") return - frappe.sendmail( - recipients=email, subject="Your OTP", message=f"Your OTP: {otp}", now=True - ) + frappe.sendmail(recipients=email, subject="Your OTP", message=f"Your OTP: {otp}", now=True) diff --git a/ls_shop/hooks.py b/ls_shop/hooks.py index ad1c1bd..fe8a872 100644 --- a/ls_shop/hooks.py +++ b/ls_shop/hooks.py @@ -29,7 +29,7 @@ # ------------ # # -> Landing - {"from_route": "/en", "to_route": "/products/list.html"}, + {"from_route": "/en", "to_route": "/index.html"}, # -> Products {"from_route": "/en/products", "to_route": "/products/list.html"}, {"from_route": "/en/products/", "to_route": "/products/details.html"}, @@ -56,7 +56,7 @@ # ------------ # # -> Landing - {"from_route": "/ar", "to_route": "/products/list.html"}, + {"from_route": "/ar", "to_route": "/index.html"}, # -> Products {"from_route": "/ar/products", "to_route": "/products/list.html"}, {"from_route": "/ar/products/", "to_route": "/products/details.html"}, @@ -95,7 +95,6 @@ }, "Payment Entry": { "on_submit": [ - "ls_shop.lifestyle_shop_ecommerce.doctype.telr_payment_request.telr_payment_request.refund_payment_for_payment_entry", ], }, @@ -109,9 +108,7 @@ "ls_shop.utils.update_so_status_from_related_doc", ], }, - "Stock Ledger Entry": { - "after_insert": ["ls_shop.jobs.send_product_back_in_stock_email"] - }, + "Stock Ledger Entry": {"after_insert": ["ls_shop.jobs.send_product_back_in_stock_email"]}, "Sales Invoice": {"on_submit": "ls_shop.utils.update_so_status_from_related_doc"}, "Delivery Note": {"on_submit": "ls_shop.utils.update_so_status_from_related_doc"}, "Shipment": {"on_submit": "ls_shop.utils.update_so_status_from_related_doc"}, diff --git a/ls_shop/jobs.py b/ls_shop/jobs.py index 7ec7392..c47089b 100644 --- a/ls_shop/jobs.py +++ b/ls_shop/jobs.py @@ -6,9 +6,7 @@ def get_cc_email(): - return frappe.get_cached_value( - "Lifestyle Settings", "Lifestyle Settings", "cc_email" - ) + return frappe.get_cached_value("Lifestyle Settings", "Lifestyle Settings", "cc_email") def send_order_success_acknowledgement(doc, method): @@ -19,19 +17,13 @@ def send_order_success_acknowledgement(doc, method): "Lifestyle Settings", "order_confirmation_email_template", ) - email_template = frappe.get_doc( - "Email Template", order_confirmation_template_name - ) + email_template = frappe.get_doc("Email Template", order_confirmation_template_name) message = frappe.render_template(email_template.response_, doc_args) subject = frappe.render_template(email_template.subject, doc_args) - emails = frappe.get_all( - "Portal User", {"parent": doc.customer}, ["user"], limit=1 - ) + emails = frappe.get_all("Portal User", {"parent": doc.customer}, ["user"], limit=1) email = emails[0].get("user", "") - frappe.sendmail( - recipients=[email], subject=subject, message=message, cc=[get_cc_email()] - ) + frappe.sendmail(recipients=[email], subject=subject, message=message, cc=[get_cc_email()]) except Exception as e: create_request_log( data=doc_args, @@ -49,19 +41,13 @@ def send_order_cancel_acknowledgement(doc, method): "Lifestyle Settings", "order_cancellation_email_template", ) - email_template = frappe.get_doc( - "Email Template", order_confirmation_template_name - ) + email_template = frappe.get_doc("Email Template", order_confirmation_template_name) message = frappe.render_template(email_template.response_, doc_args) subject = frappe.render_template(email_template.subject, doc_args) - emails = frappe.get_all( - "Portal User", {"parent": doc.customer}, ["user"], limit=1 - ) + emails = frappe.get_all("Portal User", {"parent": doc.customer}, ["user"], limit=1) email = emails[0].get("user", "") - frappe.sendmail( - recipients=[email], subject=subject, message=message, cc=[get_cc_email()] - ) + frappe.sendmail(recipients=[email], subject=subject, message=message, cc=[get_cc_email()]) except Exception as e: create_request_log( data=doc_args, @@ -155,9 +141,7 @@ def notify_users_if_item_in_stock(item_code): doc_args = { "item_code": style_attribute_variant.display_name, "company": "Lifestyle", - "website_url": get_url( - f"{frappe.local.lang}/products/{style_attribute_variant.route}" - ), + "website_url": get_url(f"{frappe.local.lang}/products/{style_attribute_variant.route}"), } message = frappe.render_template(email_template.response_, doc_args) subject = frappe.render_template(email_template.subject, doc_args) @@ -169,9 +153,7 @@ def notify_users_if_item_in_stock(item_code): ) for user in user_subscriptions: try: - frappe.sendmail( - recipients=[user], subject=subject, message=message, now=True - ) + frappe.sendmail(recipients=[user], subject=subject, message=message, now=True) frappe.db.set_value( "OOS Notify Subscription", {"item": item_code, "user": user}, @@ -207,8 +189,4 @@ def delete_old_draft_quotations(): try: frappe.delete_doc("Quotation", quotation) except Exception as e: - frappe.log_error( - title=frappe._("Error in deleting the quotation {0}: {1}").format( - quotation, e - ) - ) + frappe.log_error(title=frappe._("Error in deleting the quotation {0}: {1}").format(quotation, e)) diff --git a/ls_shop/lifestyle_shop_ecommerce/doctype/bulk_image_upload/bulk_image_upload.py b/ls_shop/lifestyle_shop_ecommerce/doctype/bulk_image_upload/bulk_image_upload.py index 18ea71b..402d001 100644 --- a/ls_shop/lifestyle_shop_ecommerce/doctype/bulk_image_upload/bulk_image_upload.py +++ b/ls_shop/lifestyle_shop_ecommerce/doctype/bulk_image_upload/bulk_image_upload.py @@ -94,15 +94,11 @@ def import_images_for_folder( reference_name=self.name, ) - def import_images_in_variant( - self, variant_doc, folder_name: str, zip_file: "zipfile.ZipFile" - ): + def import_images_in_variant(self, variant_doc, folder_name: str, zip_file: "zipfile.ZipFile"): import_count = 0 image_files = sorted(zip_file.namelist()) for image_file in image_files: - if image_file.startswith(folder_name) and image_file.endswith( - (".png", ".jpg", ".jpeg", ".webp") - ): + if image_file.startswith(folder_name) and image_file.endswith((".png", ".jpg", ".jpeg", ".webp")): image_data = zip_file.read(image_file) image_data = io.BytesIO(image_data) diff --git a/ls_shop/lifestyle_shop_ecommerce/doctype/bulk_publish_variants/bulk_publish_variants.py b/ls_shop/lifestyle_shop_ecommerce/doctype/bulk_publish_variants/bulk_publish_variants.py index 3afda04..d2e57f6 100644 --- a/ls_shop/lifestyle_shop_ecommerce/doctype/bulk_publish_variants/bulk_publish_variants.py +++ b/ls_shop/lifestyle_shop_ecommerce/doctype/bulk_publish_variants/bulk_publish_variants.py @@ -50,32 +50,21 @@ def bulk_toggle_publish( .on(item_attr.parent == child_item.name) ) if style_attribute_variant_list: - query = query.where( - style_attribute_variant.name.isin(style_attribute_variant_list) - ) + query = query.where(style_attribute_variant.name.isin(style_attribute_variant_list)) if self.vendor_code: query = query.where( (style_item.custom_vendor_code == self.vendor_code) | (child_item.custom_vendor_code == self.vendor_code) ) if self.dcs: - query = query.where( - (style_item.custom_dcs == self.dcs) - | (child_item.custom_dcs == self.dcs) - ) + query = query.where((style_item.custom_dcs == self.dcs) | (child_item.custom_dcs == self.dcs)) if self.brand: - query = query.where( - (style_item.brand == self.brand) | (child_item.brand == self.brand) - ) + query = query.where((style_item.brand == self.brand) | (child_item.brand == self.brand)) if self.item_code: - query = query.where( - (style_item.name == self.item_code) - | (style_item.name == self.item_code) - ) + query = query.where((style_item.name == self.item_code) | (style_item.name == self.item_code)) if self.season_code: query = query.where( - (item_attr.attribute == "Season") - & (item_attr.attribute_value == self.season_code) + (item_attr.attribute == "Season") & (item_attr.attribute_value == self.season_code) ) query = query.select(style_attribute_variant.name) diff --git a/ls_shop/lifestyle_shop_ecommerce/doctype/landing_page_settings/landing_page_settings.json b/ls_shop/lifestyle_shop_ecommerce/doctype/landing_page_settings/landing_page_settings.json index 06e9eca..4123e9a 100644 --- a/ls_shop/lifestyle_shop_ecommerce/doctype/landing_page_settings/landing_page_settings.json +++ b/ls_shop/lifestyle_shop_ecommerce/doctype/landing_page_settings/landing_page_settings.json @@ -33,46 +33,17 @@ "gif_url_2_ar", "gif_url_4_ar", "section_break_hdto", - "browse_by_brands", - "browse_by_brands_ar", "new_arrivals", - "section_break_xmqq", - "banner_1", - "banner_url_1", - "column_break_gzsu", - "banner_2", - "banner_url_2", - "banner_ar_section", - "banner_1_ar", - "banner_url_1_ar", - "column_break_sbpz", - "banner_2_ar", - "banner_url_2_ar", "section_break_dyhp", "best_picks", - "section_break_dehn", - "banner_3", - "column_break_nzln", - "banner_url_3", - "banner_section_2_ar_section", - "banner_3_ar", - "column_break_pwrs", - "banner_url_3_ar", - "section_break_jqas", - "banner_4", - "banner_url_4", - "banner_4_ar", - "banner_url_4_ar", - "column_break_nbcp", - "other_products", "section_break_tgjx", - "banner_5", + "banner_1", "column_break_mscz", - "banner_url_5", + "banner_url_1", "banner_section_4_ar_section", - "banner_5_ar", + "banner_1_ar", "column_break_irta", - "banner_url_5_ar" + "banner_url_1_ar" ], "fields": [ { @@ -82,6 +53,7 @@ "options": "Landing Page Hero Banner" }, { + "description": "Brands or Categories", "fieldname": "section_break_uazs", "fieldtype": "Section Break", "label": "GIFs" @@ -114,33 +86,12 @@ "fieldname": "section_break_hdto", "fieldtype": "Section Break" }, - { - "fieldname": "browse_by_brands", - "fieldtype": "Table", - "label": "Browse By Brands", - "options": "Website Slideshow Item" - }, { "fieldname": "new_arrivals", "fieldtype": "Table", "label": "New Arrivals", "options": "Recommended Variant" }, - { - "fieldname": "section_break_xmqq", - "fieldtype": "Section Break", - "label": "Banner Section 1" - }, - { - "fieldname": "banner_1", - "fieldtype": "Attach Image", - "label": "Banner 1" - }, - { - "fieldname": "banner_2", - "fieldtype": "Attach Image", - "label": "Banner 2" - }, { "fieldname": "section_break_dyhp", "fieldtype": "Section Break" @@ -151,45 +102,10 @@ "label": "Best Picks", "options": "Recommended Variant" }, - { - "fieldname": "section_break_dehn", - "fieldtype": "Section Break", - "label": "Banner Section 2" - }, - { - "fieldname": "banner_3", - "fieldtype": "Attach Image", - "label": "Banner 3" - }, - { - "fieldname": "section_break_jqas", - "fieldtype": "Section Break", - "label": "Banner Section 3" - }, - { - "fieldname": "banner_4", - "fieldtype": "Attach Image", - "label": "Banner 4" - }, - { - "fieldname": "column_break_nbcp", - "fieldtype": "Column Break" - }, { "fieldname": "section_break_tgjx", "fieldtype": "Section Break", - "label": "Banner Section 4" - }, - { - "fieldname": "banner_5", - "fieldtype": "Attach Image", - "label": "Banner 5" - }, - { - "fieldname": "other_products", - "fieldtype": "Table", - "label": "Other Products", - "options": "Recommended Variant" + "label": "Banner Section" }, { "fieldname": "column_break_hjwh", @@ -227,53 +143,10 @@ "label": "GIF URL 4", "options": "URL" }, - { - "fieldname": "column_break_gzsu", - "fieldtype": "Column Break" - }, - { - "default": "/", - "fieldname": "banner_url_1", - "fieldtype": "Data", - "label": "Banner URL 1", - "options": "URL" - }, - { - "default": "/", - "fieldname": "banner_url_2", - "fieldtype": "Data", - "label": "Banner URL 2", - "options": "URL" - }, - { - "fieldname": "column_break_nzln", - "fieldtype": "Column Break" - }, - { - "default": "/", - "fieldname": "banner_url_3", - "fieldtype": "Data", - "label": "Banner URL 3", - "options": "URL" - }, - { - "default": "/", - "fieldname": "banner_url_4", - "fieldtype": "Data", - "label": "Banner URL 4", - "options": "URL" - }, { "fieldname": "column_break_mscz", "fieldtype": "Column Break" }, - { - "default": "/", - "fieldname": "banner_url_5", - "fieldtype": "Data", - "label": "Banner URL 5", - "options": "URL" - }, { "fieldname": "hero_banner_ar", "fieldtype": "Table", @@ -281,6 +154,7 @@ "options": "Landing Page Hero Banner" }, { + "description": "Banner images", "fieldname": "banner_section", "fieldtype": "Section Break", "label": "Hero Banner" @@ -334,12 +208,7 @@ "options": "URL" }, { - "fieldname": "browse_by_brands_ar", - "fieldtype": "Table", - "label": "Browse By Brands AR", - "options": "Website Slideshow Item" - }, - { + "description": "Brands or Categories AR", "fieldname": "gifs_ar_section", "fieldtype": "Section Break", "label": "GIFs AR" @@ -357,90 +226,36 @@ "fieldtype": "Column Break" }, { - "fieldname": "banner_ar_section", - "fieldtype": "Section Break", - "label": "Banner Section 1 AR" - }, - { - "fieldname": "banner_1_ar", - "fieldtype": "Attach Image", - "label": "Banner 1 AR" - }, - { - "default": "/", - "fieldname": "banner_url_1_ar", - "fieldtype": "Data", - "label": "Banner URL 1 AR", - "options": "URL" - }, - { - "fieldname": "column_break_sbpz", - "fieldtype": "Column Break" - }, - { - "fieldname": "banner_2_ar", - "fieldtype": "Attach Image", - "label": "Banner 2 AR" - }, - { - "default": "/", - "fieldname": "banner_url_2_ar", - "fieldtype": "Data", - "label": "Banner URL 2 AR", - "options": "URL" - }, - { - "fieldname": "banner_section_2_ar_section", + "fieldname": "banner_section_4_ar_section", "fieldtype": "Section Break", - "label": "Banner Section 2 AR" - }, - { - "fieldname": "banner_3_ar", - "fieldtype": "Attach Image", - "label": "Banner 3 AR" + "label": "Banner Section AR" }, { - "fieldname": "column_break_pwrs", + "fieldname": "column_break_irta", "fieldtype": "Column Break" }, { - "default": "/", - "fieldname": "banner_url_3_ar", - "fieldtype": "Data", - "label": "Banner URL 3 AR", - "options": "URL" - }, - { - "fieldname": "banner_4_ar", + "fieldname": "banner_1", "fieldtype": "Attach Image", - "label": "Banner 4 AR" + "label": "Banner 1" }, { "default": "/", - "fieldname": "banner_url_4_ar", + "fieldname": "banner_url_1", "fieldtype": "Data", - "label": "Banner URL 4 AR", + "label": "Banner URL 1", "options": "URL" }, { - "fieldname": "banner_section_4_ar_section", - "fieldtype": "Section Break", - "label": "Banner Section 4 AR" - }, - { - "fieldname": "banner_5_ar", + "fieldname": "banner_1_ar", "fieldtype": "Attach Image", - "label": "Banner 5 AR" - }, - { - "fieldname": "column_break_irta", - "fieldtype": "Column Break" + "label": "Banner 1 AR" }, { "default": "/", - "fieldname": "banner_url_5_ar", + "fieldname": "banner_url_1_ar", "fieldtype": "Data", - "label": "Banner URL 5 AR", + "label": "Banner URL 1 AR", "options": "URL" } ], @@ -448,7 +263,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2025-06-26 07:45:42.064761", + "modified": "2025-10-08 21:42:00.724782", "modified_by": "Administrator", "module": "Lifestyle Shop Ecommerce", "name": "Landing Page Settings", diff --git a/ls_shop/lifestyle_shop_ecommerce/doctype/landing_page_settings/landing_page_settings.py b/ls_shop/lifestyle_shop_ecommerce/doctype/landing_page_settings/landing_page_settings.py index b1decdf..916d0e6 100644 --- a/ls_shop/lifestyle_shop_ecommerce/doctype/landing_page_settings/landing_page_settings.py +++ b/ls_shop/lifestyle_shop_ecommerce/doctype/landing_page_settings/landing_page_settings.py @@ -13,9 +13,6 @@ class LandingPageSettings(Document): if TYPE_CHECKING: from frappe.types import DF - from frappe.website.doctype.website_slideshow_item.website_slideshow_item import ( - WebsiteSlideshowItem, - ) from ls_shop.lifestyle_shop_ecommerce.doctype.landing_page_hero_banner.landing_page_hero_banner import ( LandingPageHeroBanner, @@ -26,27 +23,9 @@ class LandingPageSettings(Document): banner_1: DF.AttachImage | None banner_1_ar: DF.AttachImage | None - banner_2: DF.AttachImage | None - banner_2_ar: DF.AttachImage | None - banner_3: DF.AttachImage | None - banner_3_ar: DF.AttachImage | None - banner_4: DF.AttachImage | None - banner_4_ar: DF.AttachImage | None - banner_5: DF.AttachImage | None - banner_5_ar: DF.AttachImage | None banner_url_1: DF.Data | None banner_url_1_ar: DF.Data | None - banner_url_2: DF.Data | None - banner_url_2_ar: DF.Data | None - banner_url_3: DF.Data | None - banner_url_3_ar: DF.Data | None - banner_url_4: DF.Data | None - banner_url_4_ar: DF.Data | None - banner_url_5: DF.Data | None - banner_url_5_ar: DF.Data | None best_picks: DF.Table[RecommendedVariant] - browse_by_brands: DF.Table[WebsiteSlideshowItem] - browse_by_brands_ar: DF.Table[WebsiteSlideshowItem] gif_1: DF.Attach | None gif_1_ar: DF.Attach | None gif_2: DF.Attach | None @@ -66,6 +45,5 @@ class LandingPageSettings(Document): hero_banner: DF.Table[LandingPageHeroBanner] hero_banner_ar: DF.Table[LandingPageHeroBanner] new_arrivals: DF.Table[RecommendedVariant] - other_products: DF.Table[RecommendedVariant] # end: auto-generated types pass diff --git a/ls_shop/lifestyle_shop_ecommerce/doctype/lifestyle_settings/lifestyle_settings.py b/ls_shop/lifestyle_shop_ecommerce/doctype/lifestyle_settings/lifestyle_settings.py index 952dd9c..8437ce0 100644 --- a/ls_shop/lifestyle_shop_ecommerce/doctype/lifestyle_settings/lifestyle_settings.py +++ b/ls_shop/lifestyle_shop_ecommerce/doctype/lifestyle_settings/lifestyle_settings.py @@ -49,28 +49,20 @@ class LifestyleSettings(Document): def validate(self): if not self.telr_enabled and not self.tabby_enabled and not self.cod_enabled: - frappe.throw( - frappe._( - "At least one payment method (Telr, Tabby, or COD) must be enabled." - ) - ) + frappe.throw(frappe._("At least one payment method (Telr, Tabby, or COD) must be enabled.")) def get_default_price_list(self): return ( self.default_price_list if self.default_price_list - else frappe.get_cached_value( - "Webshop Settings", "Webshop Settings", "price_list" - ) + else frappe.get_cached_value("Webshop Settings", "Webshop Settings", "price_list") ) def get_sale_price_list(self): return ( self.sale_price_list if self.sale_price_list - else frappe.get_cached_value( - "Webshop Settings", "Webshop Settings", "price_list" - ) + else frappe.get_cached_value("Webshop Settings", "Webshop Settings", "price_list") ) @frappe.whitelist() @@ -82,9 +74,7 @@ def enqueue_publish_all_variants(self, attribute: str): attribute=attribute, log_name=log.name, ) - link = get_url_to_form( - "Bulk Style Attribute Configurator Creation Log", log.name - ) + link = get_url_to_form("Bulk Style Attribute Configurator Creation Log", log.name) return frappe._(f"Creating configurators. View Log") @@ -111,9 +101,7 @@ def generate_configurators_for_all_templates(attribute: str, log_name: str): .where(configurator.item_template.isnull() & item.has_variants) ) results = query.run(as_dict=True) - configurator_log = frappe.get_doc( - "Bulk Style Attribute Configurator Creation Log", log_name - ) + configurator_log = frappe.get_doc("Bulk Style Attribute Configurator Creation Log", log_name) configurator_log.configurators = [] for row in results: diff --git a/ls_shop/lifestyle_shop_ecommerce/doctype/style_attribute_configurator/style_attribute_configurator.py b/ls_shop/lifestyle_shop_ecommerce/doctype/style_attribute_configurator/style_attribute_configurator.py index c1c81a5..4541d99 100644 --- a/ls_shop/lifestyle_shop_ecommerce/doctype/style_attribute_configurator/style_attribute_configurator.py +++ b/ls_shop/lifestyle_shop_ecommerce/doctype/style_attribute_configurator/style_attribute_configurator.py @@ -82,9 +82,7 @@ def generate_variants(self): } result[color_value]["items"].append(item_info) for color, variants in result.items(): - item_template_name = frappe.get_value( - "Item", self.item_template, "item_name" - ) + item_template_name = frappe.get_value("Item", self.item_template, "item_name") if not item_template_name: item_template_name = self.item_template try: diff --git a/ls_shop/lifestyle_shop_ecommerce/doctype/style_attribute_variant/patches/unpublish_style_attribute_variant.py b/ls_shop/lifestyle_shop_ecommerce/doctype/style_attribute_variant/patches/unpublish_style_attribute_variant.py index 2c7d256..db54952 100644 --- a/ls_shop/lifestyle_shop_ecommerce/doctype/style_attribute_variant/patches/unpublish_style_attribute_variant.py +++ b/ls_shop/lifestyle_shop_ecommerce/doctype/style_attribute_variant/patches/unpublish_style_attribute_variant.py @@ -3,9 +3,7 @@ def execute(): try: - attribute_variants = frappe.get_all( - "Style Attribute Variant", {"is_published": True}, pluck="name" - ) + attribute_variants = frappe.get_all("Style Attribute Variant", {"is_published": True}, pluck="name") for variant_name in attribute_variants: item = frappe.get_doc("Style Attribute Variant", variant_name) item.unpublish_if_incomplete_data() diff --git a/ls_shop/lifestyle_shop_ecommerce/doctype/style_attribute_variant/style_attribute_variant.py b/ls_shop/lifestyle_shop_ecommerce/doctype/style_attribute_variant/style_attribute_variant.py index c71624b..99cef17 100644 --- a/ls_shop/lifestyle_shop_ecommerce/doctype/style_attribute_variant/style_attribute_variant.py +++ b/ls_shop/lifestyle_shop_ecommerce/doctype/style_attribute_variant/style_attribute_variant.py @@ -47,12 +47,8 @@ def scrub(self, text): def update_item_group(self): if not self.item_group: - self.item_group = frappe.db.get_value( - "Item", self.item_style, "item_group", cache=True - ) - item_group_mapping = frappe.get_cached_doc( - "Lifestyle Settings" - ).ecommerce_item_group_mapping + self.item_group = frappe.db.get_value("Item", self.item_style, "item_group", cache=True) + item_group_mapping = frappe.get_cached_doc("Lifestyle Settings").ecommerce_item_group_mapping for mapping in item_group_mapping: if mapping.original_item_group == self.item_group: self.item_group = mapping.ecommerce_item_group diff --git a/ls_shop/lifestyle_shop_ecommerce/doctype/telr_payment_request/telr_payment_request.py b/ls_shop/lifestyle_shop_ecommerce/doctype/telr_payment_request/telr_payment_request.py index 96784c1..786cb1a 100644 --- a/ls_shop/lifestyle_shop_ecommerce/doctype/telr_payment_request/telr_payment_request.py +++ b/ls_shop/lifestyle_shop_ecommerce/doctype/telr_payment_request/telr_payment_request.py @@ -127,6 +127,4 @@ def refund_payment_for_payment_entry(doc, event=None): return payment_amount = doc.paid_amount - frappe.get_doc("Telr Payment Request", {"telr_order_ref": doc.reference_no}).refund( - payment_amount - ) + frappe.get_doc("Telr Payment Request", {"telr_order_ref": doc.reference_no}).refund(payment_amount) diff --git a/ls_shop/lifestyle_shop_ecommerce/doctype/telr_settings/telr_settings.py b/ls_shop/lifestyle_shop_ecommerce/doctype/telr_settings/telr_settings.py index e782dfc..d32af2d 100644 --- a/ls_shop/lifestyle_shop_ecommerce/doctype/telr_settings/telr_settings.py +++ b/ls_shop/lifestyle_shop_ecommerce/doctype/telr_settings/telr_settings.py @@ -113,13 +113,9 @@ def create_session( if error := output.get("error"): frappe.errprint("Telr Response:") frappe.errprint(output) - raise Exception( - f'{error.get("message")}: {frappe.bold(error.get("note"))}' - ) + raise Exception(f'{error.get("message")}: {frappe.bold(error.get("note"))}') - create_telr_request_log( - endpoint, data=payload, headers=headers, output=output - ) + create_telr_request_log(endpoint, data=payload, headers=headers, output=output) except Exception as e: create_telr_request_log(endpoint, error=e, headers=headers) raise @@ -138,9 +134,7 @@ def get_order_for_check(self, order_ref: str): headers = {"accept": "application/json", "Content-Type": "application/json"} try: response = make_post_request(endpoint, json=payload, headers=headers) - create_telr_request_log( - endpoint, data=payload, headers=headers, output=response - ) + create_telr_request_log(endpoint, data=payload, headers=headers, output=response) except Exception as e: create_telr_request_log(endpoint, data=payload, headers=headers, error=e) raise @@ -177,13 +171,9 @@ def refund_order(self, transaction_ref: str, amount: float): message = root.find("auth/message").text if status == "A": - create_telr_request_log( - url, data=xml_data, headers=headers, output=response.text - ) + create_telr_request_log(url, data=xml_data, headers=headers, output=response.text) else: - create_telr_request_log( - url, data=xml_data, headers=headers, output=response.text, error=message - ) + create_telr_request_log(url, data=xml_data, headers=headers, output=response.text, error=message) frappe.throw(frappe._(message)) return response diff --git a/ls_shop/lifestyle_shop_ecommerce/report/items_without_images_and_unpublished/items_without_images_and_unpublished.py b/ls_shop/lifestyle_shop_ecommerce/report/items_without_images_and_unpublished/items_without_images_and_unpublished.py index fe41f31..cc7abf9 100644 --- a/ls_shop/lifestyle_shop_ecommerce/report/items_without_images_and_unpublished/items_without_images_and_unpublished.py +++ b/ls_shop/lifestyle_shop_ecommerce/report/items_without_images_and_unpublished/items_without_images_and_unpublished.py @@ -65,10 +65,7 @@ def get_data(): .left_join(website_slideshow_item) .on(website_slideshow_item.parent == style_attribute_variant.name) .groupby(style_attribute_variant.name) - .having( - (style_attribute_variant.is_published == 0) - | (Count(website_slideshow_item.image) == 0) - ) + .having((style_attribute_variant.is_published == 0) | (Count(website_slideshow_item.image) == 0)) .select( style_attribute_variant.name, style_attribute_variant.display_name, diff --git a/ls_shop/lifestyle_shop_ecommerce/report/items_without_stock/items_without_stock.py b/ls_shop/lifestyle_shop_ecommerce/report/items_without_stock/items_without_stock.py index 80c8b3d..e87a14f 100644 --- a/ls_shop/lifestyle_shop_ecommerce/report/items_without_stock/items_without_stock.py +++ b/ls_shop/lifestyle_shop_ecommerce/report/items_without_stock/items_without_stock.py @@ -114,10 +114,7 @@ def get_data(filters=None): .left_join(website_slideshow_item) .on(website_slideshow_item.parent == style_attribute_variant.name) .left_join(bin) - .on( - (color_size_item.item_code == bin.item_code) - & (bin.warehouse == ecommerce_warehouse) - ) + .on((color_size_item.item_code == bin.item_code) & (bin.warehouse == ecommerce_warehouse)) .select( style_attribute_variant.name, style_attribute_variant.display_name, @@ -137,9 +134,7 @@ def get_data(filters=None): elif filters.unpublished_but_has_stock and not filters.published_but_no_stock: query = query.having(unpublished_has_stock == 1) else: - query = query.having( - (published_no_stock == 1) | (unpublished_has_stock == 1) - ) + query = query.having((published_no_stock == 1) | (unpublished_has_stock == 1)) else: query = query.having((published_no_stock == 1) | (unpublished_has_stock == 1)) @@ -168,6 +163,4 @@ def bulk_publish(filters): style_attribute_variants = [d.name for d in data] doc = frappe.get_single("Bulk Publish Variants") - return doc.bulk_toggle_publish( - publish=True, style_attribute_variant_list=style_attribute_variants - ) + return doc.bulk_toggle_publish(publish=True, style_attribute_variant_list=style_attribute_variants) diff --git a/ls_shop/lifestyle_shop_ecommerce/report/items_without_style_attribute_variant/items_without_style_attribute_variant.py b/ls_shop/lifestyle_shop_ecommerce/report/items_without_style_attribute_variant/items_without_style_attribute_variant.py index 5cea95b..5b188d9 100644 --- a/ls_shop/lifestyle_shop_ecommerce/report/items_without_style_attribute_variant/items_without_style_attribute_variant.py +++ b/ls_shop/lifestyle_shop_ecommerce/report/items_without_style_attribute_variant/items_without_style_attribute_variant.py @@ -25,8 +25,6 @@ def get_columns(): def get_data(): items = set(frappe.get_all("Item", {"has_variants": True}, pluck="name")) - style_attribute_variants = set( - frappe.get_all("Style Attribute Variant", pluck="item_style") - ) + style_attribute_variants = set(frappe.get_all("Style Attribute Variant", pluck="item_style")) unmatched_items = items - style_attribute_variants return [{"item_code": item} for item in unmatched_items] diff --git a/ls_shop/lifestyle_shop_ecommerce/report/orphaned_payments/orphaned_payments.py b/ls_shop/lifestyle_shop_ecommerce/report/orphaned_payments/orphaned_payments.py index b050466..08c2a72 100644 --- a/ls_shop/lifestyle_shop_ecommerce/report/orphaned_payments/orphaned_payments.py +++ b/ls_shop/lifestyle_shop_ecommerce/report/orphaned_payments/orphaned_payments.py @@ -80,14 +80,8 @@ def get_data(): ) .where( ((PaymentEntry.docstatus == 1) & (PaymentEntryReference.name.isnull())) - | ( - (PaymentEntry.docstatus == 2) - & (Telr_payment_request.status != "Refunded") - ) - | ( - (PaymentEntry.docstatus == 2) - & (tabby_payment_request.status != "REFUND") - ) + | ((PaymentEntry.docstatus == 2) & (Telr_payment_request.status != "Refunded")) + | ((PaymentEntry.docstatus == 2) & (tabby_payment_request.status != "REFUND")) ) ).run(as_dict=True) for payment in orphaned_payments: @@ -98,10 +92,7 @@ def get_data(): "payment_mode": payment.mode_of_payment, "posting_date": payment.posting_date, "cancelled": payment.docstatus == 2, - "refunded": ( - payment.telr_status == "Refunded" - or payment.tabby_status == "REFUND" - ), + "refunded": (payment.telr_status == "Refunded" or payment.tabby_status == "REFUND"), } ) return data diff --git a/ls_shop/public/css/tailwind.css b/ls_shop/public/css/tailwind.css index 528dd4a..7859bba 100644 --- a/ls_shop/public/css/tailwind.css +++ b/ls_shop/public/css/tailwind.css @@ -1,45 +1,46 @@ -/*! tailwindcss v4.0.14 | MIT License | https://tailwindcss.com */ +/*! tailwindcss v4.1.14 | MIT License | https://tailwindcss.com */ +@layer properties; @layer theme, base, components, utilities; @layer theme { :root, :host { --font-mono: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; --color-red-50: #f9d7da; - --color-red-100: oklch(0.936 0.032 17.717); - --color-red-400: oklch(0.704 0.191 22.216); - --color-red-500: oklch(0.637 0.237 25.331); - --color-red-600: oklch(0.577 0.245 27.325); - --color-red-700: oklch(0.505 0.213 27.518); + --color-red-100: oklch(93.6% 0.032 17.717); + --color-red-400: oklch(70.4% 0.191 22.216); + --color-red-500: oklch(63.7% 0.237 25.331); + --color-red-600: oklch(57.7% 0.245 27.325); + --color-red-700: oklch(50.5% 0.213 27.518); --color-red-800: #ac0005; --color-red-900: #991b1f; - --color-orange-400: oklch(0.75 0.183 55.934); - --color-yellow-50: oklch(0.987 0.026 102.212); - --color-yellow-600: oklch(0.681 0.162 75.834); - --color-green-50: oklch(0.982 0.018 155.826); - --color-green-500: oklch(0.723 0.219 149.579); - --color-green-600: oklch(0.627 0.194 149.214); - --color-green-700: oklch(0.527 0.154 150.069); - --color-blue-500: oklch(0.623 0.214 259.815); - --color-blue-600: oklch(0.546 0.245 262.881); - --color-indigo-500: oklch(0.585 0.233 277.117); - --color-indigo-600: oklch(0.511 0.262 276.966); - --color-slate-800: oklch(0.279 0.041 260.031); - --color-slate-900: oklch(0.208 0.042 265.755); - --color-gray-50: oklch(0.985 0.002 247.839); - --color-gray-100: oklch(0.967 0.003 264.542); - --color-gray-200: oklch(0.928 0.006 264.531); - --color-gray-300: oklch(0.872 0.01 258.338); - --color-gray-400: oklch(0.707 0.022 261.325); - --color-gray-500: oklch(0.551 0.027 264.364); - --color-gray-600: oklch(0.446 0.03 256.802); - --color-gray-700: oklch(0.373 0.034 259.733); - --color-gray-800: oklch(0.278 0.033 256.848); - --color-gray-900: oklch(0.21 0.034 264.665); - --color-gray-950: oklch(0.13 0.028 261.692); - --color-neutral-50: oklch(0.985 0 0); - --color-neutral-100: oklch(0.97 0 0); - --color-neutral-200: oklch(0.922 0 0); - --color-neutral-600: oklch(0.439 0 0); - --color-neutral-900: oklch(0.205 0 0); + --color-orange-400: oklch(75% 0.183 55.934); + --color-yellow-50: oklch(98.7% 0.026 102.212); + --color-yellow-600: oklch(68.1% 0.162 75.834); + --color-green-50: oklch(98.2% 0.018 155.826); + --color-green-500: oklch(72.3% 0.219 149.579); + --color-green-600: oklch(62.7% 0.194 149.214); + --color-green-700: oklch(52.7% 0.154 150.069); + --color-blue-500: oklch(62.3% 0.214 259.815); + --color-blue-600: oklch(54.6% 0.245 262.881); + --color-indigo-500: oklch(58.5% 0.233 277.117); + --color-indigo-600: oklch(51.1% 0.262 276.966); + --color-slate-800: oklch(27.9% 0.041 260.031); + --color-slate-900: oklch(20.8% 0.042 265.755); + --color-gray-50: oklch(98.5% 0.002 247.839); + --color-gray-100: oklch(96.7% 0.003 264.542); + --color-gray-200: oklch(92.8% 0.006 264.531); + --color-gray-300: oklch(87.2% 0.01 258.338); + --color-gray-400: oklch(70.7% 0.022 261.325); + --color-gray-500: oklch(55.1% 0.027 264.364); + --color-gray-600: oklch(44.6% 0.03 256.802); + --color-gray-700: oklch(37.3% 0.034 259.733); + --color-gray-800: oklch(27.8% 0.033 256.848); + --color-gray-900: oklch(21% 0.034 264.665); + --color-gray-950: oklch(13% 0.028 261.692); + --color-neutral-50: oklch(98.5% 0 0); + --color-neutral-100: oklch(97% 0 0); + --color-neutral-200: oklch(92.2% 0 0); + --color-neutral-600: oklch(43.9% 0 0); + --color-neutral-900: oklch(20.5% 0 0); --color-black: #000; --color-white: #fff; --spacing: 0.25rem; @@ -85,17 +86,7 @@ --default-transition-duration: 150ms; --default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); --default-font-family: "OpenSans"; - --default-font-feature-settings: var(--font-sans--font-feature-settings); - --default-font-variation-settings: var( - --font-sans--font-variation-settings - ); --default-mono-font-family: var(--font-mono); - --default-mono-font-feature-settings: var( - --font-mono--font-feature-settings - ); - --default-mono-font-variation-settings: var( - --font-mono--font-variation-settings - ); --text-xxs: 10px; --text-3xs: 8px; } @@ -111,14 +102,11 @@ line-height: 1.5; -webkit-text-size-adjust: 100%; tab-size: 4; - font-family: var( --default-font-family, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji" ); + font-family: var(--default-font-family, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"); font-feature-settings: var(--default-font-feature-settings, normal); - font-variation-settings: var( --default-font-variation-settings, normal ); + font-variation-settings: var(--default-font-variation-settings, normal); -webkit-tap-highlight-color: transparent; } - body { - line-height: inherit; - } hr { height: 0; color: inherit; @@ -141,9 +129,9 @@ font-weight: bolder; } code, kbd, samp, pre { - font-family: var( --default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace ); - font-feature-settings: var( --default-mono-font-feature-settings, normal ); - font-variation-settings: var( --default-mono-font-variation-settings, normal ); + font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace); + font-feature-settings: var(--default-mono-font-feature-settings, normal); + font-variation-settings: var(--default-mono-font-variation-settings, normal); font-size: 1em; } small { @@ -207,7 +195,14 @@ } ::placeholder { opacity: 1; - color: color-mix(in oklab, currentColor 50%, transparent); + } + @supports (not (-webkit-appearance: -apple-pay-button)) or (contain-intrinsic-size: 1px) { + ::placeholder { + color: currentcolor; + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklab, currentcolor 50%, transparent); + } + } } textarea { resize: vertical; @@ -228,6 +223,9 @@ ::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field { padding-block: 0; } + ::-webkit-calendar-picker-indicator { + line-height: 1; + } :-moz-ui-invalid { box-shadow: none; } @@ -258,7 +256,7 @@ padding: 0; margin: -1px; overflow: hidden; - clip: rect(0, 0, 0, 0); + clip-path: inset(50%); white-space: nowrap; border-width: 0; } @@ -640,9 +638,6 @@ .table { display: table; } - .aspect-square { - aspect-ratio: 1 / 1; - } .size-3 { width: calc(var(--spacing) * 3); height: calc(var(--spacing) * 3); @@ -692,9 +687,6 @@ .h-12 { height: calc(var(--spacing) * 12); } - .h-18 { - height: calc(var(--spacing) * 18); - } .h-20 { height: calc(var(--spacing) * 20); } @@ -761,9 +753,6 @@ .w-1\/2 { width: calc(1/2 * 100%); } - .w-1\/3 { - width: calc(1/3 * 100%); - } .w-1\/4 { width: calc(1/4 * 100%); } @@ -827,9 +816,6 @@ .w-\[18px\] { width: 18px; } - .w-\[175px\] { - width: 175px; - } .w-\[340px\] { width: 340px; } @@ -980,7 +966,7 @@ rotate: 180deg; } .transform { - transform: var(--tw-rotate-x) var(--tw-rotate-y) var(--tw-rotate-z) var(--tw-skew-x) var(--tw-skew-y); + transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,); } .animate-spin { animation: var(--animate-spin); @@ -1247,7 +1233,7 @@ border-style: dashed; } .border-current { - border-color: currentColor; + border-color: currentcolor; } .border-gray-100 { border-color: var(--color-gray-100); @@ -1256,7 +1242,10 @@ border-color: var(--color-gray-200); } .border-gray-200\/70 { - border-color: color-mix(in oklab, var(--color-gray-200) 70%, transparent); + border-color: color-mix(in srgb, oklch(92.8% 0.006 264.531) 70%, transparent); + @supports (color: color-mix(in lab, red, red)) { + border-color: color-mix(in oklab, var(--color-gray-200) 70%, transparent); + } } .border-gray-300 { border-color: var(--color-gray-300); @@ -1280,7 +1269,10 @@ border-color: var(--color-green-600); } .border-neutral-200\/70 { - border-color: color-mix(in oklab, var(--color-neutral-200) 70%, transparent); + border-color: color-mix(in srgb, oklch(92.2% 0 0) 70%, transparent); + @supports (color: color-mix(in lab, red, red)) { + border-color: color-mix(in oklab, var(--color-neutral-200) 70%, transparent); + } } .border-red-400 { border-color: var(--color-red-400); @@ -1304,13 +1296,19 @@ border-top-color: transparent; } .bg-black\/20 { - background-color: color-mix(in oklab, var(--color-black) 20%, transparent); + background-color: color-mix(in srgb, #000 20%, transparent); + @supports (color: color-mix(in lab, red, red)) { + background-color: color-mix(in oklab, var(--color-black) 20%, transparent); + } } .bg-gray-50 { background-color: var(--color-gray-50); } .bg-gray-50\/60 { - background-color: color-mix(in oklab, var(--color-gray-50) 60%, transparent); + background-color: color-mix(in srgb, oklch(98.5% 0.002 247.839) 60%, transparent); + @supports (color: color-mix(in lab, red, red)) { + background-color: color-mix(in oklab, var(--color-gray-50) 60%, transparent); + } } .bg-gray-100 { background-color: var(--color-gray-100); @@ -1325,7 +1323,10 @@ background-color: var(--color-gray-400); } .bg-gray-400\/10 { - background-color: color-mix(in oklab, var(--color-gray-400) 10%, transparent); + background-color: color-mix(in srgb, oklch(70.7% 0.022 261.325) 10%, transparent); + @supports (color: color-mix(in lab, red, red)) { + background-color: color-mix(in oklab, var(--color-gray-400) 10%, transparent); + } } .bg-gray-900 { background-color: var(--color-gray-900); @@ -1337,10 +1338,16 @@ background-color: var(--color-green-50); } .bg-green-500\/10 { - background-color: color-mix(in oklab, var(--color-green-500) 10%, transparent); + background-color: color-mix(in srgb, oklch(72.3% 0.219 149.579) 10%, transparent); + @supports (color: color-mix(in lab, red, red)) { + background-color: color-mix(in oklab, var(--color-green-500) 10%, transparent); + } } .bg-green-500\/15 { - background-color: color-mix(in oklab, var(--color-green-500) 15%, transparent); + background-color: color-mix(in srgb, oklch(72.3% 0.219 149.579) 15%, transparent); + @supports (color: color-mix(in lab, red, red)) { + background-color: color-mix(in oklab, var(--color-green-500) 15%, transparent); + } } .bg-indigo-600 { background-color: var(--color-indigo-600); @@ -1363,9 +1370,6 @@ .bg-slate-800 { background-color: var(--color-slate-800); } - .bg-slate-800\/98 { - background-color: color-mix(in oklab, var(--color-slate-800) 98%, transparent); - } .bg-transparent { background-color: transparent; } @@ -1373,7 +1377,10 @@ background-color: var(--color-white); } .bg-white\/60 { - background-color: color-mix(in oklab, var(--color-white) 60%, transparent); + background-color: color-mix(in srgb, #fff 60%, transparent); + @supports (color: color-mix(in lab, red, red)) { + background-color: color-mix(in oklab, var(--color-white) 60%, transparent); + } } .bg-yellow-50 { background-color: var(--color-yellow-50); @@ -1444,9 +1451,6 @@ .px-6 { padding-inline: calc(var(--spacing) * 6); } - .px-7 { - padding-inline: calc(var(--spacing) * 7); - } .px-8 { padding-inline: calc(var(--spacing) * 8); } @@ -1681,7 +1685,10 @@ color: var(--color-gray-600); } .text-gray-600\/50 { - color: color-mix(in oklab, var(--color-gray-600) 50%, transparent); + color: color-mix(in srgb, oklch(44.6% 0.03 256.802) 50%, transparent); + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklab, var(--color-gray-600) 50%, transparent); + } } .text-gray-700 { color: var(--color-gray-700); @@ -1705,7 +1712,10 @@ color: var(--color-neutral-600); } .text-neutral-600\/50 { - color: color-mix(in oklab, var(--color-neutral-600) 50%, transparent); + color: color-mix(in srgb, oklch(43.9% 0 0) 50%, transparent); + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklab, var(--color-neutral-600) 50%, transparent); + } } .text-orange-400 { color: var(--color-orange-400); @@ -1792,9 +1802,6 @@ .opacity-75 { opacity: 75%; } - .opacity-87 { - opacity: 87%; - } .opacity-90 { opacity: 90%; } @@ -1822,7 +1829,7 @@ box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); } .ring-1 { - --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentColor); + --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor); box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); } .ring-gray-300 { @@ -1853,7 +1860,7 @@ backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,); } .transition { - transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter; + transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, content-visibility, overlay, pointer-events; transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); transition-duration: var(--tw-duration, var(--default-transition-duration)); } @@ -1966,7 +1973,10 @@ .hover\:bg-gray-900\/5 { &:hover { @media (hover: hover) { - background-color: color-mix(in oklab, var(--color-gray-900) 5%, transparent); + background-color: color-mix(in srgb, oklch(21% 0.034 264.665) 5%, transparent); + @supports (color: color-mix(in lab, red, red)) { + background-color: color-mix(in oklab, var(--color-gray-900) 5%, transparent); + } } } } @@ -1987,7 +1997,10 @@ .hover\:bg-neutral-900\/5 { &:hover { @media (hover: hover) { - background-color: color-mix(in oklab, var(--color-neutral-900) 5%, transparent); + background-color: color-mix(in srgb, oklch(20.5% 0 0) 5%, transparent); + @supports (color: color-mix(in lab, red, red)) { + background-color: color-mix(in oklab, var(--color-neutral-900) 5%, transparent); + } } } } @@ -2096,7 +2109,7 @@ } .focus\:ring-2 { &:focus { - --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentColor); + --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor); box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); } } @@ -2158,12 +2171,18 @@ } .focus-visible\:bg-gray-900\/5 { &:focus-visible { - background-color: color-mix(in oklab, var(--color-gray-900) 5%, transparent); + background-color: color-mix(in srgb, oklch(21% 0.034 264.665) 5%, transparent); + @supports (color: color-mix(in lab, red, red)) { + background-color: color-mix(in oklab, var(--color-gray-900) 5%, transparent); + } } } .focus-visible\:bg-neutral-900\/5 { &:focus-visible { - background-color: color-mix(in oklab, var(--color-neutral-900) 5%, transparent); + background-color: color-mix(in srgb, oklch(20.5% 0 0) 5%, transparent); + @supports (color: color-mix(in lab, red, red)) { + background-color: color-mix(in oklab, var(--color-neutral-900) 5%, transparent); + } } } .focus-visible\:text-gray-600 { @@ -2446,11 +2465,6 @@ grid-column-start: 2; } } - .md\:mx-auto { - @media (width >= 48rem) { - margin-inline: auto; - } - } .md\:my-22 { @media (width >= 48rem) { margin-block: calc(var(--spacing) * 22); @@ -2516,11 +2530,6 @@ margin-top: calc(var(--spacing) * 11); } } - .md\:mt-18 { - @media (width >= 48rem) { - margin-top: calc(var(--spacing) * 18); - } - } .md\:mt-21 { @media (width >= 48rem) { margin-top: calc(var(--spacing) * 21); @@ -2581,11 +2590,6 @@ display: none; } } - .md\:h-28 { - @media (width >= 48rem) { - height: calc(var(--spacing) * 28); - } - } .md\:h-60 { @media (width >= 48rem) { height: calc(var(--spacing) * 60); @@ -2681,16 +2685,6 @@ max-width: var(--container-2xs); } } - .md\:max-w-3xl { - @media (width >= 48rem) { - max-width: var(--container-3xl); - } - } - .md\:max-w-7xl { - @media (width >= 48rem) { - max-width: var(--container-7xl); - } - } .md\:max-w-65 { @media (width >= 48rem) { max-width: calc(var(--spacing) * 65); @@ -2776,11 +2770,6 @@ gap: calc(var(--spacing) * 25); } } - .md\:gap-28 { - @media (width >= 48rem) { - gap: calc(var(--spacing) * 28); - } - } .md\:overflow-y-auto { @media (width >= 48rem) { overflow-y: auto; @@ -2827,21 +2816,11 @@ padding-inline: calc(var(--spacing) * 3); } } - .md\:px-6 { - @media (width >= 48rem) { - padding-inline: calc(var(--spacing) * 6); - } - } .md\:px-10 { @media (width >= 48rem) { padding-inline: calc(var(--spacing) * 10); } } - .md\:px-11 { - @media (width >= 48rem) { - padding-inline: calc(var(--spacing) * 11); - } - } .md\:py-4 { @media (width >= 48rem) { padding-block: calc(var(--spacing) * 4); @@ -3209,27 +3188,22 @@ @property --tw-rotate-x { syntax: "*"; inherits: false; - initial-value: rotateX(0); } @property --tw-rotate-y { syntax: "*"; inherits: false; - initial-value: rotateY(0); } @property --tw-rotate-z { syntax: "*"; inherits: false; - initial-value: rotateZ(0); } @property --tw-skew-x { syntax: "*"; inherits: false; - initial-value: skewX(0); } @property --tw-skew-y { syntax: "*"; inherits: false; - initial-value: skewY(0); } @property --tw-space-y-reverse { syntax: "*"; @@ -3314,6 +3288,11 @@ syntax: "*"; inherits: false; } +@property --tw-shadow-alpha { + syntax: ""; + inherits: false; + initial-value: 100%; +} @property --tw-inset-shadow { syntax: "*"; inherits: false; @@ -3323,6 +3302,11 @@ syntax: "*"; inherits: false; } +@property --tw-inset-shadow-alpha { + syntax: ""; + inherits: false; + initial-value: 100%; +} @property --tw-ring-color { syntax: "*"; inherits: false; @@ -3405,6 +3389,19 @@ syntax: "*"; inherits: false; } +@property --tw-drop-shadow-color { + syntax: "*"; + inherits: false; +} +@property --tw-drop-shadow-alpha { + syntax: ""; + inherits: false; + initial-value: 100%; +} +@property --tw-drop-shadow-size { + syntax: "*"; + inherits: false; +} @property --tw-backdrop-blur { syntax: "*"; inherits: false; @@ -3454,3 +3451,75 @@ transform: rotate(360deg); } } +@layer properties { + @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) { + *, ::before, ::after, ::backdrop { + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-translate-z: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-scale-z: 1; + --tw-rotate-x: initial; + --tw-rotate-y: initial; + --tw-rotate-z: initial; + --tw-skew-x: initial; + --tw-skew-y: initial; + --tw-space-y-reverse: 0; + --tw-space-x-reverse: 0; + --tw-divide-y-reverse: 0; + --tw-border-style: solid; + --tw-gradient-position: initial; + --tw-gradient-from: #0000; + --tw-gradient-via: #0000; + --tw-gradient-to: #0000; + --tw-gradient-stops: initial; + --tw-gradient-via-stops: initial; + --tw-gradient-from-position: 0%; + --tw-gradient-via-position: 50%; + --tw-gradient-to-position: 100%; + --tw-leading: initial; + --tw-font-weight: initial; + --tw-tracking: initial; + --tw-shadow: 0 0 #0000; + --tw-shadow-color: initial; + --tw-shadow-alpha: 100%; + --tw-inset-shadow: 0 0 #0000; + --tw-inset-shadow-color: initial; + --tw-inset-shadow-alpha: 100%; + --tw-ring-color: initial; + --tw-ring-shadow: 0 0 #0000; + --tw-inset-ring-color: initial; + --tw-inset-ring-shadow: 0 0 #0000; + --tw-ring-inset: initial; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-offset-shadow: 0 0 #0000; + --tw-outline-style: solid; + --tw-blur: initial; + --tw-brightness: initial; + --tw-contrast: initial; + --tw-grayscale: initial; + --tw-hue-rotate: initial; + --tw-invert: initial; + --tw-opacity: initial; + --tw-saturate: initial; + --tw-sepia: initial; + --tw-drop-shadow: initial; + --tw-drop-shadow-color: initial; + --tw-drop-shadow-alpha: 100%; + --tw-drop-shadow-size: initial; + --tw-backdrop-blur: initial; + --tw-backdrop-brightness: initial; + --tw-backdrop-contrast: initial; + --tw-backdrop-grayscale: initial; + --tw-backdrop-hue-rotate: initial; + --tw-backdrop-invert: initial; + --tw-backdrop-opacity: initial; + --tw-backdrop-saturate: initial; + --tw-backdrop-sepia: initial; + --tw-duration: initial; + --tw-ease: initial; + } + } +} diff --git a/ls_shop/public/images/lifestyle.png b/ls_shop/public/images/lifestyle.png index 131686a..4cfa2f9 100644 Binary files a/ls_shop/public/images/lifestyle.png and b/ls_shop/public/images/lifestyle.png differ diff --git a/ls_shop/utils.py b/ls_shop/utils.py index b467d81..45260b1 100644 --- a/ls_shop/utils.py +++ b/ls_shop/utils.py @@ -44,9 +44,7 @@ def get_nested_links(link_doctype, link_name): return _get_nested_links(link_doctype, link_name, ignore_permissions=True) -def get_product_list( - filters=None, product_list=None, page=1, page_length=30, sort_by="default" -): +def get_product_list(filters=None, product_list=None, page=1, page_length=30, sort_by="default"): """Fetches products dynamically based on selected filters.""" query = get_product_base_query(filters, product_list) @@ -64,10 +62,7 @@ def get_product_list( .when( Min(item_price_default.price_list_rate) > 0, ( - ( - Min(item_price_default.price_list_rate) - - Min(item_price_sale.price_list_rate) - ) + (Min(item_price_default.price_list_rate) - Min(item_price_sale.price_list_rate)) / Min(item_price_default.price_list_rate) ) * 100, @@ -118,9 +113,7 @@ def get_total_product_count(filters=None, product_list=None): query = get_product_base_query(filters, product_list) style_attribute_variant = DocType("Style Attribute Variant") - query = query.select( - Count(style_attribute_variant.name).distinct().as_("total_count") - ) + query = query.select(Count(style_attribute_variant.name).distinct().as_("total_count")) result = query.run(as_dict=True) return result[0]["total_count"] if result else 0 @@ -169,17 +162,11 @@ def get_product_base_query(filters=None, product_list=None): # Apply Filters if filters: if filters.get("has_discount"): - query = query.where( - item_price_default.price_list_rate > item_price_sale.price_list_rate - ) + query = query.where(item_price_default.price_list_rate > item_price_sale.price_list_rate) if filters.get("subcategory"): - query = query.where( - style_attribute_variant.item_group.isin(filters["subcategory"]) - ) + query = query.where(style_attribute_variant.item_group.isin(filters["subcategory"])) if filters.get("colors"): - query = query.where( - style_attribute_variant.attribute_name.isin(filters["colors"]) - ) + query = query.where(style_attribute_variant.attribute_name.isin(filters["colors"])) if filters.get("sizes"): query = query.where(color_size_item.size.isin(filters["sizes"])) if filters.get("brands"): @@ -202,15 +189,13 @@ def get_product_base_query(filters=None, product_list=None): | (item.name.like(f"%{search}%")) ) if child_categories: - search_condition |= style_attribute_variant.item_group.isin( - child_categories - ) + search_condition |= style_attribute_variant.item_group.isin(child_categories) # Add exclusion condition if search starts with "men" if search.lower().startswith("men"): - exclusion_condition = ( - ~style_attribute_variant.display_name.like("%women%") - ) & (~style_attribute_variant.item_group.like("%women%")) + exclusion_condition = (~style_attribute_variant.display_name.like("%women%")) & ( + ~style_attribute_variant.item_group.like("%women%") + ) search_condition = search_condition & exclusion_condition query = query.where(search_condition) @@ -221,9 +206,7 @@ def get_delivery_configuration(): shoe_arena_settings = frappe.get_cached_doc("Lifestyle Settings") if not shoe_arena_settings.shipping_rule: return 0, 0 - shipping_rule = frappe.get_cached_doc( - "Shipping Rule", shoe_arena_settings.shipping_rule - ) + shipping_rule = frappe.get_cached_doc("Shipping Rule", shoe_arena_settings.shipping_rule) if not shipping_rule or not shipping_rule.conditions: return 0, 0 condition = shipping_rule.conditions[0] @@ -239,9 +222,7 @@ def get_cod_configuration(): def get_currency_symbol(): - currency = frappe.get_cached_value( - "Global Defaults", "Global Defaults", "default_currency" - ) + currency = frappe.get_cached_value("Global Defaults", "Global Defaults", "default_currency") if currency == "SAR" or frappe.conf.developer_mode: return '' return frappe.get_cached_value("Currency", currency, "symbol") @@ -269,9 +250,7 @@ def format_addresses(addresses, address_type): address.get("state", ""), address.get("country", ""), address.get("pincode", ""), - f"Phone: {address.get('phone')}" - if address.get("phone") - else "", + f"Phone: {address.get('phone')}" if address.get("phone") else "", ] if part ] @@ -330,9 +309,7 @@ def get_current_page(): def can_return(order_name, return_period_days): """Check if the order is still within the return period.""" - invoices = frappe.get_all( - "Sales Invoice", {"sales_order": order_name}, ["name", "creation"], limit=1 - ) + invoices = frappe.get_all("Sales Invoice", {"sales_order": order_name}, ["name", "creation"], limit=1) if not invoices: return False invoice_creation_date = invoices[0].creation @@ -356,9 +333,7 @@ def get_available_stock(item_code, warehouse): "in_stock": 0, } pos_reserved_qty = get_pos_reserved_qty(item_code, warehouse) - actual_qty = ( - flt(bin_data.actual_qty) - flt(bin_data.reserved_qty) - flt(pos_reserved_qty) - ) + actual_qty = flt(bin_data.actual_qty) - flt(bin_data.reserved_qty) - flt(pos_reserved_qty) return { "stock_qty": actual_qty, "in_stock": int(actual_qty > 0), @@ -406,9 +381,7 @@ def update_so_status_from_related_doc(doc, method): sales_orders.update([d.sales_order for d in doc.items if d.sales_order]) elif doc.doctype == "Delivery Note": - sales_orders.update( - [d.against_sales_order for d in doc.items if d.against_sales_order] - ) + sales_orders.update([d.against_sales_order for d in doc.items if d.against_sales_order]) elif doc.doctype == "Shipment": for dn in doc.shipment_delivery_note: @@ -435,9 +408,7 @@ def update_sales_order_ecommerce_status(sales_order_name): new_status = "Waiting for Approval" elif sales_order.docstatus == 1: # Check related documents - dn_exists = frappe.db.exists( - "Delivery Note Item", {"against_sales_order": sales_order.name} - ) + dn_exists = frappe.db.exists("Delivery Note Item", {"against_sales_order": sales_order.name}) shipment_exists = frappe.db.exists( "Shipment Delivery Note", { @@ -451,9 +422,7 @@ def update_sales_order_ecommerce_status(sales_order_name): ] }, ) - invoice_exists = frappe.db.exists( - "Sales Invoice Item", {"sales_order": sales_order.name} - ) + invoice_exists = frappe.db.exists("Sales Invoice Item", {"sales_order": sales_order.name}) if invoice_exists: new_status = "Delivered" @@ -464,6 +433,4 @@ def update_sales_order_ecommerce_status(sales_order_name): else: new_status = "Order Received" - frappe.db.set_value( - "Sales Order", sales_order.name, "custom_ecommerce_status", new_status - ) + frappe.db.set_value("Sales Order", sales_order.name, "custom_ecommerce_status", new_status) diff --git a/ls_shop/www/account/orders/detail.py b/ls_shop/www/account/orders/detail.py index c41e117..13ac2d3 100644 --- a/ls_shop/www/account/orders/detail.py +++ b/ls_shop/www/account/orders/detail.py @@ -16,9 +16,7 @@ def get_context(context): context.return_period = frappe.get_cached_value( "Lifestyle Settings", "Lifestyle Settings", "return_period" ) - return_reasons = frappe.get_cached_value( - "Lifestyle Settings", "Lifestyle Settings", "reason_for_return" - ) + return_reasons = frappe.get_cached_value("Lifestyle Settings", "Lifestyle Settings", "reason_for_return") context.return_reasons = [ {"name": return_reason.name, "display_name": return_reason.display_name} for return_reason in return_reasons diff --git a/ls_shop/www/account/orders/index.py b/ls_shop/www/account/orders/index.py index b8a689e..880b15f 100644 --- a/ls_shop/www/account/orders/index.py +++ b/ls_shop/www/account/orders/index.py @@ -23,9 +23,7 @@ def get_context(context): ) context.orders = orders context.total_count = total_count - return_reasons = frappe.get_cached_value( - "Lifestyle Settings", "Lifestyle Settings", "reason_for_return" - ) + return_reasons = frappe.get_cached_value("Lifestyle Settings", "Lifestyle Settings", "reason_for_return") context.return_reasons = [ {"name": return_reason.name, "display_name": return_reason.display_name} for return_reason in return_reasons diff --git a/ls_shop/www/cart/cart.py b/ls_shop/www/cart/cart.py index 580bf3f..c8769cd 100644 --- a/ls_shop/www/cart/cart.py +++ b/ls_shop/www/cart/cart.py @@ -5,9 +5,7 @@ def get_context(context): - context.delivery_charge, context.delivery_charge_applicable_below = ( - get_delivery_configuration() - ) + context.delivery_charge, context.delivery_charge_applicable_below = get_delivery_configuration() context.breadcrumbs = [ { "label": "Cart", diff --git a/ls_shop/www/cart/checkout.py b/ls_shop/www/cart/checkout.py index 8f1e109..70c40b1 100644 --- a/ls_shop/www/cart/checkout.py +++ b/ls_shop/www/cart/checkout.py @@ -44,9 +44,7 @@ def get_context(context): context.billing_addresses = get_addresses() context.shipping_addresses = get_addresses(address_type="Shipping") context.store_pickup_addresses = get_store_pickup_addresses() - context.delivery_charge, context.delivery_charge_applicable_below = ( - get_delivery_configuration() - ) + context.delivery_charge, context.delivery_charge_applicable_below = get_delivery_configuration() context.cod_charge_applicable_below, context.cod_charge = get_cod_configuration() context.breadcrumbs = [ { @@ -63,9 +61,7 @@ def get_context(context): def get_coupon_code(cart_quotation): coupon_code = "" try: - coupon_code = frappe.get_cached_value( - "Coupon Code", cart_quotation.coupon_code, "coupon_code" - ) + coupon_code = frappe.get_cached_value("Coupon Code", cart_quotation.coupon_code, "coupon_code") except Exception: pass return coupon_code @@ -117,9 +113,7 @@ def get_checkout_items(cart_quotation): @site_cache(ttl=60 * 60) def get_store_pickup_addresses(): # Get all warehouse names with store pickup - warehouses = frappe.get_all( - "Warehouse", filters={"custom_store_pickup": 1}, pluck="name" - ) + warehouses = frappe.get_all("Warehouse", filters={"custom_store_pickup": 1}, pluck="name") if not warehouses: return [] @@ -140,9 +134,7 @@ def get_store_pickup_addresses(): # Map address to warehouse address_to_warehouse = {link["parent"]: link["link_name"] for link in links} address_names = list(address_to_warehouse.keys()) - addresses = frappe.get_all( - "Address", filters={"name": ["in", address_names]}, fields=["*"] - ) + addresses = frappe.get_all("Address", filters={"name": ["in", address_names]}, fields=["*"]) # Format and attach warehouse info formatted_addresses = format_addresses(addresses, address_type="Shop") for addr in formatted_addresses: diff --git a/ls_shop/www/index.html b/ls_shop/www/index.html new file mode 100644 index 0000000..bf54765 --- /dev/null +++ b/ls_shop/www/index.html @@ -0,0 +1,185 @@ + +{% extends "templates/layout.html" %} +{% import "templates/macros/carousel.html" as carousel %} +{% import "templates/macros/item_carousel.html" as item_carousel %} +{% from "templates/macros/item_card.html" import item_card %} + +{% block head %} + + +{% endblock %} + + +{% block uncontained_body %} + +
+
+ + {% call carousel.carousel(pagination = 'True') %} + {% for image in homepage_details.hero_banner %} +
+ +
+ {% endfor %} + {% endcall %} +
+ +
+ {% if homepage_details.gif_1 %} + + {% endif %} + + {% if homepage_details.gif_2 %} + + {% endif %} + + {% if homepage_details.gif_3 %} + + {% endif %} + + {% if homepage_details.gif_4 %} + + {% endif %} +
+ +
+

+ {{ _('New Arrivals') }} + +

+ +
+ + {% call item_carousel.item_carousel(items=new_arrivals,navigation = True, pagination = True) %} + {% endcall %} +
+
+ +
+

+ {{ _('Best Picks') }} +

+
+ {% call item_carousel.item_carousel( + items=best_picks, + navigation=True, + pagination=True) %} + + {% endcall %} +
+ +
+ +
+ +
+
+ +{% endblock %} \ No newline at end of file diff --git a/ls_shop/www/index.py b/ls_shop/www/index.py index 5924da0..ee5ca0b 100644 --- a/ls_shop/www/index.py +++ b/ls_shop/www/index.py @@ -10,26 +10,15 @@ def get_context(context): homepage_details = get_homepage_details() context.homepage_details = homepage_details - new_arrivals = [ - item.item_variant for item in homepage_details.get("new_arrivals", []) - ] + new_arrivals = [item.item_variant for item in homepage_details.get("new_arrivals", [])] best_picks = [item.item_variant for item in homepage_details.get("best_picks", [])] - other_products = [ - item.item_variant for item in homepage_details.get("other_products", []) - ] - best_picks = get_product_list( - product_list=best_picks, page_length=len(best_picks) or 6 - ) - new_arrivals = get_product_list( - product_list=new_arrivals, page_length=len(new_arrivals) or 6 - ) - other_products = get_product_list( - product_list=other_products, page_length=len(other_products) or 6 - ) + + best_picks = get_product_list(product_list=best_picks, page_length=len(best_picks) or 6) + new_arrivals = get_product_list(product_list=new_arrivals, page_length=len(new_arrivals) or 6) context["new_arrivals"] = new_arrivals context["best_picks"] = best_picks - context["other_products"] = other_products + context.show_breadcrumb = False @@ -38,11 +27,9 @@ def get_homepage_details(): homepage_details = {} if frappe.local.lang == "ar": homepage_details = { - "hero_banner": homepage_settings.hero_banner_ar - or homepage_settings.hero_banner, + "hero_banner": homepage_settings.hero_banner_ar or homepage_settings.hero_banner, "new_arrivals": homepage_settings.new_arrivals, "best_picks": homepage_settings.best_picks, - "other_products": homepage_settings.other_products, "gif_1": homepage_settings.gif_1_ar or homepage_settings.gif_1, "gif_url_1": homepage_settings.gif_url_1_ar or homepage_settings.gif_url_1, "gif_2": homepage_settings.gif_2_ar or homepage_settings.gif_2, @@ -51,30 +38,14 @@ def get_homepage_details(): "gif_url_3": homepage_settings.gif_url_3_ar or homepage_settings.gif_url_3, "gif_4": homepage_settings.gif_4_ar or homepage_settings.gif_4, "gif_url_4": homepage_settings.gif_url_4_ar or homepage_settings.gif_url_4, - "browse_by_brands": homepage_settings.browse_by_brands_ar - or homepage_settings.browse_by_brands, "banner_1": homepage_settings.banner_1_ar or homepage_settings.banner_1, - "banner_url_1": homepage_settings.banner_url_1_ar - or homepage_settings.banner_url_1, - "banner_2": homepage_settings.banner_2_ar or homepage_settings.banner_2, - "banner_url_2": homepage_settings.banner_url_2_ar - or homepage_settings.banner_url_2, - "banner_3": homepage_settings.banner_3_ar or homepage_settings.banner_3, - "banner_url_3": homepage_settings.banner_url_3_ar - or homepage_settings.banner_url_3, - "banner_4": homepage_settings.banner_4_ar or homepage_settings.banner_4, - "banner_url_4": homepage_settings.banner_url_4_ar - or homepage_settings.banner_url_4, - "banner_5": homepage_settings.banner_5_ar or homepage_settings.banner_5, - "banner_url_5": homepage_settings.banner_url_5_ar - or homepage_settings.banner_url_5, + "banner_url_1": homepage_settings.banner_url_1_ar or homepage_settings.banner_url_1, } else: homepage_details = { "hero_banner": homepage_settings.hero_banner, "new_arrivals": homepage_settings.new_arrivals, "best_picks": homepage_settings.best_picks, - "other_products": homepage_settings.other_products, "gif_1": homepage_settings.gif_1, "gif_url_1": homepage_settings.gif_url_1, "gif_2": homepage_settings.gif_2, @@ -83,16 +54,7 @@ def get_homepage_details(): "gif_url_3": homepage_settings.gif_url_3, "gif_4": homepage_settings.gif_4, "gif_url_4": homepage_settings.gif_url_4, - "browse_by_brands": homepage_settings.browse_by_brands, "banner_1": homepage_settings.banner_1, "banner_url_1": homepage_settings.banner_url_1, - "banner_2": homepage_settings.banner_2, - "banner_url_2": homepage_settings.banner_url_2, - "banner_3": homepage_settings.banner_3, - "banner_url_3": homepage_settings.banner_url_3, - "banner_4": homepage_settings.banner_4, - "banner_url_4": homepage_settings.banner_url_4, - "banner_5": homepage_settings.banner_5, - "banner_url_5": homepage_settings.banner_url_5, } return homepage_details diff --git a/ls_shop/www/products/details.py b/ls_shop/www/products/details.py index 9b6cf94..2fc27ec 100644 --- a/ls_shop/www/products/details.py +++ b/ls_shop/www/products/details.py @@ -16,9 +16,7 @@ def get_context(context): try: # Fetch product variant and related item - product_variant = frappe.get_doc( - "Style Attribute Variant", {"route": product_route} - ) + product_variant = frappe.get_doc("Style Attribute Variant", {"route": product_route}) product = frappe.get_doc("Item", product_variant.item_style) # Get available sizes and selected item @@ -58,23 +56,18 @@ def get_context(context): if product_variant.images else [default_product_image()] ) - context.product_in_stock = ( - selected_item.get("stock_detail", {}).get("stock_qty", 0) > 0 - ) + context.product_in_stock = selected_item.get("stock_detail", {}).get("stock_qty", 0) > 0 # Enrich selected item details selected_item_doc = frappe.get_cached_doc("Item", selected_item["item_code"]) selected_item.update( { "item_name": selected_item_doc.item_name, - "item_name_ar": selected_item_doc.get("custom_item_name_ar") - or selected_item_doc.item_name, + "item_name_ar": selected_item_doc.get("custom_item_name_ar") or selected_item_doc.item_name, "style_code": selected_item_doc.get("custom_style_code", ""), "material": selected_item_doc.get("custom_material", ""), "description": selected_item_doc.get("description", ""), - "description_ar": selected_item_doc.get( - "custom_description_ar", selected_item_doc.description - ), + "description_ar": selected_item_doc.get("custom_description_ar", selected_item_doc.description), } ) @@ -87,9 +80,7 @@ def get_context(context): context.default_price = price_data["default_price"] context.recommended_items = recommended_items context.other_variants = other_variants - context.discount_percent = get_discount_percent( - price_data["default_price"], price_data["sale_price"] - ) + context.discount_percent = get_discount_percent(price_data["default_price"], price_data["sale_price"]) context.size_chart = get_size_chart(product.brand, product_variant.item_group) context.item_qty = get_available_stock(product.item_code, warehouse) context.breadcrumbs = [ diff --git a/ls_shop/www/products/list.py b/ls_shop/www/products/list.py index de579d1..db95ecd 100644 --- a/ls_shop/www/products/list.py +++ b/ls_shop/www/products/list.py @@ -37,9 +37,7 @@ def get_filter_colors(filters=None): query = get_product_base_query(filter_copy) variant = DocType("Style Attribute Variant") - query = ( - query.select(variant.attribute_name).distinct().orderby(variant.attribute_name) - ) + query = query.select(variant.attribute_name).distinct().orderby(variant.attribute_name) colors = query.run(pluck=True) if not colors: return colors @@ -54,11 +52,7 @@ def get_filter_sizes(filters=None): query = get_product_base_query(filter_copy) color_size_item = DocType("Color Size Item") - query = ( - query.select(color_size_item.size) - .distinct() - .orderby(Cast_(color_size_item.size, "Decimal")) - ) + query = query.select(color_size_item.size).distinct().orderby(Cast_(color_size_item.size, "Decimal")) return query.run(pluck=True) @@ -101,17 +95,13 @@ def get_product_filters(selected_filters): # Define DocTypes category = selected_filters.get("category", "") item_price = DocType("Item Price") - sale_price_list = frappe.get_cached_value( - "Lifestyle Settings", "Lifestyle Settings", "sale_price_list" - ) + sale_price_list = frappe.get_cached_value("Lifestyle Settings", "Lifestyle Settings", "sale_price_list") # Get price range (min and max) price_range = ( frappe.qb.from_(item_price) .select( Min(item_price.price_list_rate).as_("min_price"), - frappe.query_builder.functions.Max(item_price.price_list_rate).as_( - "max_price" - ), + frappe.query_builder.functions.Max(item_price.price_list_rate).as_("max_price"), ) .where(item_price.price_list == sale_price_list) # Adjust price list as needed ).run(as_dict=True) @@ -139,15 +129,9 @@ def get_selected_filters(): "subcategory": query_params.get("subcategory", "").split(",") if query_params.get("subcategory") else [], - "colors": query_params.get("colors", "").split(",") - if query_params.get("colors") - else [], - "sizes": query_params.get("sizes", "").split(",") - if query_params.get("sizes") - else [], - "brands": query_params.get("brands", "").split(",") - if query_params.get("brands") - else [], + "colors": query_params.get("colors", "").split(",") if query_params.get("colors") else [], + "sizes": query_params.get("sizes", "").split(",") if query_params.get("sizes") else [], + "brands": query_params.get("brands", "").split(",") if query_params.get("brands") else [], "search": query_params.get("search", ""), "category": query_params.get("category", ""), "has_discount": query_params.get("has_discount", "0") == "1", @@ -186,9 +170,7 @@ def get_context(context): # Pass data to frontend context.products = products context.current_page = page - context.total_count = get_total_product_count( - filters=selected_filters - ) # Adjust for pagination + context.total_count = get_total_product_count(filters=selected_filters) # Adjust for pagination context.filters = filters # Updated filters context.selected_filters = selected_filters context.price_range = price_range # Updated price range diff --git a/package.json b/package.json new file mode 100644 index 0000000..dd6a4ff --- /dev/null +++ b/package.json @@ -0,0 +1,30 @@ +{ + "name": "ls_shop", + "version": "1.0.0", + "description": "ShoeArena Frontend", + "main": "index.js", + "scripts": { + "postinstall": "yarn tailwind:build", + "build": "yarn tailwind:build", + "tailwind:watch": "npx @tailwindcss/cli -i ./ls_shop/public/css/main.css -o ./ls_shop/public/css/tailwind.css --watch", + "tailwind:build": "npx @tailwindcss/cli -i ./ls_shop/public/css/main.css -o ./ls_shop/public/css/tailwind.css" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/BuildWithHussain/ls_shop.git" + }, + "author": "BWH Studios", + "license": "ISC", + "bugs": { + "url": "https://github.com/BuildWithHussain/ls_shop/issues" + }, + "homepage": "https://github.com/BuildWithHussain/ls_shop#readme", + "dependencies": { + "@tailwindcss/cli": "^4.0.14", + "tailwindcss": "^4.0.14" + }, + "devDependencies": { + "@biomejs/biome": "1.9.4", + "@tailwindcss/typography": "^0.5.16" + } +} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..38c2375 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,586 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@biomejs/biome@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/biome/-/biome-1.9.4.tgz#89766281cbc3a0aae865a7ff13d6aaffea2842bf" + integrity sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog== + optionalDependencies: + "@biomejs/cli-darwin-arm64" "1.9.4" + "@biomejs/cli-darwin-x64" "1.9.4" + "@biomejs/cli-linux-arm64" "1.9.4" + "@biomejs/cli-linux-arm64-musl" "1.9.4" + "@biomejs/cli-linux-x64" "1.9.4" + "@biomejs/cli-linux-x64-musl" "1.9.4" + "@biomejs/cli-win32-arm64" "1.9.4" + "@biomejs/cli-win32-x64" "1.9.4" + +"@biomejs/cli-darwin-arm64@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.4.tgz#dfa376d23a54a2d8f17133c92f23c1bf2e62509f" + integrity sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw== + +"@biomejs/cli-darwin-x64@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.4.tgz#eafc2ce3849d385fc02238aad1ca4a73395a64d9" + integrity sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg== + +"@biomejs/cli-linux-arm64-musl@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.4.tgz#d780c3e01758fc90f3268357e3f19163d1f84fca" + integrity sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA== + +"@biomejs/cli-linux-arm64@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.4.tgz#8ed1dd0e89419a4b66a47f95aefb8c46ae6041c9" + integrity sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g== + +"@biomejs/cli-linux-x64-musl@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.4.tgz#f36982b966bd671a36671e1de4417963d7db15fb" + integrity sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg== + +"@biomejs/cli-linux-x64@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.4.tgz#a0a7f56680c76b8034ddc149dbf398bdd3a462e8" + integrity sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg== + +"@biomejs/cli-win32-arm64@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.4.tgz#e2ef4e0084e76b7e26f0fc887c5ef1265ea56200" + integrity sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg== + +"@biomejs/cli-win32-x64@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.4.tgz#4c7afa90e3970213599b4095e62f87e5972b2340" + integrity sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA== + +"@emnapi/core@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.5.0.tgz#85cd84537ec989cebb2343606a1ee663ce4edaf0" + integrity sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg== + dependencies: + "@emnapi/wasi-threads" "1.1.0" + tslib "^2.4.0" + +"@emnapi/runtime@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.5.0.tgz#9aebfcb9b17195dce3ab53c86787a6b7d058db73" + integrity sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ== + dependencies: + tslib "^2.4.0" + +"@emnapi/wasi-threads@1.1.0", "@emnapi/wasi-threads@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz#60b2102fddc9ccb78607e4a3cf8403ea69be41bf" + integrity sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ== + dependencies: + tslib "^2.4.0" + +"@isaacs/fs-minipass@^4.0.0": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz#2d59ae3ab4b38fb4270bfa23d30f8e2e86c7fe32" + integrity sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w== + dependencies: + minipass "^7.0.4" + +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.13" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f" + integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.0" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/remapping@^2.3.4": + version "2.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/remapping/-/remapping-2.3.5.tgz#375c476d1972947851ba1e15ae8f123047445aa1" + integrity sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0", "@jridgewell/sourcemap-codec@^1.5.5": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" + integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== + +"@jridgewell/trace-mapping@^0.3.24": + version "0.3.31" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0" + integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@napi-rs/wasm-runtime@^1.0.5": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.6.tgz#ba6cf875b7bf96052d0de29a91b029c94c6e9a48" + integrity sha512-DXj75ewm11LIWUk198QSKUTxjyRjsBwk09MuMk5DGK+GDUtyPhhEHOGP/Xwwj3DjQXXkivoBirmOnKrLfc0+9g== + dependencies: + "@emnapi/core" "^1.5.0" + "@emnapi/runtime" "^1.5.0" + "@tybys/wasm-util" "^0.10.1" + +"@parcel/watcher-android-arm64@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz#507f836d7e2042f798c7d07ad19c3546f9848ac1" + integrity sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA== + +"@parcel/watcher-darwin-arm64@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz#3d26dce38de6590ef79c47ec2c55793c06ad4f67" + integrity sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw== + +"@parcel/watcher-darwin-x64@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz#99f3af3869069ccf774e4ddfccf7e64fd2311ef8" + integrity sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg== + +"@parcel/watcher-freebsd-x64@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz#14d6857741a9f51dfe51d5b08b7c8afdbc73ad9b" + integrity sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ== + +"@parcel/watcher-linux-arm-glibc@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz#43c3246d6892381db473bb4f663229ad20b609a1" + integrity sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA== + +"@parcel/watcher-linux-arm-musl@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz#663750f7090bb6278d2210de643eb8a3f780d08e" + integrity sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q== + +"@parcel/watcher-linux-arm64-glibc@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz#ba60e1f56977f7e47cd7e31ad65d15fdcbd07e30" + integrity sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w== + +"@parcel/watcher-linux-arm64-musl@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz#f7fbcdff2f04c526f96eac01f97419a6a99855d2" + integrity sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg== + +"@parcel/watcher-linux-x64-glibc@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz#4d2ea0f633eb1917d83d483392ce6181b6a92e4e" + integrity sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A== + +"@parcel/watcher-linux-x64-musl@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz#277b346b05db54f55657301dd77bdf99d63606ee" + integrity sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg== + +"@parcel/watcher-win32-arm64@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz#7e9e02a26784d47503de1d10e8eab6cceb524243" + integrity sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw== + +"@parcel/watcher-win32-ia32@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz#2d0f94fa59a873cdc584bf7f6b1dc628ddf976e6" + integrity sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ== + +"@parcel/watcher-win32-x64@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz#ae52693259664ba6f2228fa61d7ee44b64ea0947" + integrity sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA== + +"@parcel/watcher@^2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.5.1.tgz#342507a9cfaaf172479a882309def1e991fb1200" + integrity sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg== + dependencies: + detect-libc "^1.0.3" + is-glob "^4.0.3" + micromatch "^4.0.5" + node-addon-api "^7.0.0" + optionalDependencies: + "@parcel/watcher-android-arm64" "2.5.1" + "@parcel/watcher-darwin-arm64" "2.5.1" + "@parcel/watcher-darwin-x64" "2.5.1" + "@parcel/watcher-freebsd-x64" "2.5.1" + "@parcel/watcher-linux-arm-glibc" "2.5.1" + "@parcel/watcher-linux-arm-musl" "2.5.1" + "@parcel/watcher-linux-arm64-glibc" "2.5.1" + "@parcel/watcher-linux-arm64-musl" "2.5.1" + "@parcel/watcher-linux-x64-glibc" "2.5.1" + "@parcel/watcher-linux-x64-musl" "2.5.1" + "@parcel/watcher-win32-arm64" "2.5.1" + "@parcel/watcher-win32-ia32" "2.5.1" + "@parcel/watcher-win32-x64" "2.5.1" + +"@tailwindcss/cli@^4.0.14": + version "4.1.14" + resolved "https://registry.yarnpkg.com/@tailwindcss/cli/-/cli-4.1.14.tgz#1c71e11b919667457d154545ba5b8d82c55c537e" + integrity sha512-2cErQRcsI8jIObUMVwcd1H2AWgGxwzozHJk7AKM2KB1taOp7L15xQ8kEsZrvVbOjNrb8yXtnSvNtJ+mhCB7EBg== + dependencies: + "@parcel/watcher" "^2.5.1" + "@tailwindcss/node" "4.1.14" + "@tailwindcss/oxide" "4.1.14" + enhanced-resolve "^5.18.3" + mri "^1.2.0" + picocolors "^1.1.1" + tailwindcss "4.1.14" + +"@tailwindcss/node@4.1.14": + version "4.1.14" + resolved "https://registry.yarnpkg.com/@tailwindcss/node/-/node-4.1.14.tgz#cf3864490c746db6b06b46aa235df9021a289bad" + integrity sha512-hpz+8vFk3Ic2xssIA3e01R6jkmsAhvkQdXlEbRTk6S10xDAtiQiM3FyvZVGsucefq764euO/b8WUW9ysLdThHw== + dependencies: + "@jridgewell/remapping" "^2.3.4" + enhanced-resolve "^5.18.3" + jiti "^2.6.0" + lightningcss "1.30.1" + magic-string "^0.30.19" + source-map-js "^1.2.1" + tailwindcss "4.1.14" + +"@tailwindcss/oxide-android-arm64@4.1.14": + version "4.1.14" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.14.tgz#8903678d75715d913b8f7c5f6fa0517af83b5111" + integrity sha512-a94ifZrGwMvbdeAxWoSuGcIl6/DOP5cdxagid7xJv6bwFp3oebp7y2ImYsnZBMTwjn5Ev5xESvS3FFYUGgPODQ== + +"@tailwindcss/oxide-darwin-arm64@4.1.14": + version "4.1.14" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.14.tgz#72d56afadce829047a83d8512f29ee16cf6fbea5" + integrity sha512-HkFP/CqfSh09xCnrPJA7jud7hij5ahKyWomrC3oiO2U9i0UjP17o9pJbxUN0IJ471GTQQmzwhp0DEcpbp4MZTA== + +"@tailwindcss/oxide-darwin-x64@4.1.14": + version "4.1.14" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.14.tgz#ac1af82da01299143129fdf615f6fcc046b4094e" + integrity sha512-eVNaWmCgdLf5iv6Qd3s7JI5SEFBFRtfm6W0mphJYXgvnDEAZ5sZzqmI06bK6xo0IErDHdTA5/t7d4eTfWbWOFw== + +"@tailwindcss/oxide-freebsd-x64@4.1.14": + version "4.1.14" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.14.tgz#a955cedf9b020147d222f92490e9d331db9b5c36" + integrity sha512-QWLoRXNikEuqtNb0dhQN6wsSVVjX6dmUFzuuiL09ZeXju25dsei2uIPl71y2Ic6QbNBsB4scwBoFnlBfabHkEw== + +"@tailwindcss/oxide-linux-arm-gnueabihf@4.1.14": + version "4.1.14" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.14.tgz#5474bee4d377144107f3f0198a3c0225a46c02e6" + integrity sha512-VB4gjQni9+F0VCASU+L8zSIyjrLLsy03sjcR3bM0V2g4SNamo0FakZFKyUQ96ZVwGK4CaJsc9zd/obQy74o0Fw== + +"@tailwindcss/oxide-linux-arm64-gnu@4.1.14": + version "4.1.14" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.14.tgz#b06ca140083b353735414e32f7a8786f55ce2dd6" + integrity sha512-qaEy0dIZ6d9vyLnmeg24yzA8XuEAD9WjpM5nIM1sUgQ/Zv7cVkharPDQcmm/t/TvXoKo/0knI3me3AGfdx6w1w== + +"@tailwindcss/oxide-linux-arm64-musl@4.1.14": + version "4.1.14" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.14.tgz#85f4cabea2a07609274d1f747bd098c5da2a7cd2" + integrity sha512-ISZjT44s59O8xKsPEIesiIydMG/sCXoMBCqsphDm/WcbnuWLxxb+GcvSIIA5NjUw6F8Tex7s5/LM2yDy8RqYBQ== + +"@tailwindcss/oxide-linux-x64-gnu@4.1.14": + version "4.1.14" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.14.tgz#0d7fbf91763a2f6886044a050298489107d120bd" + integrity sha512-02c6JhLPJj10L2caH4U0zF8Hji4dOeahmuMl23stk0MU1wfd1OraE7rOloidSF8W5JTHkFdVo/O7uRUJJnUAJg== + +"@tailwindcss/oxide-linux-x64-musl@4.1.14": + version "4.1.14" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.14.tgz#93578713064ba4c16df517df01b3c546ecc9878d" + integrity sha512-TNGeLiN1XS66kQhxHG/7wMeQDOoL0S33x9BgmydbrWAb9Qw0KYdd8o1ifx4HOGDWhVmJ+Ul+JQ7lyknQFilO3Q== + +"@tailwindcss/oxide-wasm32-wasi@4.1.14": + version "4.1.14" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.14.tgz#9e55999129a952a3dcc2196cc9cc55248cc1b1fe" + integrity sha512-uZYAsaW/jS/IYkd6EWPJKW/NlPNSkWkBlaeVBi/WsFQNP05/bzkebUL8FH1pdsqx4f2fH/bWFcUABOM9nfiJkQ== + dependencies: + "@emnapi/core" "^1.5.0" + "@emnapi/runtime" "^1.5.0" + "@emnapi/wasi-threads" "^1.1.0" + "@napi-rs/wasm-runtime" "^1.0.5" + "@tybys/wasm-util" "^0.10.1" + tslib "^2.4.0" + +"@tailwindcss/oxide-win32-arm64-msvc@4.1.14": + version "4.1.14" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.14.tgz#097c00bfc60cd84943a9cb5e853b25fa25525c77" + integrity sha512-Az0RnnkcvRqsuoLH2Z4n3JfAef0wElgzHD5Aky/e+0tBUxUhIeIqFBTMNQvmMRSP15fWwmvjBxZ3Q8RhsDnxAA== + +"@tailwindcss/oxide-win32-x64-msvc@4.1.14": + version "4.1.14" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.14.tgz#eaa49fa930ce16b23478d3b58c079a40ac0b6622" + integrity sha512-ttblVGHgf68kEE4om1n/n44I0yGPkCPbLsqzjvybhpwa6mKKtgFfAzy6btc3HRmuW7nHe0OOrSeNP9sQmmH9XA== + +"@tailwindcss/oxide@4.1.14": + version "4.1.14" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide/-/oxide-4.1.14.tgz#acfc7869142665693b3b08e4e51d0f419ca13662" + integrity sha512-23yx+VUbBwCg2x5XWdB8+1lkPajzLmALEfMb51zZUBYaYVPDQvBSD/WYDqiVyBIo2BZFa3yw1Rpy3G2Jp+K0dw== + dependencies: + detect-libc "^2.0.4" + tar "^7.5.1" + optionalDependencies: + "@tailwindcss/oxide-android-arm64" "4.1.14" + "@tailwindcss/oxide-darwin-arm64" "4.1.14" + "@tailwindcss/oxide-darwin-x64" "4.1.14" + "@tailwindcss/oxide-freebsd-x64" "4.1.14" + "@tailwindcss/oxide-linux-arm-gnueabihf" "4.1.14" + "@tailwindcss/oxide-linux-arm64-gnu" "4.1.14" + "@tailwindcss/oxide-linux-arm64-musl" "4.1.14" + "@tailwindcss/oxide-linux-x64-gnu" "4.1.14" + "@tailwindcss/oxide-linux-x64-musl" "4.1.14" + "@tailwindcss/oxide-wasm32-wasi" "4.1.14" + "@tailwindcss/oxide-win32-arm64-msvc" "4.1.14" + "@tailwindcss/oxide-win32-x64-msvc" "4.1.14" + +"@tailwindcss/typography@^0.5.16": + version "0.5.19" + resolved "https://registry.yarnpkg.com/@tailwindcss/typography/-/typography-0.5.19.tgz#ecb734af2569681eb40932f09f60c2848b909456" + integrity sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg== + dependencies: + postcss-selector-parser "6.0.10" + +"@tybys/wasm-util@^0.10.1": + version "0.10.1" + resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.10.1.tgz#ecddd3205cf1e2d5274649ff0eedd2991ed7f414" + integrity sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg== + dependencies: + tslib "^2.4.0" + +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +chownr@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-3.0.0.tgz#9855e64ecd240a9cc4267ce8a4aa5d24a1da15e4" + integrity sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g== + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +detect-libc@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== + +detect-libc@^2.0.3, detect-libc@^2.0.4: + version "2.1.2" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad" + integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ== + +enhanced-resolve@^5.18.3: + version "5.18.3" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz#9b5f4c5c076b8787c78fe540392ce76a88855b44" + integrity sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +graceful-fs@^4.2.4: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +jiti@^2.6.0: + version "2.6.1" + resolved "https://registry.yarnpkg.com/jiti/-/jiti-2.6.1.tgz#178ef2fc9a1a594248c20627cd820187a4d78d92" + integrity sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ== + +lightningcss-darwin-arm64@1.30.1: + version "1.30.1" + resolved "https://registry.yarnpkg.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz#3d47ce5e221b9567c703950edf2529ca4a3700ae" + integrity sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ== + +lightningcss-darwin-x64@1.30.1: + version "1.30.1" + resolved "https://registry.yarnpkg.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz#e81105d3fd6330860c15fe860f64d39cff5fbd22" + integrity sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA== + +lightningcss-freebsd-x64@1.30.1: + version "1.30.1" + resolved "https://registry.yarnpkg.com/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz#a0e732031083ff9d625c5db021d09eb085af8be4" + integrity sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig== + +lightningcss-linux-arm-gnueabihf@1.30.1: + version "1.30.1" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz#1f5ecca6095528ddb649f9304ba2560c72474908" + integrity sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q== + +lightningcss-linux-arm64-gnu@1.30.1: + version "1.30.1" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz#eee7799726103bffff1e88993df726f6911ec009" + integrity sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw== + +lightningcss-linux-arm64-musl@1.30.1: + version "1.30.1" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz#f2e4b53f42892feeef8f620cbb889f7c064a7dfe" + integrity sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ== + +lightningcss-linux-x64-gnu@1.30.1: + version "1.30.1" + resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz#2fc7096224bc000ebb97eea94aea248c5b0eb157" + integrity sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw== + +lightningcss-linux-x64-musl@1.30.1: + version "1.30.1" + resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz#66dca2b159fd819ea832c44895d07e5b31d75f26" + integrity sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ== + +lightningcss-win32-arm64-msvc@1.30.1: + version "1.30.1" + resolved "https://registry.yarnpkg.com/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz#7d8110a19d7c2d22bfdf2f2bb8be68e7d1b69039" + integrity sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA== + +lightningcss-win32-x64-msvc@1.30.1: + version "1.30.1" + resolved "https://registry.yarnpkg.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz#fd7dd008ea98494b85d24b4bea016793f2e0e352" + integrity sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg== + +lightningcss@1.30.1: + version "1.30.1" + resolved "https://registry.yarnpkg.com/lightningcss/-/lightningcss-1.30.1.tgz#78e979c2d595bfcb90d2a8c0eb632fe6c5bfed5d" + integrity sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg== + dependencies: + detect-libc "^2.0.3" + optionalDependencies: + lightningcss-darwin-arm64 "1.30.1" + lightningcss-darwin-x64 "1.30.1" + lightningcss-freebsd-x64 "1.30.1" + lightningcss-linux-arm-gnueabihf "1.30.1" + lightningcss-linux-arm64-gnu "1.30.1" + lightningcss-linux-arm64-musl "1.30.1" + lightningcss-linux-x64-gnu "1.30.1" + lightningcss-linux-x64-musl "1.30.1" + lightningcss-win32-arm64-msvc "1.30.1" + lightningcss-win32-x64-msvc "1.30.1" + +magic-string@^0.30.19: + version "0.30.19" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.19.tgz#cebe9f104e565602e5d2098c5f2e79a77cc86da9" + integrity sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.5" + +micromatch@^4.0.5: + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + +minipass@^7.0.4, minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + +minizlib@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-3.1.0.tgz#6ad76c3a8f10227c9b51d1c9ac8e30b27f5a251c" + integrity sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw== + dependencies: + minipass "^7.1.2" + +mri@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b" + integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA== + +node-addon-api@^7.0.0: + version "7.1.1" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558" + integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ== + +picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +postcss-selector-parser@6.0.10: + version "6.0.10" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d" + integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +source-map-js@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + +tailwindcss@4.1.14, tailwindcss@^4.0.14: + version "4.1.14" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-4.1.14.tgz#a5907cc2202a2a1f5f15bac6f2031e53117e43a8" + integrity sha512-b7pCxjGO98LnxVkKjaZSDeNuljC4ueKUddjENJOADtubtdo8llTaJy7HwBMeLNSSo2N5QIAgklslK1+Ir8r6CA== + +tapable@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.3.0.tgz#7e3ea6d5ca31ba8e078b560f0d83ce9a14aa8be6" + integrity sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg== + +tar@^7.5.1: + version "7.5.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-7.5.1.tgz#750a8bd63b7c44c1848e7bf982260a083cf747c9" + integrity sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g== + dependencies: + "@isaacs/fs-minipass" "^4.0.0" + chownr "^3.0.0" + minipass "^7.1.2" + minizlib "^3.1.0" + yallist "^5.0.0" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +tslib@^2.4.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + +util-deprecate@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +yallist@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-5.0.0.tgz#00e2de443639ed0d78fd87de0d27469fbcffb533" + integrity sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==