🦄 Simple taxes (#2072)
* Tax configuration - implement channels view (#2048) * Add channels view * Fix channels view import * Remove legacy stories references * Fix link in configuration * Update snapshots * Remove sample checkboxes props * Disable hover in country exceptions * Update snapshots * Extract country exception rows to seperate component * Extract components to seperate files * Remove duplicated section name * Remove backlink * Add translations to section names * Extract messages * Add ListItemLink component * Replace navigator with link in TaxChannelsMenu * Fix horizontal scroll in TaxChannelsMenu * Change codegen to build from custom schema * Build types * Update fragments * Add fixtures * Change any to proper types * Add story for tax channels page * Replace MUI Skeleton with Saleor Skeleton * Change clsx import to classnames * Fix checkboxes shadows in settings card * Update IDs in fixtures * Fix offset in TaxChannelsMenu * Update snapshots * Remove any from TaxSettings * Fix todos * Change relative marginLeft to before pseudoelement * Extract styles to seperate files * Change folder structure * Extract redirect logic to custom hook * Update snapshots * Fix comment * Add early return in channels view * Tax configuration - implement countries view (#2053) * Add channels view * Remove sample checkboxes props * Disable hover in country exceptions * Extract country exception rows to seperate component * Extract components to seperate files * Remove duplicated section name * Remove backlink * Add translations to section names * Replace navigator with link in TaxChannelsMenu * Fix horizontal scroll in TaxChannelsMenu * Change any to proper types * Add story for tax channels page * Replace MUI Skeleton with Saleor Skeleton * Change clsx import to classnames * Fix checkboxes shadows in settings card * Update IDs in fixtures * Fix offset in TaxChannelsMenu * Remove any from TaxSettings * Add countries list view * Add TaxCountryMenu component * Add CountryList page * Change channels menu rows height * Change countries menu rows height * Add TaxInput component * Add tax classes rates to countries page * Fix search input padding * Add minmax to TaxInput * Add searching through tax class rates * Extract messages * Add better handlers * Add fullWidth to TaxInput * Specify type for TaxInputs * Remove spinboxes on firefox * Remove custom spinboxes * Remove maxHeight from menu rows * Post-rebase fix * Change setter to formchagne * Add TaxConfiguration fragment * Add isDefault field to taxClass * Add fixtures * Shape data * Replace useEffect with useTaxUrlRedirect * Fix country names in menu * Add country page story * Add early return in countries view * Unify loading states between channels and countries pages * Handle special chars and case insensitiveness in local search * Replace navigate function with ListItemLink * Move styles to seperate file * Move styles to seperate file * Migrate to strict null checks * Remove unnecessary optional chaining * Change overflow scroll to Y only * Add useMemo on finding selected country * Add useMemo on local search * Translate labels in page tabs * Change url from /taxes/classes to /taxes/tax-classes * Remove capitalization from strings * Extract messages * Bump macaw to 0.6.2 * Update snapshots * Add spinboxes explanation comment * Handle empty state * Add tax classes view (#2093) * Add TaxClass fragment * Build types * Add tax classes to fixtures * Add tax classes view * wip Add tax classes page * Add tax classes menu * Add TaxRate fragment * Extract logic * Handle loading state & add story * Extract messages * Update snapshots * Change schema building from schema back to introspection * Update schema * Update fragments * Build types * Update fixtures * Reshape data * Move styles to seperate file * Use getById * Add explicit undefined * Comment out unfinished modal stories * Update snapshots * Taxes - add API calls in channels view (#2106) * Build types * Add TaxRate fragment * Update snapshots * Add taxConfigurationList query * Add taxCountriesList query * Add TaxClassesList query * Rename TaxConfigurationsList query * Handle empty state * Fix types post-rebase * Add form to TaxChannelsPage * wip Add dialog for handling country exceptions * wip Fix dialog url * wip Add update exceptions handlers * Add dialog story * Fix type errors * Add mutation support * Fix types in story * Add transition state to submit button * Add notifier * Extract messages * Remove unused import * Add backlink in savebar * Update snapshots * Fix link in navigation * Update snapshots * Remove message from tax config error fragment * Add hook description * Use useStateFromProps * Remove error handling * Improve url & path function names * Use theme.spacing in TaxCountryDialog styles * Remove redundant key modification * Revert "Use useStateFromProps" This reverts commit d3c68b04701cf935e917d7baa3ed1361ca3446d5. * Move initial map to parent & add open dependency to countries state * Use useModalDialogOpen * Fix state update * Remove scrolls & add ellipsis in side menu * Center checkboxes * Update snapshots * Add fake div for list alignment * Trigger deployment * Close modal on submit * Remove divider on last ListItem * Align add country button * Wrap grid child in div to avoid card stretching * Update snapshots * Trigger changes in add/delete exceptions * Trigger change on expcetion checkboxes * Add trailing commas * Connect countries view to API (#2178) * Add empty states * Update countries view urls * Remove unused import * Add country modal to countries view * Update schema * Implement country view mutations & error fragments * Implement tax class update mutation * Add sidebar temporary state for new configs * Remove unused imports * Wrap in form * Add savebar & fix search * Update schema * Add form wrapper * Fix types * Extract messages * Bump macaw * Update snapshots * Fix comma dangles * Update snapshots * Notify about mutation success * Add logic for mixing current and new rates * Workaround for sending null rates * Fix filling form with correct data after submitting * Handle deleting configuration * Fix selected banner * Remove leftover comment * Add handler for country configuration delete * Trigger deployment * Clean up useEffects causing infinite render loops * Sort countries from api by name * Fix card bottom padding * Remove bottom divider & fix padding * Remove scroll wrapper in side menu * Update snapshots * Remove scroll wrapper from tax classes menu * Update snapshots * Refresh form to initial onSubmit * Revert "Refresh form to initial onSubmit" This reverts commit 42414237d35086da63f4aa088c8072411429b1d8. * Allow only 3 decimal characters in tax inputs * Update snapshots * Update schema * Update types * Change logic from default tax class to null class * Fix sorting * Send empty country rates as nulls in mutation * Extract messages * Update lockfile * Update schema * Drop default tax classes * Update snapshots * Post-rebase fixes * Connect tax classes view to API (#2334) * Add mutations * Handle empty state * Wrap page in form * Update stories * Build types * Handle tax class delete * Handle update tax class * Update stories * Handle tax class change name * Add mutation state to savebar * Handle creating new tax classes * Extract messages * Specify type * Update stories * Sort rates * Fix skeleton rendering * Remove placeholders * Fix skeleton rendering on country list * Update snapshots * Change initial pagination to 100 * Disallow creating multiple new tax classes * Disallow creating multiple country configurations * Fix messages * Autofocus on new tax class name * Add country name to header * Temporarily comment out broken code in tax channels * Update snapshots * Update snapshots post-rebase * Add tax strategies & assigning tax classes (#2369) * Update fragments * Add optional merging in useForm * Handle tax strategies * Update snapshots * Update fixtures * Extract messages * Remove unused shop query fields * Fix breaking bug when fetchMore is used in non-searchable SingleAutocompleteSelectFields * Migrate product types to tax classes * Add tax classes to shipping methods * Use encapsulated logic in product types * Fix product type stories * Fix shipping fixtures * Fix product type type mismatch * Fix shipping stories * Fix product type fixtures * Fix mismatching types * Extract messages * Update snapshots * Update snapshots * Fix comment * Drop deprecated graphql fields * Replace tax types with tax classes in product create view * Replace tax types with tax classes in product update view * Fix tests, stories, fixtures * Extract messages * Update snapshots * Move status messages to commonStatusMessages * Handle empty array case in tax class change handler * Reuse messages * Simple taxes bugfixes (#2395) * Fix tax channels menu - dense layout * Change view names to fit convention * Fix per country exceptions in tax channels view * Fix skeleton rendering on tax countries card title * Filter out existing countries from modal * Update snapshots * Fix deleting country configuration * Disallow negative values in tax inputs * Handle empty tax classes view * Allow empty options in shipping & product types views tax class assignment field * Modify undefined rates in tax classes view * Update macaw-ui * Fix UI on channels view * Fix UI on countries view * Fix UI on countries view * Align tax class rate label to the right * Updaste snapshots * Extract messages * Fix adding rates on new tax class * Fix key errors * Update schema * Build types * Allow empty rates in taxClassUpdate mutation * Extract tax channels change country function as a handler * Deprecate useStateFromProps * Change useStateFromProps to useStateUpdate * Fix dividers * Delete delete icon on new tax classes * Update snapshots * Update lockfile * Update macaw to 0.6.6 * Update snapshots * Specify type of input in country change handler * Extract autofocus logic to custom hook * Replace alternative with switch statement * Extract country exclusion logic from JSX * Update lockfile * Update lockfile * Trigger deployment * Fix invisible select markers * Fix linter issue * Fix crashing product details page * Fix e2e error * Update snapshots * Allow view taxes with any staff permissions (#2510) * Update after rebase Co-authored-by: Dawid <tarasiukdawid@gmail.com>
This commit is contained in:
parent
6e1230b10d
commit
5c1a62171d
131 changed files with 17441 additions and 7433 deletions
6027
introspection.json
6027
introspection.json
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,8 @@
|
|||
{
|
||||
"+2VydL": {
|
||||
"context": "card title",
|
||||
"string": "Country exceptions"
|
||||
},
|
||||
"+2VzH4": {
|
||||
"context": "avatar change button",
|
||||
"string": "Change"
|
||||
|
@ -31,14 +35,14 @@
|
|||
"context": "variant sku",
|
||||
"string": "SKU {sku}"
|
||||
},
|
||||
"+Jgot0": {
|
||||
"context": "tax class for a product type",
|
||||
"string": "Tax class"
|
||||
},
|
||||
"+NUzaQ": {
|
||||
"context": "check to mark this account as active",
|
||||
"string": "User account active"
|
||||
},
|
||||
"+OV+Gj": {
|
||||
"context": "button",
|
||||
"string": "Fetch taxes"
|
||||
},
|
||||
"+PbHKD": {
|
||||
"context": "dialog header",
|
||||
"string": "Capture Payment"
|
||||
|
@ -148,6 +152,10 @@
|
|||
"context": "product type shipping settings, section header",
|
||||
"string": "Shipping"
|
||||
},
|
||||
"/5r4he": {
|
||||
"context": "label for button",
|
||||
"string": "Add country"
|
||||
},
|
||||
"/68iG8": {
|
||||
"context": "product export to csv file, header",
|
||||
"string": "Information exported"
|
||||
|
@ -176,8 +184,9 @@
|
|||
"context": "section description",
|
||||
"string": "Strategy defines the preference of warehouses for stock allocations and reservations."
|
||||
},
|
||||
"/JENWS": {
|
||||
"string": "Reduced Tax Rates"
|
||||
"/ILyIf": {
|
||||
"context": "tax classes menu header",
|
||||
"string": "Tax class label"
|
||||
},
|
||||
"/KWNJW": {
|
||||
"context": "order discount was updated event title",
|
||||
|
@ -269,9 +278,6 @@
|
|||
"context": "order history message",
|
||||
"string": "Order cancel information was sent to customer"
|
||||
},
|
||||
"07KB2d": {
|
||||
"string": "Country Code"
|
||||
},
|
||||
"0AQH0Q": {
|
||||
"string": "Plugin is misconfigured and cannot be activated"
|
||||
},
|
||||
|
@ -286,9 +292,6 @@
|
|||
"context": "min price in channel",
|
||||
"string": "Min. value"
|
||||
},
|
||||
"0GJfWd": {
|
||||
"string": "Country Name"
|
||||
},
|
||||
"0MetrR": {
|
||||
"context": "webhook input help text",
|
||||
"string": "This URL will receive webhook POST requests"
|
||||
|
@ -309,6 +312,10 @@
|
|||
"context": "dialog header",
|
||||
"string": "Unassign Collection From Sale"
|
||||
},
|
||||
"0V1q0d": {
|
||||
"context": "add country dialog header",
|
||||
"string": "Choose country you want to add"
|
||||
},
|
||||
"0VDwAP": {
|
||||
"context": "checkbox label",
|
||||
"string": "Send shipment details to customer"
|
||||
|
@ -317,6 +324,10 @@
|
|||
"context": "menu item name",
|
||||
"string": "Name"
|
||||
},
|
||||
"0a0fLZ": {
|
||||
"context": "countries list menu label when no countries are assigned",
|
||||
"string": "There are no countries assigned"
|
||||
},
|
||||
"0dPP8O": {
|
||||
"string": "Order #{orderId} was placed"
|
||||
},
|
||||
|
@ -658,9 +669,6 @@
|
|||
"context": "no category set error",
|
||||
"string": "Product category not set"
|
||||
},
|
||||
"3BTtL2": {
|
||||
"string": "No countries found"
|
||||
},
|
||||
"3C3Nj5": {
|
||||
"context": "button",
|
||||
"string": "Add variant"
|
||||
|
@ -780,9 +788,6 @@
|
|||
"context": "order status",
|
||||
"string": "Returned"
|
||||
},
|
||||
"4EuJKs": {
|
||||
"string": "All products prices are entered with tax included"
|
||||
},
|
||||
"4JW9iJ": {
|
||||
"context": "home section name",
|
||||
"string": "Home"
|
||||
|
@ -837,6 +842,10 @@
|
|||
"context": "variant attribute checkbox",
|
||||
"string": "Variant Selection"
|
||||
},
|
||||
"4p3bjX": {
|
||||
"context": "tax strategy combobox choice",
|
||||
"string": "Use flat rates"
|
||||
},
|
||||
"4prRLv": {
|
||||
"string": "Permission is out of your scope"
|
||||
},
|
||||
|
@ -1034,6 +1043,10 @@
|
|||
"context": "WarehouseSettings local warehouse description",
|
||||
"string": "If selected customer will be able to choose this warehouse as pickup point. Ordered products will be only fulfilled from this warehouse stock"
|
||||
},
|
||||
"6HHPFy": {
|
||||
"context": "tax strategy combobox hint",
|
||||
"string": "Select the method of tax calculation"
|
||||
},
|
||||
"6JlXeD": {
|
||||
"context": "button",
|
||||
"string": "Create page type"
|
||||
|
@ -1118,6 +1131,10 @@
|
|||
"7+GBlj": {
|
||||
"string": "Error code {errorCode} {fieldError}"
|
||||
},
|
||||
"720c51": {
|
||||
"context": "tax classes name input placeholder",
|
||||
"string": "Tax rate name"
|
||||
},
|
||||
"73RU3R": {
|
||||
"context": "deactivate app",
|
||||
"string": "Are you sure you want to disable this app? Your data will be kept until you reactivate the app. You will be still billed for the app."
|
||||
|
@ -1164,6 +1181,10 @@
|
|||
"context": "navigator section header",
|
||||
"string": "Search in Catalog"
|
||||
},
|
||||
"7U/NPm": {
|
||||
"context": "tax class rates list label when no country is selected",
|
||||
"string": "Add country to access tax classes"
|
||||
},
|
||||
"7U8GRy": {
|
||||
"string": "Shipping method successfully updated"
|
||||
},
|
||||
|
@ -1226,6 +1247,10 @@
|
|||
"context": "navigator placeholder",
|
||||
"string": "Order Number"
|
||||
},
|
||||
"8BBMRj": {
|
||||
"context": "default tax class name for new tax classes",
|
||||
"string": "New tax class"
|
||||
},
|
||||
"8EGagh": {
|
||||
"context": "search box label",
|
||||
"string": "Filter Countries"
|
||||
|
@ -1312,13 +1337,14 @@
|
|||
"97l2MO": {
|
||||
"string": "Customer Email"
|
||||
},
|
||||
"98Nw4g": {
|
||||
"context": "card subtitle",
|
||||
"string": "Rendered prices"
|
||||
},
|
||||
"98WMlR": {
|
||||
"context": "header",
|
||||
"string": "Translation Product Variant \"{productName}\" - {languageCode}"
|
||||
},
|
||||
"98isC5": {
|
||||
"string": "Show gross prices to customers in the storefront"
|
||||
},
|
||||
"9C7PZE": {
|
||||
"context": "navigation section name",
|
||||
"string": "Navigation"
|
||||
|
@ -1427,9 +1453,6 @@
|
|||
"context": "collection label",
|
||||
"string": "Visible"
|
||||
},
|
||||
"9xUIAh": {
|
||||
"string": "Tax group"
|
||||
},
|
||||
"9xlPgt": {
|
||||
"context": "used staff users counter",
|
||||
"string": "{count}/{max} members"
|
||||
|
@ -1589,6 +1612,10 @@
|
|||
"context": "number of subcategories",
|
||||
"string": "Subcategories"
|
||||
},
|
||||
"BJtUQI": {
|
||||
"context": "button",
|
||||
"string": "Add"
|
||||
},
|
||||
"BL/Lbk": {
|
||||
"context": "install app permissions",
|
||||
"string": "Installing this app will give it following permissions:"
|
||||
|
@ -1721,6 +1748,10 @@
|
|||
"context": "dialog content",
|
||||
"string": "{counter,plural,one{Are you sure you want to delete this shipping zone?} other{Are you sure you want to delete {displayQuantity} shipping zones?}}"
|
||||
},
|
||||
"CFT171": {
|
||||
"context": "card header title",
|
||||
"string": "Country list"
|
||||
},
|
||||
"CG+awx": {
|
||||
"context": "dialog content",
|
||||
"string": "Which address would you like to use as shipping address for selected customer:"
|
||||
|
@ -1777,10 +1808,6 @@
|
|||
"context": "dialog header",
|
||||
"string": "Delete Page Types"
|
||||
},
|
||||
"CdIHMu": {
|
||||
"context": "select tax ratte",
|
||||
"string": "Tax Rate"
|
||||
},
|
||||
"ChAjJu": {
|
||||
"context": "character limit",
|
||||
"string": "{numberOfCharacters} of {maxCharacters} characters"
|
||||
|
@ -1834,6 +1861,10 @@
|
|||
"context": "checkbox label",
|
||||
"string": "Grant this app full access to the store"
|
||||
},
|
||||
"D5Wtf/": {
|
||||
"context": "table header column",
|
||||
"string": "Country name"
|
||||
},
|
||||
"D8nsBc": {
|
||||
"context": "no warehouses info",
|
||||
"string": "There are no warehouses set up for your store. To add stock quantity to the variant please <a>configure a warehouse</a>"
|
||||
|
@ -2004,6 +2035,10 @@
|
|||
"context": "delete app",
|
||||
"string": "Deleting {name}, you will remove installation of the app. If you are paying for app subscription, remember to unsubscribe from the app in Saleor Marketplace. Are you sure you want to delete the app?"
|
||||
},
|
||||
"EYkW1J": {
|
||||
"context": "checkbox label",
|
||||
"string": "Charge taxes for this channel"
|
||||
},
|
||||
"Eau5AV": {
|
||||
"string": "Manage Products Channel Availability"
|
||||
},
|
||||
|
@ -2097,9 +2132,6 @@
|
|||
"FNAZoh": {
|
||||
"string": "Last login"
|
||||
},
|
||||
"FNKhkx": {
|
||||
"string": "Charge taxes on shipping rates"
|
||||
},
|
||||
"FNT4b+": {
|
||||
"context": "tabel column header",
|
||||
"string": "Product"
|
||||
|
@ -2499,9 +2531,6 @@
|
|||
"context": "create gift card product alert message",
|
||||
"string": "Create a gift card product"
|
||||
},
|
||||
"HtQGEH": {
|
||||
"string": "Successfully fetched tax rates"
|
||||
},
|
||||
"HtfL5/": {
|
||||
"context": "button",
|
||||
"string": "Open App"
|
||||
|
@ -3242,6 +3271,14 @@
|
|||
"context": "section header",
|
||||
"string": "All Subcategories"
|
||||
},
|
||||
"Nj9iSB": {
|
||||
"context": "table header column",
|
||||
"string": "Tax rate"
|
||||
},
|
||||
"NlEVVT": {
|
||||
"context": "label for button",
|
||||
"string": "Create class"
|
||||
},
|
||||
"NlSJMM": {
|
||||
"context": "channels section name",
|
||||
"string": "Channels"
|
||||
|
@ -3604,10 +3641,6 @@
|
|||
"context": "table header name col label",
|
||||
"string": "Name"
|
||||
},
|
||||
"QHB48n": {
|
||||
"context": "header",
|
||||
"string": "Tax Rates in {countryName}"
|
||||
},
|
||||
"QJG+d/": {
|
||||
"context": "staff added type order discount",
|
||||
"string": "Staff added"
|
||||
|
@ -3694,6 +3727,10 @@
|
|||
"Qox+kb": {
|
||||
"string": "on field {fieldName}"
|
||||
},
|
||||
"QpBqa9": {
|
||||
"context": "label for radio button",
|
||||
"string": "Product prices are entered without tax"
|
||||
},
|
||||
"Qph0GE": {
|
||||
"context": "dialog content",
|
||||
"string": "Add a new address:"
|
||||
|
@ -3772,6 +3809,10 @@
|
|||
"context": "issue card button label",
|
||||
"string": "Issue card"
|
||||
},
|
||||
"Rfk+8B": {
|
||||
"context": "tax classes menu label when there are no tax classes",
|
||||
"string": "There are no tax classes"
|
||||
},
|
||||
"Rj8LxK": {
|
||||
"string": "Add search engine title and description to make this collection easier to find"
|
||||
},
|
||||
|
@ -3793,6 +3834,10 @@
|
|||
"context": "dialog header",
|
||||
"string": "Unassign Attribute from Page Type"
|
||||
},
|
||||
"RqtZQ6": {
|
||||
"context": "tax classes card header",
|
||||
"string": "Tax class rates"
|
||||
},
|
||||
"RrCui3": {
|
||||
"string": "Summary"
|
||||
},
|
||||
|
@ -3975,6 +4020,10 @@
|
|||
"context": "product availability available date",
|
||||
"string": "Set available on"
|
||||
},
|
||||
"TJ7WHA": {
|
||||
"context": "tax strategy combobox choice",
|
||||
"string": "Use tax app"
|
||||
},
|
||||
"TKmub+": {
|
||||
"string": "This field is required"
|
||||
},
|
||||
|
@ -4009,13 +4058,9 @@
|
|||
"context": "orders section name",
|
||||
"string": "Orders"
|
||||
},
|
||||
"TalJlD": {
|
||||
"context": "tax rate for a product type",
|
||||
"string": "Tax"
|
||||
},
|
||||
"TfY/Pi": {
|
||||
"context": "checkbox",
|
||||
"string": "Charge taxes on this product"
|
||||
"TfzIXS": {
|
||||
"context": "tax classes card header",
|
||||
"string": "General information"
|
||||
},
|
||||
"TjGYna": {
|
||||
"context": "product inventory, checkbox",
|
||||
|
@ -4075,6 +4120,10 @@
|
|||
"context": "no gift card products and types alert message",
|
||||
"string": "{createGiftCardProductType} and {giftCardProduct} to start selling gift cards in your store."
|
||||
},
|
||||
"UBuKZ9": {
|
||||
"context": "searchbar placeholder",
|
||||
"string": "Country"
|
||||
},
|
||||
"UCHtG6": {
|
||||
"context": "button",
|
||||
"string": "About"
|
||||
|
@ -4127,9 +4176,6 @@
|
|||
"UaYJJ8": {
|
||||
"string": "Are you sure you want to delete {name} search tab?"
|
||||
},
|
||||
"Ubath+": {
|
||||
"string": "No reduced tax categories found"
|
||||
},
|
||||
"Uf3oHA": {
|
||||
"context": "add new menu item",
|
||||
"string": "Create new item"
|
||||
|
@ -4398,6 +4444,10 @@
|
|||
"context": "order refund amount",
|
||||
"string": "Shipment Cost"
|
||||
},
|
||||
"WIxSDm": {
|
||||
"context": "card header",
|
||||
"string": "{country} class rates"
|
||||
},
|
||||
"WMGoqz": {
|
||||
"context": "used by filter label",
|
||||
"string": "Used by"
|
||||
|
@ -4469,6 +4519,10 @@
|
|||
"context": "title",
|
||||
"string": "There’s a problem with app."
|
||||
},
|
||||
"Ww69SE": {
|
||||
"context": "search input placeholder",
|
||||
"string": "Search tax classes"
|
||||
},
|
||||
"WwNtFn": {
|
||||
"context": "delete product variant",
|
||||
"string": "Are you sure you want to delete {name}?"
|
||||
|
@ -4794,6 +4848,10 @@
|
|||
"context": "stock exceeded dialog description",
|
||||
"string": "Stock for items shown below are not enough to prepare fulfillment:"
|
||||
},
|
||||
"ZAaXfz": {
|
||||
"context": "Taxes section title",
|
||||
"string": "Countries"
|
||||
},
|
||||
"ZDJEat": {
|
||||
"context": "button",
|
||||
"string": "Load More"
|
||||
|
@ -4932,6 +4990,10 @@
|
|||
"aI80kg": {
|
||||
"string": "Properties"
|
||||
},
|
||||
"aJm/by": {
|
||||
"context": "Taxes section title",
|
||||
"string": "Tax classes"
|
||||
},
|
||||
"aMwxYb": {
|
||||
"string": "Countries"
|
||||
},
|
||||
|
@ -5018,6 +5080,10 @@
|
|||
"b1zuN9": {
|
||||
"string": "Price"
|
||||
},
|
||||
"b2DlTO": {
|
||||
"context": "Taxes section title",
|
||||
"string": "Channels"
|
||||
},
|
||||
"b6L9n7": {
|
||||
"context": "voucher is active until date",
|
||||
"string": "Ends"
|
||||
|
@ -5026,6 +5092,10 @@
|
|||
"context": "product price",
|
||||
"string": "Price"
|
||||
},
|
||||
"bDBiac": {
|
||||
"context": "dropdown or column label",
|
||||
"string": "Tax class"
|
||||
},
|
||||
"bDHiYK": {
|
||||
"context": "gift card export success alert description",
|
||||
"string": "We are currently exporting your gift card codes. As soon as your file is available it will be sent to your email address"
|
||||
|
@ -5190,6 +5260,10 @@
|
|||
"context": "button",
|
||||
"string": "Unassign and save"
|
||||
},
|
||||
"cVjewM": {
|
||||
"context": "label for radio button",
|
||||
"string": "Product prices are entered with tax"
|
||||
},
|
||||
"cW1RIo": {
|
||||
"context": "section header",
|
||||
"string": "Media View"
|
||||
|
@ -5802,10 +5876,6 @@
|
|||
"context": "dialog content",
|
||||
"string": "{counter,plural,one{Are you sure you want to unassign this variant?} other{Are you sure you want to unassign {displayQuantity} variants?}}"
|
||||
},
|
||||
"iYH3Y7": {
|
||||
"context": "checkbox",
|
||||
"string": "Override the product type's tax rate"
|
||||
},
|
||||
"ibnmEd": {
|
||||
"context": "voucher country range",
|
||||
"string": "Countries"
|
||||
|
@ -5901,6 +5971,10 @@
|
|||
"jHJmjf": {
|
||||
"string": "No results"
|
||||
},
|
||||
"jMzyU8": {
|
||||
"context": "tax classes card header",
|
||||
"string": "Tax classes"
|
||||
},
|
||||
"jNSOSu": {
|
||||
"context": "cancelled fulfillment, section header",
|
||||
"string": "Refunded and Returned ({quantity})"
|
||||
|
@ -6065,6 +6139,10 @@
|
|||
"context": "product updated at",
|
||||
"string": "Last updated"
|
||||
},
|
||||
"kXqn6A": {
|
||||
"context": "table header column",
|
||||
"string": "Charge taxes"
|
||||
},
|
||||
"kYYbrv": {
|
||||
"context": "channel publication date",
|
||||
"string": "Published since {date}"
|
||||
|
@ -6182,9 +6260,6 @@
|
|||
"context": "money",
|
||||
"string": "from {money}"
|
||||
},
|
||||
"la9cZ4": {
|
||||
"string": "Tax Rate"
|
||||
},
|
||||
"labkPK": {
|
||||
"context": "search gift card placeholder",
|
||||
"string": "Search Gift Cards, e.g {exampleGiftCardCode}"
|
||||
|
@ -6215,9 +6290,9 @@
|
|||
"context": "password reset, button",
|
||||
"string": "Send an email with reset link"
|
||||
},
|
||||
"lnQAos": {
|
||||
"context": "header",
|
||||
"string": "Taxes"
|
||||
"lnteBJ": {
|
||||
"context": "country rates list label for the default rate",
|
||||
"string": "Country default rate"
|
||||
},
|
||||
"lqIzC8": {
|
||||
"string": "This field needs to be unique"
|
||||
|
@ -6288,10 +6363,6 @@
|
|||
"context": "NoChannels content",
|
||||
"string": "No channels to assign. Please first assign them for the product."
|
||||
},
|
||||
"mUb8Gt": {
|
||||
"context": "section header",
|
||||
"string": "Taxes"
|
||||
},
|
||||
"mWQt3s": {
|
||||
"string": "No. of Products"
|
||||
},
|
||||
|
@ -6439,6 +6510,10 @@
|
|||
"context": "attribute internal name",
|
||||
"string": "Slug"
|
||||
},
|
||||
"ngAgBy": {
|
||||
"context": "tax class rates list label when no countries are assigned",
|
||||
"string": "There are no countries using this tax class yet, use {tab} tab to assign tax rates."
|
||||
},
|
||||
"njBulj": {
|
||||
"context": "check to require attribute to have value",
|
||||
"string": "Value Required"
|
||||
|
@ -6634,6 +6709,10 @@
|
|||
"pVFoOk": {
|
||||
"string": "Are you sure you want to delete {collectionName}?"
|
||||
},
|
||||
"pWClYm": {
|
||||
"context": "card title",
|
||||
"string": "Default settings"
|
||||
},
|
||||
"paa4m0": {
|
||||
"string": "Successfully created product type"
|
||||
},
|
||||
|
@ -6673,6 +6752,10 @@
|
|||
"context": "notes about customer, header",
|
||||
"string": "Notes"
|
||||
},
|
||||
"puRlnN": {
|
||||
"context": "card subtitle",
|
||||
"string": "Entered prices"
|
||||
},
|
||||
"puikeb": {
|
||||
"context": "button",
|
||||
"string": "Delete Address"
|
||||
|
@ -6746,6 +6829,10 @@
|
|||
"context": "stock exceeded dialog title",
|
||||
"string": "Not enough stock"
|
||||
},
|
||||
"qbcNjQ": {
|
||||
"context": "table header column",
|
||||
"string": "Tax name"
|
||||
},
|
||||
"qbmeUI": {
|
||||
"context": "dialog header",
|
||||
"string": "Delete Order Drafts"
|
||||
|
@ -7042,6 +7129,10 @@
|
|||
"context": "button",
|
||||
"string": "Accept"
|
||||
},
|
||||
"skklRz": {
|
||||
"context": "table header column",
|
||||
"string": "Show gross prices in storefront"
|
||||
},
|
||||
"sn2awN": {
|
||||
"context": "ExitFormPrompt cancel button",
|
||||
"string": "Discard changes"
|
||||
|
@ -7163,6 +7254,9 @@
|
|||
"context": "window title",
|
||||
"string": "Create collection"
|
||||
},
|
||||
"tthToS": {
|
||||
"string": "Disabled"
|
||||
},
|
||||
"ttk0w7": {
|
||||
"context": "resend code to customer description",
|
||||
"string": "Gift Card Code will be resent to email provided during checkout. You can provide a different email address if you want to:"
|
||||
|
@ -7196,6 +7290,10 @@
|
|||
"u24Ppd": {
|
||||
"string": "This attribute cannot be assigned to this product type"
|
||||
},
|
||||
"u34css": {
|
||||
"context": "label for empty list in channels list",
|
||||
"string": "There are no exceptions for this channel"
|
||||
},
|
||||
"u3sYPH": {
|
||||
"context": "independent of any particular day, eg. 11:35",
|
||||
"string": "Time"
|
||||
|
@ -7734,6 +7832,10 @@
|
|||
"context": "attribute can be searched in dashboard",
|
||||
"string": "Searchable"
|
||||
},
|
||||
"yLfbSh": {
|
||||
"context": "support label",
|
||||
"string": "Channel name"
|
||||
},
|
||||
"yMi8I8": {
|
||||
"context": "dialog header",
|
||||
"string": "Dectivate App"
|
||||
|
|
1292
schema.graphql
1292
schema.graphql
File diff suppressed because it is too large
Load diff
|
@ -14,6 +14,7 @@ export interface FormProps<TData, TErrors>
|
|||
onSubmit?: (data: TData) => SubmitPromise<TErrors[]> | void;
|
||||
formId?: FormId;
|
||||
checkIfSaveIsDisabled?: CheckIfSaveIsDisabledFnType<TData>;
|
||||
mergeData?: boolean;
|
||||
}
|
||||
|
||||
function Form<TData, Terrors>({
|
||||
|
@ -25,6 +26,7 @@ function Form<TData, Terrors>({
|
|||
formId,
|
||||
checkIfSaveIsDisabled,
|
||||
disabled,
|
||||
mergeData,
|
||||
...rest
|
||||
}: FormProps<TData, Terrors>) {
|
||||
const renderProps = useForm(initial, onSubmit, {
|
||||
|
@ -32,6 +34,7 @@ function Form<TData, Terrors>({
|
|||
formId,
|
||||
checkIfSaveIsDisabled,
|
||||
disabled,
|
||||
mergeData,
|
||||
});
|
||||
|
||||
function handleSubmit(event?: React.FormEvent<any>, cb?: () => void) {
|
||||
|
|
45
src/components/ListItemLink/ListItemLink.tsx
Normal file
45
src/components/ListItemLink/ListItemLink.tsx
Normal file
|
@ -0,0 +1,45 @@
|
|||
import { BaseListItemProps, ListItem, makeStyles } from "@saleor/macaw-ui";
|
||||
import clsx from "classnames";
|
||||
import React from "react";
|
||||
|
||||
import Link from "../Link";
|
||||
|
||||
export interface ListItemLinkProps
|
||||
extends Omit<BaseListItemProps, "onClick" | "classes"> {
|
||||
href?: string;
|
||||
className?: string;
|
||||
linkClassName?: string;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(
|
||||
{
|
||||
link: {
|
||||
all: "inherit",
|
||||
display: "contents",
|
||||
},
|
||||
},
|
||||
{ name: "ListItemLink" },
|
||||
);
|
||||
|
||||
export const ListItemLink: React.FC<ListItemLinkProps> = ({
|
||||
href,
|
||||
children,
|
||||
linkClassName,
|
||||
...props
|
||||
}) => {
|
||||
const classes = useStyles();
|
||||
|
||||
if (!href) {
|
||||
return <ListItem {...props}>{children}</ListItem>;
|
||||
}
|
||||
|
||||
return (
|
||||
<ListItem {...props}>
|
||||
<Link className={clsx(classes.link, linkClassName)} href={href}>
|
||||
{children}
|
||||
</Link>
|
||||
</ListItem>
|
||||
);
|
||||
};
|
||||
|
||||
export default ListItemLink;
|
2
src/components/ListItemLink/index.ts
Normal file
2
src/components/ListItemLink/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./ListItemLink";
|
||||
export * from "./ListItemLink";
|
|
@ -16,7 +16,7 @@ import { productTypeListUrl } from "@saleor/productTypes/urls";
|
|||
import { shippingZonesListUrl } from "@saleor/shipping/urls";
|
||||
import { siteSettingsUrl } from "@saleor/siteSettings/urls";
|
||||
import { staffListUrl } from "@saleor/staff/urls";
|
||||
import { countryListUrl } from "@saleor/taxes/urls";
|
||||
import { taxConfigurationListUrl } from "@saleor/taxes/urls";
|
||||
import { languageListUrl } from "@saleor/translations/urls";
|
||||
import { warehouseListUrl } from "@saleor/warehouses/urls";
|
||||
import { score } from "fuzzaldrin";
|
||||
|
@ -108,7 +108,7 @@ function searchInViews(
|
|||
},
|
||||
{
|
||||
label: intl.formatMessage(sectionNames.taxes),
|
||||
url: countryListUrl,
|
||||
url: taxConfigurationListUrl(),
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage(sectionNames.translations),
|
||||
|
|
|
@ -16,7 +16,6 @@ export const shopInfo = gql`
|
|||
...CountryWithCode
|
||||
}
|
||||
defaultWeightUnit
|
||||
displayGrossPrices
|
||||
domain {
|
||||
host
|
||||
url
|
||||
|
@ -24,7 +23,6 @@ export const shopInfo = gql`
|
|||
languages {
|
||||
...Language
|
||||
}
|
||||
includeTaxesInPrices
|
||||
name
|
||||
trackInventoryByDefault
|
||||
permissions {
|
||||
|
|
|
@ -167,7 +167,7 @@ const SingleAutocompleteSelectFieldContent: React.FC<SingleAutocompleteSelectFie
|
|||
const scrolledToBottom = isScrolledToBottom(anchor, scrollPosition, offset);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!calledForMore && onFetchMore && scrolledToBottom) {
|
||||
if (!calledForMore && onFetchMore && scrolledToBottom && hasMore) {
|
||||
onFetchMore();
|
||||
setCalledForMore(true);
|
||||
} else if (scrolledToBottom && !onFetchMore) {
|
||||
|
|
|
@ -1,32 +1,18 @@
|
|||
import { Typography } from "@material-ui/core";
|
||||
import { IconProps } from "@material-ui/core/Icon";
|
||||
import { useTheme } from "@material-ui/core/styles";
|
||||
import useMediaQuery from "@material-ui/core/useMediaQuery";
|
||||
import { PermissionEnum, UserFragment } from "@saleor/graphql";
|
||||
import { UserFragment } from "@saleor/graphql";
|
||||
import { sectionNames } from "@saleor/intl";
|
||||
import { makeStyles, NavigationCard } from "@saleor/macaw-ui";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import { hasAnyPermissions } from "../auth/misc";
|
||||
import Container from "../components/Container";
|
||||
import PageHeader from "../components/PageHeader";
|
||||
import VersionInfo from "../components/VersionInfo";
|
||||
|
||||
export interface MenuItem {
|
||||
description: string;
|
||||
icon: React.ReactElement<IconProps>;
|
||||
permissions: PermissionEnum[];
|
||||
title: string;
|
||||
url?: string;
|
||||
testId?: string;
|
||||
}
|
||||
|
||||
export interface MenuSection {
|
||||
label: string;
|
||||
menuItems: MenuItem[];
|
||||
}
|
||||
import { MenuSection } from "./types";
|
||||
import { hasUserMenuItemPermissions } from "./utils";
|
||||
|
||||
interface VersionInfo {
|
||||
dashboardVersion: string;
|
||||
|
@ -115,7 +101,7 @@ export const ConfigurationPage: React.FC<ConfigurationPageProps> = props => {
|
|||
{menus
|
||||
.filter(menu =>
|
||||
menu.menuItems.some(menuItem =>
|
||||
hasAnyPermissions(menuItem.permissions, user),
|
||||
hasUserMenuItemPermissions(menuItem, user),
|
||||
),
|
||||
)
|
||||
.map((menu, menuIndex) => (
|
||||
|
@ -125,9 +111,7 @@ export const ConfigurationPage: React.FC<ConfigurationPageProps> = props => {
|
|||
</div>
|
||||
<div className={classes.configurationItem}>
|
||||
{menu.menuItems
|
||||
.filter(menuItem =>
|
||||
hasAnyPermissions(menuItem.permissions, user),
|
||||
)
|
||||
.filter(menuItem => hasUserMenuItemPermissions(menuItem, user))
|
||||
.map((item, itemIndex) => (
|
||||
<Link className={classes.link} to={item.url}>
|
||||
<NavigationCard
|
||||
|
|
|
@ -27,12 +27,13 @@ import { productTypeListUrl } from "@saleor/productTypes/urls";
|
|||
import { shippingZonesListUrl } from "@saleor/shipping/urls";
|
||||
import { siteSettingsUrl } from "@saleor/siteSettings/urls";
|
||||
import { staffListUrl } from "@saleor/staff/urls";
|
||||
import { taxSection } from "@saleor/taxes/urls";
|
||||
import { taxConfigurationListUrl } from "@saleor/taxes/urls";
|
||||
import { warehouseSection } from "@saleor/warehouses/urls";
|
||||
import React from "react";
|
||||
import { IntlShape, useIntl } from "react-intl";
|
||||
|
||||
import ConfigurationPage, { MenuSection } from "./ConfigurationPage";
|
||||
import ConfigurationPage from "./ConfigurationPage";
|
||||
import { MenuSection } from "./types";
|
||||
|
||||
export function createConfigurationMenu(intl: IntlShape): MenuSection[] {
|
||||
return [
|
||||
|
@ -81,9 +82,8 @@ export function createConfigurationMenu(intl: IntlShape): MenuSection[] {
|
|||
defaultMessage: "Manage how your store charges tax",
|
||||
}),
|
||||
icon: <Taxes />,
|
||||
permissions: [PermissionEnum.MANAGE_SETTINGS],
|
||||
title: intl.formatMessage(sectionNames.taxes),
|
||||
url: taxSection,
|
||||
url: taxConfigurationListUrl(),
|
||||
testId: "configuration-menu-taxes",
|
||||
},
|
||||
],
|
||||
|
|
16
src/configuration/types.ts
Normal file
16
src/configuration/types.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { IconProps } from "@material-ui/core";
|
||||
import { PermissionEnum } from "@saleor/graphql";
|
||||
|
||||
export interface MenuItem {
|
||||
description: string;
|
||||
icon: React.ReactElement<IconProps>;
|
||||
permissions?: PermissionEnum[];
|
||||
title: string;
|
||||
url?: string;
|
||||
testId?: string;
|
||||
}
|
||||
|
||||
export interface MenuSection {
|
||||
label: string;
|
||||
menuItems: MenuItem[];
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
import { PermissionEnum } from "@saleor/graphql";
|
||||
import { hasAnyPermissions } from "@saleor/auth/misc";
|
||||
import { PermissionEnum, UserFragment } from "@saleor/graphql";
|
||||
import { IntlShape } from "react-intl";
|
||||
|
||||
import { createConfigurationMenu } from ".";
|
||||
import { MenuItem } from "./types";
|
||||
|
||||
export const getConfigMenuItemsPermissions = (
|
||||
intl: IntlShape,
|
||||
|
@ -15,3 +17,9 @@ export const getConfigMenuItemsPermissions = (
|
|||
[],
|
||||
)
|
||||
.flat();
|
||||
|
||||
export const hasUserMenuItemPermissions = (
|
||||
menuItem: MenuItem,
|
||||
user: UserFragment,
|
||||
): boolean =>
|
||||
menuItem.permissions ? hasAnyPermissions(menuItem.permissions, user) : true;
|
||||
|
|
|
@ -487,3 +487,42 @@ export const shippingPriceTranslateErrorFragment = gql`
|
|||
message
|
||||
}
|
||||
`;
|
||||
|
||||
export const taxConfigurationUpdateError = gql`
|
||||
fragment TaxConfigurationUpdateErrorFragment on TaxConfigurationUpdateError {
|
||||
field
|
||||
code
|
||||
}
|
||||
`;
|
||||
|
||||
export const taxCountryConfigurationUpdateError = gql`
|
||||
fragment TaxCountryConfigurationUpdateErrorFragment on TaxCountryConfigurationUpdateError {
|
||||
field
|
||||
code
|
||||
}
|
||||
`;
|
||||
export const taxCountryConfigurationDeleteError = gql`
|
||||
fragment TaxCountryConfigurationDeleteErrorFragment on TaxCountryConfigurationDeleteError {
|
||||
field
|
||||
code
|
||||
}
|
||||
`;
|
||||
|
||||
export const taxClassUpdateError = gql`
|
||||
fragment TaxClassUpdateErrorFragment on TaxClassUpdateError {
|
||||
field
|
||||
code
|
||||
}
|
||||
`;
|
||||
export const taxClassCreateError = gql`
|
||||
fragment TaxClassCreateErrorFragment on TaxClassCreateError {
|
||||
field
|
||||
code
|
||||
}
|
||||
`;
|
||||
export const taxClassDeleteError = gql`
|
||||
fragment TaxClassDeleteErrorFragment on TaxClassDeleteError {
|
||||
field
|
||||
code
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -7,9 +7,9 @@ export const productTypeFragment = gql`
|
|||
kind
|
||||
hasVariants
|
||||
isShippingRequired
|
||||
taxType {
|
||||
description
|
||||
taxCode
|
||||
taxClass {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -226,7 +226,6 @@ export const productFragmentDetails = gql`
|
|||
id
|
||||
name
|
||||
}
|
||||
chargeTaxes
|
||||
channelListings {
|
||||
...ChannelListingProductWithoutPricing
|
||||
}
|
||||
|
@ -241,15 +240,13 @@ export const productFragmentDetails = gql`
|
|||
id
|
||||
name
|
||||
hasVariants
|
||||
taxType {
|
||||
...TaxType
|
||||
}
|
||||
}
|
||||
weight {
|
||||
...Weight
|
||||
}
|
||||
taxType {
|
||||
...TaxType
|
||||
taxClass {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -28,6 +28,10 @@ export const shippingMethodTypeFragment = gql`
|
|||
fragment ShippingMethodType on ShippingMethodType {
|
||||
...ShippingMethodWithPostalCodes
|
||||
...Metadata
|
||||
taxClass {
|
||||
name
|
||||
id
|
||||
}
|
||||
minimumOrderWeight {
|
||||
unit
|
||||
value
|
||||
|
|
|
@ -16,28 +16,65 @@ export const countryFragment = gql`
|
|||
code
|
||||
}
|
||||
`;
|
||||
export const countryWithTaxesFragment = gql`
|
||||
fragment CountryWithTaxes on CountryDisplay {
|
||||
...Country
|
||||
vat {
|
||||
standardRate
|
||||
reducedRates {
|
||||
rateType
|
||||
rate
|
||||
|
||||
export const taxConfigurationPerCountry = gql`
|
||||
fragment TaxConfigurationPerCountry on TaxConfigurationPerCountry {
|
||||
country {
|
||||
...CountryWithCode
|
||||
}
|
||||
chargeTaxes
|
||||
taxCalculationStrategy
|
||||
displayGrossPrices
|
||||
}
|
||||
`;
|
||||
|
||||
export const taxConfiguration = gql`
|
||||
fragment TaxConfiguration on TaxConfiguration {
|
||||
id
|
||||
channel {
|
||||
id
|
||||
name
|
||||
}
|
||||
displayGrossPrices
|
||||
pricesEnteredWithTax
|
||||
chargeTaxes
|
||||
taxCalculationStrategy
|
||||
countries {
|
||||
...TaxConfigurationPerCountry
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const taxCountryConfigurationFragment = gql`
|
||||
fragment TaxCountryConfiguration on TaxCountryConfiguration {
|
||||
country {
|
||||
...CountryWithCode
|
||||
}
|
||||
taxClassCountryRates {
|
||||
rate
|
||||
taxClass {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const shopTaxesFragment = gql`
|
||||
fragment ShopTaxes on Shop {
|
||||
chargeTaxesOnShipping
|
||||
includeTaxesInPrices
|
||||
displayGrossPrices
|
||||
|
||||
export const taxRateFragment = gql`
|
||||
fragment TaxRate on TaxClassCountryRate {
|
||||
country {
|
||||
...CountryWithCode
|
||||
}
|
||||
rate
|
||||
}
|
||||
`;
|
||||
export const taxTypeFragment = gql`
|
||||
fragment TaxType on TaxType {
|
||||
description
|
||||
taxCode
|
||||
|
||||
export const taxClassFragment = gql`
|
||||
fragment TaxClass on TaxClass {
|
||||
id
|
||||
name
|
||||
countries {
|
||||
...TaxRate
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -210,6 +210,8 @@
|
|||
"ShopTranslation",
|
||||
"StaffNotificationRecipient",
|
||||
"Stock",
|
||||
"TaxClass",
|
||||
"TaxConfiguration",
|
||||
"Transaction",
|
||||
"TransactionEvent",
|
||||
"TransactionItem",
|
||||
|
@ -246,6 +248,8 @@
|
|||
"ShippingMethod",
|
||||
"ShippingMethodType",
|
||||
"ShippingZone",
|
||||
"TaxClass",
|
||||
"TaxConfiguration",
|
||||
"TransactionItem",
|
||||
"User",
|
||||
"Voucher",
|
||||
|
|
|
@ -972,6 +972,42 @@ export const ShippingPriceTranslateErrorFragmentFragmentDoc = gql`
|
|||
message
|
||||
}
|
||||
`;
|
||||
export const TaxConfigurationUpdateErrorFragmentFragmentDoc = gql`
|
||||
fragment TaxConfigurationUpdateErrorFragment on TaxConfigurationUpdateError {
|
||||
field
|
||||
code
|
||||
}
|
||||
`;
|
||||
export const TaxCountryConfigurationUpdateErrorFragmentFragmentDoc = gql`
|
||||
fragment TaxCountryConfigurationUpdateErrorFragment on TaxCountryConfigurationUpdateError {
|
||||
field
|
||||
code
|
||||
}
|
||||
`;
|
||||
export const TaxCountryConfigurationDeleteErrorFragmentFragmentDoc = gql`
|
||||
fragment TaxCountryConfigurationDeleteErrorFragment on TaxCountryConfigurationDeleteError {
|
||||
field
|
||||
code
|
||||
}
|
||||
`;
|
||||
export const TaxClassUpdateErrorFragmentFragmentDoc = gql`
|
||||
fragment TaxClassUpdateErrorFragment on TaxClassUpdateError {
|
||||
field
|
||||
code
|
||||
}
|
||||
`;
|
||||
export const TaxClassCreateErrorFragmentFragmentDoc = gql`
|
||||
fragment TaxClassCreateErrorFragment on TaxClassCreateError {
|
||||
field
|
||||
code
|
||||
}
|
||||
`;
|
||||
export const TaxClassDeleteErrorFragmentFragmentDoc = gql`
|
||||
fragment TaxClassDeleteErrorFragment on TaxClassDeleteError {
|
||||
field
|
||||
code
|
||||
}
|
||||
`;
|
||||
export const GiftCardsSettingsFragmentDoc = gql`
|
||||
fragment GiftCardsSettings on GiftCardSettings {
|
||||
expiryType
|
||||
|
@ -1796,9 +1832,9 @@ export const ProductTypeFragmentDoc = gql`
|
|||
kind
|
||||
hasVariants
|
||||
isShippingRequired
|
||||
taxType {
|
||||
description
|
||||
taxCode
|
||||
taxClass {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@ -1996,12 +2032,6 @@ export const ProductDetailsVariantFragmentDoc = gql`
|
|||
${StockFragmentDoc}
|
||||
${PreorderFragmentDoc}
|
||||
${ChannelListingProductVariantFragmentDoc}`;
|
||||
export const TaxTypeFragmentDoc = gql`
|
||||
fragment TaxType on TaxType {
|
||||
description
|
||||
taxCode
|
||||
}
|
||||
`;
|
||||
export const WeightFragmentDoc = gql`
|
||||
fragment Weight on Weight {
|
||||
unit
|
||||
|
@ -2029,7 +2059,6 @@ export const ProductFragmentDoc = gql`
|
|||
id
|
||||
name
|
||||
}
|
||||
chargeTaxes
|
||||
channelListings {
|
||||
...ChannelListingProductWithoutPricing
|
||||
}
|
||||
|
@ -2044,15 +2073,13 @@ export const ProductFragmentDoc = gql`
|
|||
id
|
||||
name
|
||||
hasVariants
|
||||
taxType {
|
||||
...TaxType
|
||||
}
|
||||
}
|
||||
weight {
|
||||
...Weight
|
||||
}
|
||||
taxType {
|
||||
...TaxType
|
||||
taxClass {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
${ProductVariantAttributesFragmentDoc}
|
||||
|
@ -2060,7 +2087,6 @@ ${MetadataFragmentDoc}
|
|||
${ChannelListingProductWithoutPricingFragmentDoc}
|
||||
${ProductMediaFragmentDoc}
|
||||
${ProductDetailsVariantFragmentDoc}
|
||||
${TaxTypeFragmentDoc}
|
||||
${WeightFragmentDoc}`;
|
||||
export const VariantAttributeFragmentDoc = gql`
|
||||
fragment VariantAttribute on Attribute {
|
||||
|
@ -2201,6 +2227,10 @@ export const ShippingMethodTypeFragmentDoc = gql`
|
|||
fragment ShippingMethodType on ShippingMethodType {
|
||||
...ShippingMethodWithPostalCodes
|
||||
...Metadata
|
||||
taxClass {
|
||||
name
|
||||
id
|
||||
}
|
||||
minimumOrderWeight {
|
||||
unit
|
||||
value
|
||||
|
@ -2282,12 +2312,6 @@ export const ShippingZoneDetailsFragmentDoc = gql`
|
|||
}
|
||||
${ShippingZoneFragmentDoc}
|
||||
${ShippingMethodTypeFragmentDoc}`;
|
||||
export const CountryWithCodeFragmentDoc = gql`
|
||||
fragment CountryWithCode on CountryDisplay {
|
||||
country
|
||||
code
|
||||
}
|
||||
`;
|
||||
export const LanguageFragmentDoc = gql`
|
||||
fragment Language on LanguageDisplay {
|
||||
code
|
||||
|
@ -2370,25 +2394,69 @@ export const CountryFragmentDoc = gql`
|
|||
code
|
||||
}
|
||||
`;
|
||||
export const CountryWithTaxesFragmentDoc = gql`
|
||||
fragment CountryWithTaxes on CountryDisplay {
|
||||
...Country
|
||||
vat {
|
||||
standardRate
|
||||
reducedRates {
|
||||
rateType
|
||||
rate
|
||||
export const CountryWithCodeFragmentDoc = gql`
|
||||
fragment CountryWithCode on CountryDisplay {
|
||||
country
|
||||
code
|
||||
}
|
||||
`;
|
||||
export const TaxConfigurationPerCountryFragmentDoc = gql`
|
||||
fragment TaxConfigurationPerCountry on TaxConfigurationPerCountry {
|
||||
country {
|
||||
...CountryWithCode
|
||||
}
|
||||
chargeTaxes
|
||||
taxCalculationStrategy
|
||||
displayGrossPrices
|
||||
}
|
||||
${CountryWithCodeFragmentDoc}`;
|
||||
export const TaxConfigurationFragmentDoc = gql`
|
||||
fragment TaxConfiguration on TaxConfiguration {
|
||||
id
|
||||
channel {
|
||||
id
|
||||
name
|
||||
}
|
||||
displayGrossPrices
|
||||
pricesEnteredWithTax
|
||||
chargeTaxes
|
||||
taxCalculationStrategy
|
||||
countries {
|
||||
...TaxConfigurationPerCountry
|
||||
}
|
||||
}
|
||||
${TaxConfigurationPerCountryFragmentDoc}`;
|
||||
export const TaxCountryConfigurationFragmentDoc = gql`
|
||||
fragment TaxCountryConfiguration on TaxCountryConfiguration {
|
||||
country {
|
||||
...CountryWithCode
|
||||
}
|
||||
taxClassCountryRates {
|
||||
rate
|
||||
taxClass {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
${CountryFragmentDoc}`;
|
||||
export const ShopTaxesFragmentDoc = gql`
|
||||
fragment ShopTaxes on Shop {
|
||||
chargeTaxesOnShipping
|
||||
includeTaxesInPrices
|
||||
displayGrossPrices
|
||||
${CountryWithCodeFragmentDoc}`;
|
||||
export const TaxRateFragmentDoc = gql`
|
||||
fragment TaxRate on TaxClassCountryRate {
|
||||
country {
|
||||
...CountryWithCode
|
||||
}
|
||||
rate
|
||||
}
|
||||
`;
|
||||
${CountryWithCodeFragmentDoc}`;
|
||||
export const TaxClassFragmentDoc = gql`
|
||||
fragment TaxClass on TaxClass {
|
||||
id
|
||||
name
|
||||
countries {
|
||||
...TaxRate
|
||||
}
|
||||
}
|
||||
${TaxRateFragmentDoc}`;
|
||||
export const TimePeriodFragmentDoc = gql`
|
||||
fragment TimePeriod on TimePeriod {
|
||||
amount
|
||||
|
@ -5091,7 +5159,6 @@ export const ShopInfoDocument = gql`
|
|||
...CountryWithCode
|
||||
}
|
||||
defaultWeightUnit
|
||||
displayGrossPrices
|
||||
domain {
|
||||
host
|
||||
url
|
||||
|
@ -5099,7 +5166,6 @@ export const ShopInfoDocument = gql`
|
|||
languages {
|
||||
...Language
|
||||
}
|
||||
includeTaxesInPrices
|
||||
name
|
||||
trackInventoryByDefault
|
||||
permissions {
|
||||
|
@ -11032,10 +11098,6 @@ export const ProductTypeDetailsDocument = gql`
|
|||
shop {
|
||||
defaultWeightUnit
|
||||
}
|
||||
taxTypes {
|
||||
taxCode
|
||||
description
|
||||
}
|
||||
}
|
||||
${ProductTypeDetailsFragmentDoc}`;
|
||||
|
||||
|
@ -11071,10 +11133,6 @@ export const ProductTypeCreateDataDocument = gql`
|
|||
shop {
|
||||
defaultWeightUnit
|
||||
}
|
||||
taxTypes {
|
||||
taxCode
|
||||
description
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
|
@ -12425,12 +12483,8 @@ export const ProductDetailsDocument = gql`
|
|||
product(id: $id, channel: $channel) {
|
||||
...Product
|
||||
}
|
||||
taxTypes {
|
||||
...TaxType
|
||||
}
|
||||
}
|
||||
${ProductFragmentDoc}
|
||||
${TaxTypeFragmentDoc}`;
|
||||
${ProductFragmentDoc}`;
|
||||
|
||||
/**
|
||||
* __useProductDetailsQuery__
|
||||
|
@ -12487,13 +12541,13 @@ export const ProductTypeDocument = gql`
|
|||
...AttributeValueList
|
||||
}
|
||||
}
|
||||
taxType {
|
||||
...TaxType
|
||||
taxClass {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
${AttributeValueListFragmentDoc}
|
||||
${TaxTypeFragmentDoc}`;
|
||||
${AttributeValueListFragmentDoc}`;
|
||||
|
||||
/**
|
||||
* __useProductTypeQuery__
|
||||
|
@ -14745,157 +14799,421 @@ export function useStaffMemberDetailsLazyQuery(baseOptions?: ApolloReactHooks.La
|
|||
export type StaffMemberDetailsQueryHookResult = ReturnType<typeof useStaffMemberDetailsQuery>;
|
||||
export type StaffMemberDetailsLazyQueryHookResult = ReturnType<typeof useStaffMemberDetailsLazyQuery>;
|
||||
export type StaffMemberDetailsQueryResult = Apollo.QueryResult<Types.StaffMemberDetailsQuery, Types.StaffMemberDetailsQueryVariables>;
|
||||
export const UpdateTaxSettingsDocument = gql`
|
||||
mutation UpdateTaxSettings($input: ShopSettingsInput!) {
|
||||
shopSettingsUpdate(input: $input) {
|
||||
export const TaxConfigurationUpdateDocument = gql`
|
||||
mutation TaxConfigurationUpdate($id: ID!, $input: TaxConfigurationUpdateInput!) {
|
||||
taxConfigurationUpdate(id: $id, input: $input) {
|
||||
errors {
|
||||
...ShopSettingsUpdateErrorFragment
|
||||
...TaxConfigurationUpdateErrorFragment
|
||||
}
|
||||
shop {
|
||||
...ShopTaxes
|
||||
taxConfiguration {
|
||||
...TaxConfiguration
|
||||
}
|
||||
}
|
||||
}
|
||||
${ShopSettingsUpdateErrorFragmentFragmentDoc}
|
||||
${ShopTaxesFragmentDoc}`;
|
||||
export type UpdateTaxSettingsMutationFn = Apollo.MutationFunction<Types.UpdateTaxSettingsMutation, Types.UpdateTaxSettingsMutationVariables>;
|
||||
${TaxConfigurationUpdateErrorFragmentFragmentDoc}
|
||||
${TaxConfigurationFragmentDoc}`;
|
||||
export type TaxConfigurationUpdateMutationFn = Apollo.MutationFunction<Types.TaxConfigurationUpdateMutation, Types.TaxConfigurationUpdateMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useUpdateTaxSettingsMutation__
|
||||
* __useTaxConfigurationUpdateMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useUpdateTaxSettingsMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useUpdateTaxSettingsMutation` returns a tuple that includes:
|
||||
* To run a mutation, you first call `useTaxConfigurationUpdateMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useTaxConfigurationUpdateMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [updateTaxSettingsMutation, { data, loading, error }] = useUpdateTaxSettingsMutation({
|
||||
* const [taxConfigurationUpdateMutation, { data, loading, error }] = useTaxConfigurationUpdateMutation({
|
||||
* variables: {
|
||||
* id: // value for 'id'
|
||||
* input: // value for 'input'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useTaxConfigurationUpdateMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<Types.TaxConfigurationUpdateMutation, Types.TaxConfigurationUpdateMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return ApolloReactHooks.useMutation<Types.TaxConfigurationUpdateMutation, Types.TaxConfigurationUpdateMutationVariables>(TaxConfigurationUpdateDocument, options);
|
||||
}
|
||||
export type TaxConfigurationUpdateMutationHookResult = ReturnType<typeof useTaxConfigurationUpdateMutation>;
|
||||
export type TaxConfigurationUpdateMutationResult = Apollo.MutationResult<Types.TaxConfigurationUpdateMutation>;
|
||||
export type TaxConfigurationUpdateMutationOptions = Apollo.BaseMutationOptions<Types.TaxConfigurationUpdateMutation, Types.TaxConfigurationUpdateMutationVariables>;
|
||||
export const TaxCountryConfigurationUpdateDocument = gql`
|
||||
mutation TaxCountryConfigurationUpdate($countryCode: CountryCode!, $updateTaxClassRates: [TaxClassRateInput!]!) {
|
||||
taxCountryConfigurationUpdate(
|
||||
countryCode: $countryCode
|
||||
updateTaxClassRates: $updateTaxClassRates
|
||||
) {
|
||||
errors {
|
||||
...TaxCountryConfigurationUpdateErrorFragment
|
||||
}
|
||||
taxCountryConfiguration {
|
||||
...TaxCountryConfiguration
|
||||
}
|
||||
}
|
||||
}
|
||||
${TaxCountryConfigurationUpdateErrorFragmentFragmentDoc}
|
||||
${TaxCountryConfigurationFragmentDoc}`;
|
||||
export type TaxCountryConfigurationUpdateMutationFn = Apollo.MutationFunction<Types.TaxCountryConfigurationUpdateMutation, Types.TaxCountryConfigurationUpdateMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useTaxCountryConfigurationUpdateMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useTaxCountryConfigurationUpdateMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useTaxCountryConfigurationUpdateMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [taxCountryConfigurationUpdateMutation, { data, loading, error }] = useTaxCountryConfigurationUpdateMutation({
|
||||
* variables: {
|
||||
* countryCode: // value for 'countryCode'
|
||||
* updateTaxClassRates: // value for 'updateTaxClassRates'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useTaxCountryConfigurationUpdateMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<Types.TaxCountryConfigurationUpdateMutation, Types.TaxCountryConfigurationUpdateMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return ApolloReactHooks.useMutation<Types.TaxCountryConfigurationUpdateMutation, Types.TaxCountryConfigurationUpdateMutationVariables>(TaxCountryConfigurationUpdateDocument, options);
|
||||
}
|
||||
export type TaxCountryConfigurationUpdateMutationHookResult = ReturnType<typeof useTaxCountryConfigurationUpdateMutation>;
|
||||
export type TaxCountryConfigurationUpdateMutationResult = Apollo.MutationResult<Types.TaxCountryConfigurationUpdateMutation>;
|
||||
export type TaxCountryConfigurationUpdateMutationOptions = Apollo.BaseMutationOptions<Types.TaxCountryConfigurationUpdateMutation, Types.TaxCountryConfigurationUpdateMutationVariables>;
|
||||
export const TaxCountryConfigurationDeleteDocument = gql`
|
||||
mutation TaxCountryConfigurationDelete($countryCode: CountryCode!) {
|
||||
taxCountryConfigurationDelete(countryCode: $countryCode) {
|
||||
errors {
|
||||
...TaxCountryConfigurationDeleteErrorFragment
|
||||
}
|
||||
taxCountryConfiguration {
|
||||
...TaxCountryConfiguration
|
||||
}
|
||||
}
|
||||
}
|
||||
${TaxCountryConfigurationDeleteErrorFragmentFragmentDoc}
|
||||
${TaxCountryConfigurationFragmentDoc}`;
|
||||
export type TaxCountryConfigurationDeleteMutationFn = Apollo.MutationFunction<Types.TaxCountryConfigurationDeleteMutation, Types.TaxCountryConfigurationDeleteMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useTaxCountryConfigurationDeleteMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useTaxCountryConfigurationDeleteMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useTaxCountryConfigurationDeleteMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [taxCountryConfigurationDeleteMutation, { data, loading, error }] = useTaxCountryConfigurationDeleteMutation({
|
||||
* variables: {
|
||||
* countryCode: // value for 'countryCode'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useTaxCountryConfigurationDeleteMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<Types.TaxCountryConfigurationDeleteMutation, Types.TaxCountryConfigurationDeleteMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return ApolloReactHooks.useMutation<Types.TaxCountryConfigurationDeleteMutation, Types.TaxCountryConfigurationDeleteMutationVariables>(TaxCountryConfigurationDeleteDocument, options);
|
||||
}
|
||||
export type TaxCountryConfigurationDeleteMutationHookResult = ReturnType<typeof useTaxCountryConfigurationDeleteMutation>;
|
||||
export type TaxCountryConfigurationDeleteMutationResult = Apollo.MutationResult<Types.TaxCountryConfigurationDeleteMutation>;
|
||||
export type TaxCountryConfigurationDeleteMutationOptions = Apollo.BaseMutationOptions<Types.TaxCountryConfigurationDeleteMutation, Types.TaxCountryConfigurationDeleteMutationVariables>;
|
||||
export const TaxClassUpdateDocument = gql`
|
||||
mutation TaxClassUpdate($id: ID!, $input: TaxClassUpdateInput!) {
|
||||
taxClassUpdate(id: $id, input: $input) {
|
||||
errors {
|
||||
...TaxClassUpdateErrorFragment
|
||||
}
|
||||
taxClass {
|
||||
...TaxClass
|
||||
}
|
||||
}
|
||||
}
|
||||
${TaxClassUpdateErrorFragmentFragmentDoc}
|
||||
${TaxClassFragmentDoc}`;
|
||||
export type TaxClassUpdateMutationFn = Apollo.MutationFunction<Types.TaxClassUpdateMutation, Types.TaxClassUpdateMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useTaxClassUpdateMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useTaxClassUpdateMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useTaxClassUpdateMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [taxClassUpdateMutation, { data, loading, error }] = useTaxClassUpdateMutation({
|
||||
* variables: {
|
||||
* id: // value for 'id'
|
||||
* input: // value for 'input'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useTaxClassUpdateMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<Types.TaxClassUpdateMutation, Types.TaxClassUpdateMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return ApolloReactHooks.useMutation<Types.TaxClassUpdateMutation, Types.TaxClassUpdateMutationVariables>(TaxClassUpdateDocument, options);
|
||||
}
|
||||
export type TaxClassUpdateMutationHookResult = ReturnType<typeof useTaxClassUpdateMutation>;
|
||||
export type TaxClassUpdateMutationResult = Apollo.MutationResult<Types.TaxClassUpdateMutation>;
|
||||
export type TaxClassUpdateMutationOptions = Apollo.BaseMutationOptions<Types.TaxClassUpdateMutation, Types.TaxClassUpdateMutationVariables>;
|
||||
export const TaxClassCreateDocument = gql`
|
||||
mutation TaxClassCreate($input: TaxClassCreateInput!) {
|
||||
taxClassCreate(input: $input) {
|
||||
errors {
|
||||
...TaxClassCreateErrorFragment
|
||||
}
|
||||
taxClass {
|
||||
...TaxClass
|
||||
}
|
||||
}
|
||||
}
|
||||
${TaxClassCreateErrorFragmentFragmentDoc}
|
||||
${TaxClassFragmentDoc}`;
|
||||
export type TaxClassCreateMutationFn = Apollo.MutationFunction<Types.TaxClassCreateMutation, Types.TaxClassCreateMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useTaxClassCreateMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useTaxClassCreateMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useTaxClassCreateMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [taxClassCreateMutation, { data, loading, error }] = useTaxClassCreateMutation({
|
||||
* variables: {
|
||||
* input: // value for 'input'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useUpdateTaxSettingsMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<Types.UpdateTaxSettingsMutation, Types.UpdateTaxSettingsMutationVariables>) {
|
||||
export function useTaxClassCreateMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<Types.TaxClassCreateMutation, Types.TaxClassCreateMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return ApolloReactHooks.useMutation<Types.UpdateTaxSettingsMutation, Types.UpdateTaxSettingsMutationVariables>(UpdateTaxSettingsDocument, options);
|
||||
return ApolloReactHooks.useMutation<Types.TaxClassCreateMutation, Types.TaxClassCreateMutationVariables>(TaxClassCreateDocument, options);
|
||||
}
|
||||
export type UpdateTaxSettingsMutationHookResult = ReturnType<typeof useUpdateTaxSettingsMutation>;
|
||||
export type UpdateTaxSettingsMutationResult = Apollo.MutationResult<Types.UpdateTaxSettingsMutation>;
|
||||
export type UpdateTaxSettingsMutationOptions = Apollo.BaseMutationOptions<Types.UpdateTaxSettingsMutation, Types.UpdateTaxSettingsMutationVariables>;
|
||||
export const FetchTaxesDocument = gql`
|
||||
mutation FetchTaxes {
|
||||
shopFetchTaxRates {
|
||||
export type TaxClassCreateMutationHookResult = ReturnType<typeof useTaxClassCreateMutation>;
|
||||
export type TaxClassCreateMutationResult = Apollo.MutationResult<Types.TaxClassCreateMutation>;
|
||||
export type TaxClassCreateMutationOptions = Apollo.BaseMutationOptions<Types.TaxClassCreateMutation, Types.TaxClassCreateMutationVariables>;
|
||||
export const TaxClassDeleteDocument = gql`
|
||||
mutation TaxClassDelete($id: ID!) {
|
||||
taxClassDelete(id: $id) {
|
||||
errors {
|
||||
...ShopFetchTaxRatesErrorFragment
|
||||
}
|
||||
shop {
|
||||
countries {
|
||||
...Country
|
||||
}
|
||||
...TaxClassDeleteErrorFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
${ShopFetchTaxRatesErrorFragmentFragmentDoc}
|
||||
${CountryFragmentDoc}`;
|
||||
export type FetchTaxesMutationFn = Apollo.MutationFunction<Types.FetchTaxesMutation, Types.FetchTaxesMutationVariables>;
|
||||
${TaxClassDeleteErrorFragmentFragmentDoc}`;
|
||||
export type TaxClassDeleteMutationFn = Apollo.MutationFunction<Types.TaxClassDeleteMutation, Types.TaxClassDeleteMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useFetchTaxesMutation__
|
||||
* __useTaxClassDeleteMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useFetchTaxesMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useFetchTaxesMutation` returns a tuple that includes:
|
||||
* To run a mutation, you first call `useTaxClassDeleteMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useTaxClassDeleteMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [fetchTaxesMutation, { data, loading, error }] = useFetchTaxesMutation({
|
||||
* const [taxClassDeleteMutation, { data, loading, error }] = useTaxClassDeleteMutation({
|
||||
* variables: {
|
||||
* id: // value for 'id'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useFetchTaxesMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<Types.FetchTaxesMutation, Types.FetchTaxesMutationVariables>) {
|
||||
export function useTaxClassDeleteMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<Types.TaxClassDeleteMutation, Types.TaxClassDeleteMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return ApolloReactHooks.useMutation<Types.FetchTaxesMutation, Types.FetchTaxesMutationVariables>(FetchTaxesDocument, options);
|
||||
return ApolloReactHooks.useMutation<Types.TaxClassDeleteMutation, Types.TaxClassDeleteMutationVariables>(TaxClassDeleteDocument, options);
|
||||
}
|
||||
export type TaxClassDeleteMutationHookResult = ReturnType<typeof useTaxClassDeleteMutation>;
|
||||
export type TaxClassDeleteMutationResult = Apollo.MutationResult<Types.TaxClassDeleteMutation>;
|
||||
export type TaxClassDeleteMutationOptions = Apollo.BaseMutationOptions<Types.TaxClassDeleteMutation, Types.TaxClassDeleteMutationVariables>;
|
||||
export const TaxConfigurationsListDocument = gql`
|
||||
query TaxConfigurationsList($before: String, $after: String, $first: Int, $last: Int, $filter: TaxConfigurationFilterInput) {
|
||||
taxConfigurations(
|
||||
before: $before
|
||||
after: $after
|
||||
first: $first
|
||||
last: $last
|
||||
filter: $filter
|
||||
) {
|
||||
edges {
|
||||
node {
|
||||
...TaxConfiguration
|
||||
}
|
||||
export type FetchTaxesMutationHookResult = ReturnType<typeof useFetchTaxesMutation>;
|
||||
export type FetchTaxesMutationResult = Apollo.MutationResult<Types.FetchTaxesMutation>;
|
||||
export type FetchTaxesMutationOptions = Apollo.BaseMutationOptions<Types.FetchTaxesMutation, Types.FetchTaxesMutationVariables>;
|
||||
export const CountryListDocument = gql`
|
||||
query CountryList {
|
||||
shop {
|
||||
...ShopTaxes
|
||||
countries {
|
||||
...CountryWithTaxes
|
||||
}
|
||||
}
|
||||
}
|
||||
${ShopTaxesFragmentDoc}
|
||||
${CountryWithTaxesFragmentDoc}`;
|
||||
${TaxConfigurationFragmentDoc}`;
|
||||
|
||||
/**
|
||||
* __useCountryListQuery__
|
||||
* __useTaxConfigurationsListQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useCountryListQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useCountryListQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* To run a query within a React component, call `useTaxConfigurationsListQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useTaxConfigurationsListQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useCountryListQuery({
|
||||
* const { data, loading, error } = useTaxConfigurationsListQuery({
|
||||
* variables: {
|
||||
* before: // value for 'before'
|
||||
* after: // value for 'after'
|
||||
* first: // value for 'first'
|
||||
* last: // value for 'last'
|
||||
* filter: // value for 'filter'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useCountryListQuery(baseOptions?: ApolloReactHooks.QueryHookOptions<Types.CountryListQuery, Types.CountryListQueryVariables>) {
|
||||
export function useTaxConfigurationsListQuery(baseOptions?: ApolloReactHooks.QueryHookOptions<Types.TaxConfigurationsListQuery, Types.TaxConfigurationsListQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return ApolloReactHooks.useQuery<Types.CountryListQuery, Types.CountryListQueryVariables>(CountryListDocument, options);
|
||||
return ApolloReactHooks.useQuery<Types.TaxConfigurationsListQuery, Types.TaxConfigurationsListQueryVariables>(TaxConfigurationsListDocument, options);
|
||||
}
|
||||
export function useCountryListLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions<Types.CountryListQuery, Types.CountryListQueryVariables>) {
|
||||
export function useTaxConfigurationsListLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions<Types.TaxConfigurationsListQuery, Types.TaxConfigurationsListQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return ApolloReactHooks.useLazyQuery<Types.CountryListQuery, Types.CountryListQueryVariables>(CountryListDocument, options);
|
||||
return ApolloReactHooks.useLazyQuery<Types.TaxConfigurationsListQuery, Types.TaxConfigurationsListQueryVariables>(TaxConfigurationsListDocument, options);
|
||||
}
|
||||
export type CountryListQueryHookResult = ReturnType<typeof useCountryListQuery>;
|
||||
export type CountryListLazyQueryHookResult = ReturnType<typeof useCountryListLazyQuery>;
|
||||
export type CountryListQueryResult = Apollo.QueryResult<Types.CountryListQuery, Types.CountryListQueryVariables>;
|
||||
export const TaxTypeListDocument = gql`
|
||||
query TaxTypeList {
|
||||
taxTypes {
|
||||
...TaxType
|
||||
export type TaxConfigurationsListQueryHookResult = ReturnType<typeof useTaxConfigurationsListQuery>;
|
||||
export type TaxConfigurationsListLazyQueryHookResult = ReturnType<typeof useTaxConfigurationsListLazyQuery>;
|
||||
export type TaxConfigurationsListQueryResult = Apollo.QueryResult<Types.TaxConfigurationsListQuery, Types.TaxConfigurationsListQueryVariables>;
|
||||
export const TaxCountriesListDocument = gql`
|
||||
query TaxCountriesList {
|
||||
taxCountryConfigurations {
|
||||
...TaxCountryConfiguration
|
||||
}
|
||||
}
|
||||
${TaxTypeFragmentDoc}`;
|
||||
${TaxCountryConfigurationFragmentDoc}`;
|
||||
|
||||
/**
|
||||
* __useTaxTypeListQuery__
|
||||
* __useTaxCountriesListQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useTaxTypeListQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useTaxTypeListQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* To run a query within a React component, call `useTaxCountriesListQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useTaxCountriesListQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useTaxTypeListQuery({
|
||||
* const { data, loading, error } = useTaxCountriesListQuery({
|
||||
* variables: {
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useTaxTypeListQuery(baseOptions?: ApolloReactHooks.QueryHookOptions<Types.TaxTypeListQuery, Types.TaxTypeListQueryVariables>) {
|
||||
export function useTaxCountriesListQuery(baseOptions?: ApolloReactHooks.QueryHookOptions<Types.TaxCountriesListQuery, Types.TaxCountriesListQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return ApolloReactHooks.useQuery<Types.TaxTypeListQuery, Types.TaxTypeListQueryVariables>(TaxTypeListDocument, options);
|
||||
return ApolloReactHooks.useQuery<Types.TaxCountriesListQuery, Types.TaxCountriesListQueryVariables>(TaxCountriesListDocument, options);
|
||||
}
|
||||
export function useTaxTypeListLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions<Types.TaxTypeListQuery, Types.TaxTypeListQueryVariables>) {
|
||||
export function useTaxCountriesListLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions<Types.TaxCountriesListQuery, Types.TaxCountriesListQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return ApolloReactHooks.useLazyQuery<Types.TaxTypeListQuery, Types.TaxTypeListQueryVariables>(TaxTypeListDocument, options);
|
||||
return ApolloReactHooks.useLazyQuery<Types.TaxCountriesListQuery, Types.TaxCountriesListQueryVariables>(TaxCountriesListDocument, options);
|
||||
}
|
||||
export type TaxTypeListQueryHookResult = ReturnType<typeof useTaxTypeListQuery>;
|
||||
export type TaxTypeListLazyQueryHookResult = ReturnType<typeof useTaxTypeListLazyQuery>;
|
||||
export type TaxTypeListQueryResult = Apollo.QueryResult<Types.TaxTypeListQuery, Types.TaxTypeListQueryVariables>;
|
||||
export type TaxCountriesListQueryHookResult = ReturnType<typeof useTaxCountriesListQuery>;
|
||||
export type TaxCountriesListLazyQueryHookResult = ReturnType<typeof useTaxCountriesListLazyQuery>;
|
||||
export type TaxCountriesListQueryResult = Apollo.QueryResult<Types.TaxCountriesListQuery, Types.TaxCountriesListQueryVariables>;
|
||||
export const TaxClassesListDocument = gql`
|
||||
query TaxClassesList($before: String, $after: String, $first: Int, $last: Int, $filter: TaxClassFilterInput, $sortBy: TaxClassSortingInput) {
|
||||
taxClasses(
|
||||
before: $before
|
||||
after: $after
|
||||
first: $first
|
||||
last: $last
|
||||
filter: $filter
|
||||
sortBy: $sortBy
|
||||
) {
|
||||
edges {
|
||||
node {
|
||||
...TaxClass
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
${TaxClassFragmentDoc}`;
|
||||
|
||||
/**
|
||||
* __useTaxClassesListQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useTaxClassesListQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useTaxClassesListQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useTaxClassesListQuery({
|
||||
* variables: {
|
||||
* before: // value for 'before'
|
||||
* after: // value for 'after'
|
||||
* first: // value for 'first'
|
||||
* last: // value for 'last'
|
||||
* filter: // value for 'filter'
|
||||
* sortBy: // value for 'sortBy'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useTaxClassesListQuery(baseOptions?: ApolloReactHooks.QueryHookOptions<Types.TaxClassesListQuery, Types.TaxClassesListQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return ApolloReactHooks.useQuery<Types.TaxClassesListQuery, Types.TaxClassesListQueryVariables>(TaxClassesListDocument, options);
|
||||
}
|
||||
export function useTaxClassesListLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions<Types.TaxClassesListQuery, Types.TaxClassesListQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return ApolloReactHooks.useLazyQuery<Types.TaxClassesListQuery, Types.TaxClassesListQueryVariables>(TaxClassesListDocument, options);
|
||||
}
|
||||
export type TaxClassesListQueryHookResult = ReturnType<typeof useTaxClassesListQuery>;
|
||||
export type TaxClassesListLazyQueryHookResult = ReturnType<typeof useTaxClassesListLazyQuery>;
|
||||
export type TaxClassesListQueryResult = Apollo.QueryResult<Types.TaxClassesListQuery, Types.TaxClassesListQueryVariables>;
|
||||
export const TaxClassAssignDocument = gql`
|
||||
query TaxClassAssign($first: Int, $after: String) {
|
||||
taxClasses(first: $first, after: $after) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
endCursor
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useTaxClassAssignQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useTaxClassAssignQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useTaxClassAssignQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useTaxClassAssignQuery({
|
||||
* variables: {
|
||||
* first: // value for 'first'
|
||||
* after: // value for 'after'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useTaxClassAssignQuery(baseOptions?: ApolloReactHooks.QueryHookOptions<Types.TaxClassAssignQuery, Types.TaxClassAssignQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return ApolloReactHooks.useQuery<Types.TaxClassAssignQuery, Types.TaxClassAssignQueryVariables>(TaxClassAssignDocument, options);
|
||||
}
|
||||
export function useTaxClassAssignLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions<Types.TaxClassAssignQuery, Types.TaxClassAssignQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return ApolloReactHooks.useLazyQuery<Types.TaxClassAssignQuery, Types.TaxClassAssignQueryVariables>(TaxClassAssignDocument, options);
|
||||
}
|
||||
export type TaxClassAssignQueryHookResult = ReturnType<typeof useTaxClassAssignQuery>;
|
||||
export type TaxClassAssignLazyQueryHookResult = ReturnType<typeof useTaxClassAssignLazyQuery>;
|
||||
export type TaxClassAssignQueryResult = Apollo.QueryResult<Types.TaxClassAssignQuery, Types.TaxClassAssignQueryVariables>;
|
||||
export const UpdateProductTranslationsDocument = gql`
|
||||
mutation UpdateProductTranslations($id: ID!, $input: TranslationInput!, $language: LanguageCodeEnum!) {
|
||||
productTranslate(id: $id, input: $input, languageCode: $language) {
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -34,6 +34,7 @@ export interface UseFormOpts<T> {
|
|||
formId?: FormId;
|
||||
checkIfSaveIsDisabled?: CheckIfSaveIsDisabledFnType<T>;
|
||||
disabled?: boolean;
|
||||
mergeData?: boolean;
|
||||
}
|
||||
|
||||
export interface UseFormResult<TData>
|
||||
|
@ -87,10 +88,11 @@ function useForm<T extends FormData, TErrors>(
|
|||
formId: propsFormId,
|
||||
checkIfSaveIsDisabled,
|
||||
disabled,
|
||||
mergeData = true,
|
||||
} = opts;
|
||||
const [errors, setErrors] = useState<FormErrors<T>>({});
|
||||
const [data, setData] = useStateFromProps(initialData, {
|
||||
mergeFunc: merge,
|
||||
mergeFunc: mergeData ? merge : undefined,
|
||||
});
|
||||
|
||||
const isSaveDisabled = () => {
|
||||
|
|
37
src/hooks/useLocalSearch.ts
Normal file
37
src/hooks/useLocalSearch.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
import React from "react";
|
||||
|
||||
/**
|
||||
* This function removes characters which break
|
||||
* regexps but are rarely useful in searches
|
||||
*/
|
||||
const parseQuery = (query: string) =>
|
||||
query.replace(/([.?*+\-=:^$\\[\]<>(){}|])/g, "\\$&");
|
||||
|
||||
/**
|
||||
* useLocalSearch is a hook that is useful when client-side
|
||||
* search is used on particular array of items.
|
||||
*
|
||||
* @param array Array to search through
|
||||
* @param getStringToSearch Function which specifies object
|
||||
* property (possibly nested) on which search is used, eg:
|
||||
* (element) => element.name where name is a string
|
||||
* @returns query, setQuery - useState result
|
||||
* searchResult - filtered array
|
||||
*/
|
||||
export function useLocalSearch<T>(
|
||||
array: T[] | undefined,
|
||||
getStringToSearch: (element: T) => string,
|
||||
) {
|
||||
const [query, setQuery] = React.useState("");
|
||||
const searchResult = React.useMemo(
|
||||
() =>
|
||||
array?.filter(
|
||||
element =>
|
||||
getStringToSearch(element).search(
|
||||
new RegExp(parseQuery(query), "i"),
|
||||
) >= 0,
|
||||
),
|
||||
[array, query],
|
||||
);
|
||||
return { query, setQuery, searchResult };
|
||||
}
|
|
@ -5,7 +5,11 @@ export interface UseStateFromPropsOpts<T> {
|
|||
mergeFunc?: (prevData: T, state: T, newData: T) => T;
|
||||
onRefresh?: (prevData: T, data: T) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This function updates state every time initial
|
||||
* value changes, but uses deep comparisons to detect changes.
|
||||
* You're most likely looking for `useStateUpdate` instead.
|
||||
*/
|
||||
function useStateFromProps<T>(
|
||||
data: T,
|
||||
opts: UseStateFromPropsOpts<T> = {},
|
||||
|
|
14
src/hooks/useStateUpdate.ts
Normal file
14
src/hooks/useStateUpdate.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import React from "react";
|
||||
|
||||
/** useState, but updates state every time initial value changes. */
|
||||
export function useStateUpdate<T>(
|
||||
data: T,
|
||||
): [T, React.Dispatch<React.SetStateAction<T>>] {
|
||||
const [state, setState] = React.useState(data);
|
||||
|
||||
React.useEffect(() => {
|
||||
setState(data);
|
||||
}, [data]);
|
||||
|
||||
return [state, setState];
|
||||
}
|
|
@ -229,11 +229,7 @@ const Routes: React.FC = () => {
|
|||
path="/site-settings"
|
||||
component={SiteSettingsSection}
|
||||
/>
|
||||
<SectionRoute
|
||||
permissions={[PermissionEnum.MANAGE_SETTINGS]}
|
||||
path="/taxes"
|
||||
component={TaxesSection}
|
||||
/>
|
||||
<SectionRoute path="/taxes" component={TaxesSection} />
|
||||
<SectionRoute
|
||||
permissions={[PermissionEnum.MANAGE_SHIPPING]}
|
||||
path="/shipping"
|
||||
|
|
29
src/intl.ts
29
src/intl.ts
|
@ -1,16 +1,6 @@
|
|||
import { defineMessages, IntlShape } from "react-intl";
|
||||
|
||||
export const commonMessages = defineMessages({
|
||||
active: {
|
||||
id: "c24hjq",
|
||||
defaultMessage: "Active",
|
||||
description: "status",
|
||||
},
|
||||
notActive: {
|
||||
id: "NwQXZp",
|
||||
defaultMessage: "Not active",
|
||||
description: "status",
|
||||
},
|
||||
availability: {
|
||||
id: "hOxIeP",
|
||||
defaultMessage: "Availability",
|
||||
|
@ -228,6 +218,11 @@ export const buttonMessages = defineMessages({
|
|||
defaultMessage: "Accept",
|
||||
description: "button",
|
||||
},
|
||||
add: {
|
||||
id: "BJtUQI",
|
||||
defaultMessage: "Add",
|
||||
description: "button",
|
||||
},
|
||||
approve: {
|
||||
id: "59XppT",
|
||||
defaultMessage: "Approve",
|
||||
|
@ -509,11 +504,25 @@ export const sectionNames = defineMessages({
|
|||
});
|
||||
|
||||
export const commonStatusMessages = defineMessages({
|
||||
active: {
|
||||
id: "c24hjq",
|
||||
defaultMessage: "Active",
|
||||
description: "status",
|
||||
},
|
||||
notActive: {
|
||||
id: "NwQXZp",
|
||||
defaultMessage: "Not active",
|
||||
description: "status",
|
||||
},
|
||||
cancelled: {
|
||||
id: "dOQB9o",
|
||||
defaultMessage: "Cancelled",
|
||||
description: "payment status",
|
||||
},
|
||||
disabled: {
|
||||
id: "tthToS",
|
||||
defaultMessage: "Disabled",
|
||||
},
|
||||
});
|
||||
|
||||
export const orderStatusMessages = defineMessages({
|
||||
|
|
|
@ -7,18 +7,21 @@ import Metadata, { MetadataFormData } from "@saleor/components/Metadata";
|
|||
import PageHeader from "@saleor/components/PageHeader";
|
||||
import Savebar from "@saleor/components/Savebar";
|
||||
import {
|
||||
ProductTypeDetailsQuery,
|
||||
ProductTypeKindEnum,
|
||||
TaxClassFragment,
|
||||
WeightUnitsEnum,
|
||||
} from "@saleor/graphql";
|
||||
import { ChangeEvent, FormChange, SubmitPromise } from "@saleor/hooks/useForm";
|
||||
import { SubmitPromise } from "@saleor/hooks/useForm";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import useStateFromProps from "@saleor/hooks/useStateFromProps";
|
||||
import { sectionNames } from "@saleor/intl";
|
||||
import { ConfirmButtonTransitionState } from "@saleor/macaw-ui";
|
||||
import { makeProductTypeKindChangeHandler } from "@saleor/productTypes/handlers";
|
||||
import {
|
||||
handleTaxClassChange,
|
||||
makeProductTypeKindChangeHandler,
|
||||
} from "@saleor/productTypes/handlers";
|
||||
import { productTypeListUrl } from "@saleor/productTypes/urls";
|
||||
import { UserError } from "@saleor/types";
|
||||
import { FetchMoreProps, UserError } from "@saleor/types";
|
||||
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
@ -31,7 +34,7 @@ export interface ProductTypeForm extends MetadataFormData {
|
|||
name: string;
|
||||
kind: ProductTypeKindEnum;
|
||||
isShippingRequired: boolean;
|
||||
taxType: string;
|
||||
taxClassId: string;
|
||||
weight: number;
|
||||
}
|
||||
|
||||
|
@ -41,10 +44,11 @@ export interface ProductTypeCreatePageProps {
|
|||
disabled: boolean;
|
||||
pageTitle: string;
|
||||
saveButtonBarState: ConfirmButtonTransitionState;
|
||||
taxTypes: ProductTypeDetailsQuery["taxTypes"];
|
||||
taxClasses: Array<Omit<TaxClassFragment, "countries">>;
|
||||
kind: ProductTypeKindEnum;
|
||||
onChangeKind: (kind: ProductTypeKindEnum) => void;
|
||||
onSubmit: (data: ProductTypeForm) => SubmitPromise<any[]>;
|
||||
onFetchMoreTaxClasses: FetchMoreProps;
|
||||
}
|
||||
|
||||
const formInitialData: ProductTypeForm = {
|
||||
|
@ -53,38 +57,26 @@ const formInitialData: ProductTypeForm = {
|
|||
name: "",
|
||||
kind: ProductTypeKindEnum.NORMAL,
|
||||
privateMetadata: [],
|
||||
taxType: "",
|
||||
taxClassId: "",
|
||||
weight: 0,
|
||||
};
|
||||
|
||||
function handleTaxTypeChange(
|
||||
event: ChangeEvent,
|
||||
taxTypes: ProductTypeDetailsQuery["taxTypes"],
|
||||
formChange: FormChange,
|
||||
displayChange: (name: string) => void,
|
||||
) {
|
||||
formChange(event);
|
||||
displayChange(
|
||||
taxTypes.find(taxType => taxType.taxCode === event.target.value)
|
||||
.description,
|
||||
);
|
||||
}
|
||||
|
||||
const ProductTypeCreatePage: React.FC<ProductTypeCreatePageProps> = ({
|
||||
defaultWeightUnit,
|
||||
disabled,
|
||||
errors,
|
||||
pageTitle,
|
||||
saveButtonBarState,
|
||||
taxTypes,
|
||||
taxClasses,
|
||||
kind,
|
||||
onChangeKind,
|
||||
onSubmit,
|
||||
onFetchMoreTaxClasses,
|
||||
}: ProductTypeCreatePageProps) => {
|
||||
const intl = useIntl();
|
||||
const navigate = useNavigator();
|
||||
|
||||
const [taxTypeDisplayName, setTaxTypeDisplayName] = useStateFromProps("");
|
||||
const [taxClassDisplayName, setTaxClassDisplayName] = useStateFromProps("");
|
||||
const {
|
||||
makeChangeHandler: makeMetadataChangeHandler,
|
||||
} = useMetadataChangeTrigger();
|
||||
|
@ -128,16 +120,17 @@ const ProductTypeCreatePage: React.FC<ProductTypeCreatePageProps> = ({
|
|||
<ProductTypeTaxes
|
||||
disabled={disabled}
|
||||
data={data}
|
||||
taxTypes={taxTypes}
|
||||
taxTypeDisplayName={taxTypeDisplayName}
|
||||
taxClasses={taxClasses}
|
||||
taxClassDisplayName={taxClassDisplayName}
|
||||
onChange={event =>
|
||||
handleTaxTypeChange(
|
||||
handleTaxClassChange(
|
||||
event,
|
||||
taxTypes,
|
||||
taxClasses,
|
||||
change,
|
||||
setTaxTypeDisplayName,
|
||||
setTaxClassDisplayName,
|
||||
)
|
||||
}
|
||||
onFetchMore={onFetchMoreTaxClasses}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<Metadata data={data} onChange={changeMetadata} />
|
||||
|
|
|
@ -12,16 +12,23 @@ import {
|
|||
ProductAttributeType,
|
||||
ProductTypeDetailsQuery,
|
||||
ProductTypeKindEnum,
|
||||
TaxClassFragment,
|
||||
WeightUnitsEnum,
|
||||
} from "@saleor/graphql";
|
||||
import { ChangeEvent, FormChange, SubmitPromise } from "@saleor/hooks/useForm";
|
||||
import { SubmitPromise } from "@saleor/hooks/useForm";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import useStateFromProps from "@saleor/hooks/useStateFromProps";
|
||||
import { sectionNames } from "@saleor/intl";
|
||||
import { ConfirmButtonTransitionState } from "@saleor/macaw-ui";
|
||||
import { maybe } from "@saleor/misc";
|
||||
import { handleTaxClassChange } from "@saleor/productTypes/handlers";
|
||||
import { productTypeListUrl } from "@saleor/productTypes/urls";
|
||||
import { ListActions, ReorderEvent, UserError } from "@saleor/types";
|
||||
import {
|
||||
FetchMoreProps,
|
||||
ListActions,
|
||||
ReorderEvent,
|
||||
UserError,
|
||||
} from "@saleor/types";
|
||||
import { mapMetadataItemToInput } from "@saleor/utils/maps";
|
||||
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
|
||||
import React from "react";
|
||||
|
@ -43,7 +50,7 @@ export interface ProductTypeForm extends MetadataFormData {
|
|||
kind: ProductTypeKindEnum;
|
||||
hasVariants: boolean;
|
||||
isShippingRequired: boolean;
|
||||
taxType: string;
|
||||
taxClassId: string;
|
||||
productAttributes: ChoiceType[];
|
||||
variantAttributes: ChoiceType[];
|
||||
weight: number;
|
||||
|
@ -57,7 +64,7 @@ export interface ProductTypeDetailsPageProps {
|
|||
pageTitle: string;
|
||||
productAttributeList: ListActions;
|
||||
saveButtonBarState: ConfirmButtonTransitionState;
|
||||
taxTypes: ProductTypeDetailsQuery["taxTypes"];
|
||||
taxClasses: Array<Omit<TaxClassFragment, "countries">>;
|
||||
variantAttributeList: ListActions;
|
||||
onAttributeAdd: (type: ProductAttributeType) => void;
|
||||
onAttributeReorder: (event: ReorderEvent, type: ProductAttributeType) => void;
|
||||
|
@ -67,19 +74,7 @@ export interface ProductTypeDetailsPageProps {
|
|||
onSubmit: (data: ProductTypeForm) => SubmitPromise;
|
||||
setSelectedVariantAttributes: (data: string[]) => void;
|
||||
selectedVariantAttributes: string[];
|
||||
}
|
||||
|
||||
function handleTaxTypeChange(
|
||||
event: ChangeEvent,
|
||||
taxTypes: ProductTypeDetailsQuery["taxTypes"],
|
||||
formChange: FormChange,
|
||||
displayChange: (name: string) => void,
|
||||
) {
|
||||
formChange(event);
|
||||
displayChange(
|
||||
taxTypes.find(taxType => taxType.taxCode === event.target.value)
|
||||
.description,
|
||||
);
|
||||
onFetchMoreTaxClasses: FetchMoreProps;
|
||||
}
|
||||
|
||||
const ProductTypeDetailsPage: React.FC<ProductTypeDetailsPageProps> = ({
|
||||
|
@ -90,7 +85,7 @@ const ProductTypeDetailsPage: React.FC<ProductTypeDetailsPageProps> = ({
|
|||
productType,
|
||||
productAttributeList,
|
||||
saveButtonBarState,
|
||||
taxTypes,
|
||||
taxClasses,
|
||||
variantAttributeList,
|
||||
onAttributeAdd,
|
||||
onAttributeUnassign,
|
||||
|
@ -100,6 +95,7 @@ const ProductTypeDetailsPage: React.FC<ProductTypeDetailsPageProps> = ({
|
|||
onSubmit,
|
||||
setSelectedVariantAttributes,
|
||||
selectedVariantAttributes,
|
||||
onFetchMoreTaxClasses,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const navigate = useNavigator();
|
||||
|
@ -110,8 +106,8 @@ const ProductTypeDetailsPage: React.FC<ProductTypeDetailsPageProps> = ({
|
|||
makeChangeHandler: makeMetadataChangeHandler,
|
||||
} = useMetadataChangeTrigger();
|
||||
|
||||
const [taxTypeDisplayName, setTaxTypeDisplayName] = useStateFromProps(
|
||||
maybe(() => productType.taxType.description, ""),
|
||||
const [taxClassDisplayName, setTaxClassDisplayName] = useStateFromProps(
|
||||
productType?.taxClass?.name ?? "",
|
||||
);
|
||||
const formInitialData: ProductTypeForm = {
|
||||
hasVariants:
|
||||
|
@ -133,7 +129,7 @@ const ProductTypeDetailsPage: React.FC<ProductTypeDetailsPageProps> = ({
|
|||
value: attribute.id,
|
||||
}))
|
||||
: [],
|
||||
taxType: maybe(() => productType.taxType.taxCode, ""),
|
||||
taxClassId: productType?.taxClass?.id ?? "",
|
||||
variantAttributes:
|
||||
maybe(() => productType.variantAttributes) !== undefined
|
||||
? productType.variantAttributes.map(attribute => ({
|
||||
|
@ -186,16 +182,17 @@ const ProductTypeDetailsPage: React.FC<ProductTypeDetailsPageProps> = ({
|
|||
<ProductTypeTaxes
|
||||
disabled={disabled}
|
||||
data={data}
|
||||
taxTypes={taxTypes}
|
||||
taxTypeDisplayName={taxTypeDisplayName}
|
||||
taxClasses={taxClasses}
|
||||
taxClassDisplayName={taxClassDisplayName}
|
||||
onChange={event =>
|
||||
handleTaxTypeChange(
|
||||
handleTaxClassChange(
|
||||
event,
|
||||
taxTypes,
|
||||
taxClasses,
|
||||
change,
|
||||
setTaxTypeDisplayName,
|
||||
setTaxClassDisplayName,
|
||||
)
|
||||
}
|
||||
onFetchMore={onFetchMoreTaxClasses}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<ProductTypeAttributes
|
||||
|
|
|
@ -115,9 +115,9 @@ const ProductTypeList: React.FC<ProductTypeListProps> = props => {
|
|||
</TableCellHeader>
|
||||
<TableCell className={classes.colTax}>
|
||||
<FormattedMessage
|
||||
id="TalJlD"
|
||||
defaultMessage="Tax"
|
||||
description="tax rate for a product type"
|
||||
id="+Jgot0"
|
||||
defaultMessage="Tax class"
|
||||
description="tax class for a product type"
|
||||
/>
|
||||
</TableCell>
|
||||
</TableHead>
|
||||
|
@ -194,11 +194,7 @@ const ProductTypeList: React.FC<ProductTypeListProps> = props => {
|
|||
)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colTax}>
|
||||
{maybe(() => productType.taxType) ? (
|
||||
productType.taxType.description
|
||||
) : (
|
||||
<Skeleton />
|
||||
)}
|
||||
{productType?.taxClass?.name ?? "-"}
|
||||
</TableCell>
|
||||
</TableRowLink>
|
||||
);
|
||||
|
|
|
@ -1,22 +1,25 @@
|
|||
import { Card, CardContent } from "@material-ui/core";
|
||||
import CardTitle from "@saleor/components/CardTitle";
|
||||
import SingleAutocompleteSelectField from "@saleor/components/SingleAutocompleteSelectField";
|
||||
import { ProductTypeDetailsQuery } from "@saleor/graphql";
|
||||
import { TaxClassFragment } from "@saleor/graphql";
|
||||
import { sectionNames } from "@saleor/intl";
|
||||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
import { taxesMessages } from "@saleor/taxes/messages";
|
||||
import { FetchMoreProps } from "@saleor/types";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import { maybe } from "../../../misc";
|
||||
import { ProductTypeForm } from "../ProductTypeDetailsPage/ProductTypeDetailsPage";
|
||||
|
||||
interface ProductTypeTaxesProps {
|
||||
data: {
|
||||
taxType: string;
|
||||
taxClassId: string;
|
||||
};
|
||||
taxTypeDisplayName: string;
|
||||
taxTypes: ProductTypeDetailsQuery["taxTypes"];
|
||||
taxClassDisplayName: string;
|
||||
taxClasses: Array<Omit<TaxClassFragment, "countries">>;
|
||||
disabled: boolean;
|
||||
onChange: (event: React.ChangeEvent<any>) => void;
|
||||
onFetchMore: FetchMoreProps;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(
|
||||
|
@ -29,39 +32,38 @@ const useStyles = makeStyles(
|
|||
);
|
||||
|
||||
const ProductTypeTaxes: React.FC<ProductTypeTaxesProps> = props => {
|
||||
const { data, disabled, taxTypes, taxTypeDisplayName, onChange } = props;
|
||||
const {
|
||||
data,
|
||||
disabled,
|
||||
taxClasses,
|
||||
taxClassDisplayName,
|
||||
onChange,
|
||||
onFetchMore,
|
||||
} = props;
|
||||
const classes = useStyles(props);
|
||||
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<Card className={classes.root}>
|
||||
<CardTitle
|
||||
title={intl.formatMessage({
|
||||
id: "mUb8Gt",
|
||||
defaultMessage: "Taxes",
|
||||
description: "section header",
|
||||
})}
|
||||
/>
|
||||
<CardTitle title={intl.formatMessage(sectionNames.taxes)} />
|
||||
<CardContent>
|
||||
<SingleAutocompleteSelectField
|
||||
emptyOption
|
||||
disabled={disabled}
|
||||
displayValue={taxTypeDisplayName}
|
||||
label={intl.formatMessage({
|
||||
id: "9xUIAh",
|
||||
defaultMessage: "Tax group",
|
||||
})}
|
||||
name={"taxType" as keyof ProductTypeForm}
|
||||
displayValue={taxClassDisplayName}
|
||||
label={intl.formatMessage(taxesMessages.taxClass)}
|
||||
name={"taxClassId" as keyof ProductTypeForm}
|
||||
onChange={onChange}
|
||||
value={data.taxType}
|
||||
choices={maybe(
|
||||
() =>
|
||||
taxTypes.map(c => ({ label: c.description, value: c.taxCode })),
|
||||
[],
|
||||
)}
|
||||
value={data.taxClassId}
|
||||
choices={taxClasses.map(choice => ({
|
||||
label: choice.name,
|
||||
value: choice.id,
|
||||
}))}
|
||||
InputProps={{
|
||||
autoComplete: "off",
|
||||
}}
|
||||
{...onFetchMore}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
|
|
@ -1007,10 +1007,10 @@ export const productTypeSearch: ProductTypeQuery["productType"] = {
|
|||
id: "UHJvZHVjdFR5cGU6NA==",
|
||||
name: "Candy",
|
||||
productAttributes: [attributes[0]],
|
||||
taxType: {
|
||||
__typename: "TaxType" as "TaxType",
|
||||
description: "PB100000",
|
||||
taxCode: "Books / Manuals",
|
||||
taxClass: {
|
||||
__typename: "TaxClass" as "TaxClass",
|
||||
name: "PB100000",
|
||||
id: "Books / Manuals",
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -1026,10 +1026,10 @@ export const productTypes: Array<RelayToFlat<
|
|||
name: "Candy",
|
||||
kind: ProductTypeKindEnum.NORMAL,
|
||||
productAttributes: [attributes[0]],
|
||||
taxType: {
|
||||
__typename: "TaxType" as "TaxType",
|
||||
description: "PB100000",
|
||||
taxCode: "Books / Manuals",
|
||||
taxClass: {
|
||||
__typename: "TaxClass" as "TaxClass",
|
||||
name: "Shipping method",
|
||||
id: "VGV4Q2xhc3M6MQ==",
|
||||
},
|
||||
variantAttributes: [attributes[1], attributes[2]],
|
||||
},
|
||||
|
@ -1041,10 +1041,10 @@ export const productTypes: Array<RelayToFlat<
|
|||
name: "E-books",
|
||||
kind: ProductTypeKindEnum.NORMAL,
|
||||
productAttributes: [attributes[5]],
|
||||
taxType: {
|
||||
__typename: "TaxType" as "TaxType",
|
||||
description: "PH403682",
|
||||
taxCode: "Holters",
|
||||
taxClass: {
|
||||
__typename: "TaxClass" as "TaxClass",
|
||||
name: "Digital goods",
|
||||
id: "VGV4Q4xhc3M6MQ==",
|
||||
},
|
||||
variantAttributes: [attributes[0], attributes[3]],
|
||||
},
|
||||
|
@ -1056,10 +1056,10 @@ export const productTypes: Array<RelayToFlat<
|
|||
name: "Mugs",
|
||||
kind: ProductTypeKindEnum.NORMAL,
|
||||
productAttributes: [attributes[7]],
|
||||
taxType: {
|
||||
__typename: "TaxType" as "TaxType",
|
||||
description: "PC077426",
|
||||
taxCode: "Cabling",
|
||||
taxClass: {
|
||||
__typename: "TaxClass" as "TaxClass",
|
||||
name: "Houseware",
|
||||
id: "VGV4Q3xhc3M6MQ==",
|
||||
},
|
||||
variantAttributes: [attributes[2], attributes[5]],
|
||||
},
|
||||
|
@ -1071,10 +1071,10 @@ export const productTypes: Array<RelayToFlat<
|
|||
name: "Coffee",
|
||||
kind: ProductTypeKindEnum.NORMAL,
|
||||
productAttributes: [attributes[8]],
|
||||
taxType: {
|
||||
__typename: "TaxType" as "TaxType",
|
||||
description: "PB100000",
|
||||
taxCode: "Books / Manuals",
|
||||
taxClass: {
|
||||
__typename: "TaxClass" as "TaxClass",
|
||||
name: "PB100000",
|
||||
id: "Books / Manuals",
|
||||
},
|
||||
variantAttributes: [attributes[1], attributes[4]],
|
||||
},
|
||||
|
@ -1086,10 +1086,10 @@ export const productTypes: Array<RelayToFlat<
|
|||
name: "T-Shirt",
|
||||
kind: ProductTypeKindEnum.NORMAL,
|
||||
productAttributes: [attributes[4]],
|
||||
taxType: {
|
||||
__typename: "TaxType" as "TaxType",
|
||||
description: "PH403970",
|
||||
taxCode: "Wheelchair",
|
||||
taxClass: {
|
||||
__typename: "TaxClass" as "TaxClass",
|
||||
name: "PH403970",
|
||||
id: "Wheelchair",
|
||||
},
|
||||
variantAttributes: [attributes[1], attributes[6]],
|
||||
},
|
||||
|
@ -1151,10 +1151,10 @@ export const productType: ProductTypeDetailsQuery["productType"] = {
|
|||
unit: null,
|
||||
},
|
||||
],
|
||||
taxType: {
|
||||
__typename: "TaxType" as "TaxType",
|
||||
description: "PH405458",
|
||||
taxCode: "Shields",
|
||||
taxClass: {
|
||||
__typename: "TaxClass",
|
||||
name: "Shipping method",
|
||||
id: "VGV4Q2xhc3M6MQ==",
|
||||
},
|
||||
variantAttributes: [
|
||||
{
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { ProductTypeKindEnum } from "@saleor/graphql";
|
||||
import { FormChange } from "@saleor/hooks/useForm";
|
||||
import { ProductTypeKindEnum, TaxClassFragment } from "@saleor/graphql";
|
||||
import { ChangeEvent, FormChange } from "@saleor/hooks/useForm";
|
||||
|
||||
export const makeProductTypeKindChangeHandler = (
|
||||
onChange: FormChange,
|
||||
|
@ -9,3 +9,15 @@ export const makeProductTypeKindChangeHandler = (
|
|||
onKindChange(kind);
|
||||
onChange(event);
|
||||
};
|
||||
|
||||
export function handleTaxClassChange(
|
||||
event: ChangeEvent,
|
||||
taxClasses: Array<Omit<TaxClassFragment, "countries">>,
|
||||
formChange: FormChange,
|
||||
displayChange: (name: string) => void,
|
||||
) {
|
||||
formChange(event);
|
||||
displayChange(
|
||||
taxClasses.find(taxClass => taxClass.id === event.target.value)?.name ?? "",
|
||||
);
|
||||
}
|
||||
|
|
|
@ -37,10 +37,6 @@ export const productTypeDetailsQuery = gql`
|
|||
shop {
|
||||
defaultWeightUnit
|
||||
}
|
||||
taxTypes {
|
||||
taxCode
|
||||
description
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
|
@ -49,9 +45,5 @@ export const productTypeCreateDataQuery = gql`
|
|||
shop {
|
||||
defaultWeightUnit
|
||||
}
|
||||
taxTypes {
|
||||
taxCode
|
||||
description
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
} from "@saleor/graphql";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import useNotifier from "@saleor/hooks/useNotifier";
|
||||
import { useTaxClassFetchMore } from "@saleor/taxes/utils/useTaxClassFetchMore";
|
||||
import createMetadataCreateHandler from "@saleor/utils/handlers/metadataCreateHandler";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
@ -47,6 +48,8 @@ export const ProductTypeCreate: React.FC<ProductTypeCreateProps> = ({
|
|||
displayLoader: true,
|
||||
});
|
||||
|
||||
const { taxClasses, fetchMoreTaxClasses } = useTaxClassFetchMore();
|
||||
|
||||
const [
|
||||
createProductType,
|
||||
createProductTypeOpts,
|
||||
|
@ -73,7 +76,7 @@ export const ProductTypeCreate: React.FC<ProductTypeCreateProps> = ({
|
|||
isShippingRequired: formData.isShippingRequired,
|
||||
name: formData.name,
|
||||
kind: formData.kind,
|
||||
taxCode: formData.taxType,
|
||||
taxClass: formData.taxClassId,
|
||||
weight: formData.weight,
|
||||
},
|
||||
},
|
||||
|
@ -110,7 +113,8 @@ export const ProductTypeCreate: React.FC<ProductTypeCreateProps> = ({
|
|||
description: "header",
|
||||
})}
|
||||
saveButtonBarState={createProductTypeOpts.status}
|
||||
taxTypes={data?.taxTypes || []}
|
||||
taxClasses={taxClasses ?? []}
|
||||
onFetchMoreTaxClasses={fetchMoreTaxClasses}
|
||||
kind={params.kind}
|
||||
onChangeKind={handleChangeKind}
|
||||
onSubmit={handleSubmit}
|
||||
|
|
|
@ -26,6 +26,7 @@ import { getStringOrPlaceholder, maybe } from "@saleor/misc";
|
|||
import useProductTypeDelete from "@saleor/productTypes/hooks/useProductTypeDelete";
|
||||
import useProductTypeOperations from "@saleor/productTypes/hooks/useProductTypeOperations";
|
||||
import useAvailableProductAttributeSearch from "@saleor/searches/useAvailableProductAttributeSearch";
|
||||
import { useTaxClassFetchMore } from "@saleor/taxes/utils/useTaxClassFetchMore";
|
||||
import { ReorderEvent } from "@saleor/types";
|
||||
import createMetadataUpdateHandler from "@saleor/utils/handlers/metadataUpdateHandler";
|
||||
import { mapEdgesToItems } from "@saleor/utils/maps";
|
||||
|
@ -142,7 +143,7 @@ export const ProductTypeUpdate: React.FC<ProductTypeUpdateProps> = ({
|
|||
productAttributes: formData.productAttributes.map(
|
||||
choice => choice.value,
|
||||
),
|
||||
taxCode: formData.taxType,
|
||||
taxClass: formData.taxClassId,
|
||||
variantAttributes: formData.variantAttributes.map(
|
||||
choice => choice.value,
|
||||
),
|
||||
|
@ -168,6 +169,8 @@ export const ProductTypeUpdate: React.FC<ProductTypeUpdateProps> = ({
|
|||
variables: { id },
|
||||
});
|
||||
|
||||
const { taxClasses, fetchMoreTaxClasses } = useTaxClassFetchMore();
|
||||
|
||||
const productType = data?.productType;
|
||||
|
||||
const closeModal = () => navigate(productTypeUrl(id), { replace: true });
|
||||
|
@ -321,7 +324,7 @@ export const ProductTypeUpdate: React.FC<ProductTypeUpdateProps> = ({
|
|||
saveButtonBarState={
|
||||
updateProductTypeOpts.status || updateProductAttributesOpts.status
|
||||
}
|
||||
taxTypes={maybe(() => data.taxTypes, [])}
|
||||
taxClasses={taxClasses ?? []}
|
||||
selectedVariantAttributes={selectedVariantAttributes}
|
||||
setSelectedVariantAttributes={setSelectedVariantAttributes}
|
||||
onAttributeAdd={type =>
|
||||
|
@ -398,6 +401,7 @@ export const ProductTypeUpdate: React.FC<ProductTypeUpdateProps> = ({
|
|||
</Button>
|
||||
),
|
||||
}}
|
||||
onFetchMoreTaxClasses={fetchMoreTaxClasses}
|
||||
/>
|
||||
{!dataLoading && (
|
||||
<>
|
||||
|
|
|
@ -28,7 +28,7 @@ import {
|
|||
SearchProductsQuery,
|
||||
SearchProductTypesQuery,
|
||||
SearchWarehousesQuery,
|
||||
TaxTypeFragment,
|
||||
TaxClassFragment,
|
||||
} from "@saleor/graphql";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import useStateFromProps from "@saleor/hooks/useStateFromProps";
|
||||
|
@ -75,7 +75,8 @@ interface ProductCreatePageProps {
|
|||
saveButtonBarState: ConfirmButtonTransitionState;
|
||||
weightUnit: string;
|
||||
warehouses: RelayToFlat<SearchWarehousesQuery["search"]>;
|
||||
taxTypes: TaxTypeFragment[];
|
||||
taxClasses: Array<Omit<TaxClassFragment, "countries">>;
|
||||
fetchMoreTaxClasses: FetchMoreProps;
|
||||
selectedProductType?: ProductTypeQuery["productType"];
|
||||
fetchCategories: (data: string) => void;
|
||||
fetchCollections: (data: string) => void;
|
||||
|
@ -117,7 +118,8 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
|
|||
referenceProducts = [],
|
||||
saveButtonBarState,
|
||||
warehouses,
|
||||
taxTypes,
|
||||
taxClasses,
|
||||
fetchMoreTaxClasses,
|
||||
selectedProductType,
|
||||
fetchProductTypes,
|
||||
weightUnit,
|
||||
|
@ -149,18 +151,18 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
|
|||
MultiAutocompleteChoiceType[]
|
||||
>([]);
|
||||
|
||||
const [selectedTaxType, setSelectedTaxType] = useStateFromProps(
|
||||
initial?.taxCode || null,
|
||||
const [selectedTaxClass, setSelectedTaxClass] = useStateFromProps(
|
||||
initial?.taxClassId ?? "",
|
||||
);
|
||||
|
||||
const categories = getChoices(categoryChoiceList);
|
||||
const collections = getChoices(collectionChoiceList);
|
||||
const productTypes = getChoices(productTypeChoiceList);
|
||||
const taxTypeChoices =
|
||||
taxTypes?.map(taxType => ({
|
||||
label: taxType.description,
|
||||
value: taxType.taxCode,
|
||||
})) || [];
|
||||
const taxClassChoices =
|
||||
taxClasses?.map(taxClass => ({
|
||||
label: taxClass.name,
|
||||
value: taxClass.id,
|
||||
})) ?? [];
|
||||
|
||||
const canOpenAssignReferencesAttributeDialog = !!assignReferencesAttributeId;
|
||||
|
||||
|
@ -194,9 +196,9 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
|
|||
selectedCollections={selectedCollections}
|
||||
setSelectedCategory={setSelectedCategory}
|
||||
setSelectedCollections={setSelectedCollections}
|
||||
setSelectedTaxType={setSelectedTaxType}
|
||||
setSelectedTaxClass={setSelectedTaxClass}
|
||||
setChannels={onChannelsChange}
|
||||
taxTypes={taxTypeChoices}
|
||||
taxClasses={taxClassChoices}
|
||||
warehouses={warehouses}
|
||||
currentChannels={currentChannels}
|
||||
fetchReferencePages={fetchReferencePages}
|
||||
|
@ -362,12 +364,12 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
|
|||
)}
|
||||
<CardSpacer />
|
||||
<ProductTaxes
|
||||
data={data}
|
||||
value={data.taxClassId}
|
||||
disabled={loading}
|
||||
onChange={change}
|
||||
onTaxTypeChange={handlers.selectTaxRate}
|
||||
selectedTaxTypeDisplayName={selectedTaxType}
|
||||
taxTypes={taxTypes}
|
||||
onChange={handlers.selectTaxClass}
|
||||
taxClassDisplayName={selectedTaxClass}
|
||||
taxClasses={taxClasses}
|
||||
onFetchMore={fetchMoreTaxClasses}
|
||||
/>
|
||||
</div>
|
||||
</Grid>
|
||||
|
|
|
@ -74,9 +74,7 @@ import { ProductStockFormsetData, ProductStockInput } from "../ProductStocks";
|
|||
|
||||
export interface ProductCreateFormData extends MetadataFormData {
|
||||
category: string;
|
||||
changeTaxCode: boolean;
|
||||
channelListings: ChannelData[];
|
||||
chargeTaxes: boolean;
|
||||
collections: string[];
|
||||
description: OutputData;
|
||||
isAvailable: boolean;
|
||||
|
@ -88,7 +86,6 @@ export interface ProductCreateFormData extends MetadataFormData {
|
|||
sku: string;
|
||||
slug: string;
|
||||
stockQuantity: number;
|
||||
taxCode: string;
|
||||
trackInventory: boolean;
|
||||
isPreorder: boolean;
|
||||
globalThreshold: string;
|
||||
|
@ -96,6 +93,7 @@ export interface ProductCreateFormData extends MetadataFormData {
|
|||
hasPreorderEndDate: boolean;
|
||||
preorderEndDateTime: string;
|
||||
weight: string;
|
||||
taxClassId: string;
|
||||
}
|
||||
export interface ProductCreateData extends ProductCreateFormData {
|
||||
attributes: AttributeInput[];
|
||||
|
@ -109,7 +107,7 @@ export interface ProductCreateHandlers
|
|||
| "selectCategory"
|
||||
| "selectCollection"
|
||||
| "selectProductType"
|
||||
| "selectTaxRate",
|
||||
| "selectTaxClass",
|
||||
FormChange
|
||||
>,
|
||||
Record<
|
||||
|
@ -150,14 +148,14 @@ export type UseProductCreateFormRenderProps = Omit<
|
|||
|
||||
export interface UseProductCreateFormOpts
|
||||
extends Record<
|
||||
"categories" | "collections" | "taxTypes",
|
||||
"categories" | "collections" | "taxClasses",
|
||||
SingleAutocompleteChoiceType[]
|
||||
> {
|
||||
setSelectedCategory: React.Dispatch<React.SetStateAction<string>>;
|
||||
setSelectedCollections: React.Dispatch<
|
||||
React.SetStateAction<MultiAutocompleteChoiceType[]>
|
||||
>;
|
||||
setSelectedTaxType: React.Dispatch<React.SetStateAction<string>>;
|
||||
setSelectedTaxClass: React.Dispatch<React.SetStateAction<string>>;
|
||||
setChannels: (channels: ChannelData[]) => void;
|
||||
selectedCollections: MultiAutocompleteChoiceType[];
|
||||
productTypes: RelayToFlat<SearchProductTypesQuery["search"]>;
|
||||
|
@ -194,9 +192,7 @@ function useProductCreateForm(
|
|||
const defaultInitialFormData: ProductCreateFormData &
|
||||
Record<"productType", string> = {
|
||||
category: "",
|
||||
changeTaxCode: false,
|
||||
channelListings: opts.currentChannels,
|
||||
chargeTaxes: false,
|
||||
collections: [],
|
||||
description: null,
|
||||
isAvailable: false,
|
||||
|
@ -210,7 +206,7 @@ function useProductCreateForm(
|
|||
sku: "",
|
||||
slug: "",
|
||||
stockQuantity: null,
|
||||
taxCode: null,
|
||||
taxClassId: "",
|
||||
trackInventory: false,
|
||||
weight: "",
|
||||
globalSoldUnits: 0,
|
||||
|
@ -331,10 +327,10 @@ function useProductCreateForm(
|
|||
triggerChange();
|
||||
stocks.remove(id);
|
||||
};
|
||||
const handleTaxTypeSelect = createSingleAutocompleteSelectHandler(
|
||||
const handleTaxClassSelect = createSingleAutocompleteSelectHandler(
|
||||
handleChange,
|
||||
opts.setSelectedTaxType,
|
||||
opts.taxTypes,
|
||||
opts.setSelectedTaxClass,
|
||||
opts.taxClasses,
|
||||
);
|
||||
const changeMetadata = makeMetadataChangeHandler(handleChange);
|
||||
const handleChannelsChange = createChannelsChangeHandler(
|
||||
|
@ -478,7 +474,7 @@ function useProductCreateForm(
|
|||
selectCategory: handleCategorySelect,
|
||||
selectCollection: handleCollectionSelect,
|
||||
selectProductType: handleProductTypeSelect,
|
||||
selectTaxRate: handleTaxTypeSelect,
|
||||
selectTaxClass: handleTaxClassSelect,
|
||||
},
|
||||
submit,
|
||||
isSaveDisabled,
|
||||
|
|
|
@ -1,114 +1,70 @@
|
|||
import { Card, CardContent } from "@material-ui/core";
|
||||
import CardTitle from "@saleor/components/CardTitle";
|
||||
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
|
||||
import Hr from "@saleor/components/Hr";
|
||||
import SingleAutocompleteSelectField from "@saleor/components/SingleAutocompleteSelectField";
|
||||
import { TaxTypeFragment } from "@saleor/graphql";
|
||||
import { FormChange } from "@saleor/hooks/useForm";
|
||||
import { TaxClassFragment } from "@saleor/graphql";
|
||||
import { sectionNames } from "@saleor/intl";
|
||||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
import { taxesMessages } from "@saleor/taxes/messages";
|
||||
import { FetchMoreProps } from "@saleor/types";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
export interface ProductTaxesProps {
|
||||
data: {
|
||||
changeTaxCode: boolean;
|
||||
chargeTaxes: boolean;
|
||||
taxCode: string;
|
||||
};
|
||||
import { ProductCreateFormData } from "../ProductCreatePage";
|
||||
|
||||
interface ProductTaxesProps {
|
||||
value: string;
|
||||
taxClassDisplayName: string;
|
||||
taxClasses: Array<Omit<TaxClassFragment, "countries">>;
|
||||
disabled: boolean;
|
||||
selectedTaxTypeDisplayName: string;
|
||||
taxTypes: TaxTypeFragment[];
|
||||
onChange: FormChange;
|
||||
onTaxTypeChange: FormChange;
|
||||
onChange: (event: React.ChangeEvent<any>) => void;
|
||||
onFetchMore: FetchMoreProps;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(
|
||||
theme => ({
|
||||
content: {
|
||||
paddingTop: theme.spacing(2),
|
||||
},
|
||||
hr: {
|
||||
margin: theme.spacing(2, 0),
|
||||
},
|
||||
select: {
|
||||
margin: theme.spacing(2, 0),
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: "ProductTaxes",
|
||||
root: {
|
||||
overflow: "visible",
|
||||
},
|
||||
},
|
||||
{ name: "ProductTaxes" },
|
||||
);
|
||||
|
||||
const ProductTaxes: React.FC<ProductTaxesProps> = ({
|
||||
data,
|
||||
disabled,
|
||||
selectedTaxTypeDisplayName,
|
||||
taxTypes,
|
||||
onChange,
|
||||
onTaxTypeChange,
|
||||
}) => {
|
||||
const ProductTaxes: React.FC<ProductTaxesProps> = props => {
|
||||
const {
|
||||
value,
|
||||
disabled,
|
||||
taxClasses,
|
||||
taxClassDisplayName,
|
||||
onChange,
|
||||
onFetchMore,
|
||||
} = props;
|
||||
const classes = useStyles(props);
|
||||
|
||||
const intl = useIntl();
|
||||
const classes = useStyles({});
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<Card className={classes.root}>
|
||||
<CardTitle title={intl.formatMessage(sectionNames.taxes)} />
|
||||
<CardContent className={classes.content}>
|
||||
<ControlledCheckbox
|
||||
checked={data.changeTaxCode}
|
||||
<CardContent>
|
||||
<SingleAutocompleteSelectField
|
||||
disabled={disabled}
|
||||
data-test-id="override-tax-type"
|
||||
label={intl.formatMessage({
|
||||
id: "iYH3Y7",
|
||||
defaultMessage: "Override the product type's tax rate",
|
||||
description: "checkbox",
|
||||
})}
|
||||
name="changeTaxCode"
|
||||
displayValue={taxClassDisplayName}
|
||||
label={intl.formatMessage(taxesMessages.taxClass)}
|
||||
name={"taxClassId" as keyof ProductCreateFormData}
|
||||
onChange={onChange}
|
||||
value={value}
|
||||
choices={taxClasses.map(choice => ({
|
||||
label: choice.name,
|
||||
value: choice.id,
|
||||
}))}
|
||||
InputProps={{
|
||||
autoComplete: "off",
|
||||
}}
|
||||
{...onFetchMore}
|
||||
/>
|
||||
<Hr className={classes.hr} />
|
||||
<ControlledCheckbox
|
||||
checked={data.chargeTaxes}
|
||||
disabled={disabled}
|
||||
data-test-id="charge-taxes"
|
||||
label={intl.formatMessage({
|
||||
id: "TfY/Pi",
|
||||
defaultMessage: "Charge taxes on this product",
|
||||
description: "checkbox",
|
||||
})}
|
||||
name="chargeTaxes"
|
||||
onChange={onChange}
|
||||
/>
|
||||
{data.changeTaxCode && (
|
||||
<SingleAutocompleteSelectField
|
||||
className={classes.select}
|
||||
disabled={disabled}
|
||||
displayValue={selectedTaxTypeDisplayName}
|
||||
data-test-id="select-tax-type"
|
||||
label={intl.formatMessage({
|
||||
id: "CdIHMu",
|
||||
defaultMessage: "Tax Rate",
|
||||
description: "select tax ratte",
|
||||
})}
|
||||
name="taxCode"
|
||||
onChange={onTaxTypeChange}
|
||||
value={data.taxCode}
|
||||
choices={
|
||||
taxTypes?.map(taxType => ({
|
||||
label: taxType.description,
|
||||
value: taxType.taxCode,
|
||||
})) || []
|
||||
}
|
||||
InputProps={{
|
||||
autoComplete: "off",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
ProductTaxes.displayName = "ProductTaxes";
|
||||
export default ProductTaxes;
|
||||
|
|
|
@ -3,7 +3,6 @@ import { channelsList } from "@saleor/channels/fixtures";
|
|||
import { collections } from "@saleor/collections/fixtures";
|
||||
import { fetchMoreProps, limits } from "@saleor/fixtures";
|
||||
import { product as productFixture } from "@saleor/products/fixtures";
|
||||
import { taxTypes } from "@saleor/storybook/stories/taxes/fixtures";
|
||||
import { warehouseList } from "@saleor/warehouses/fixtures";
|
||||
import Wrapper from "@test/wrapper";
|
||||
import { configure, mount } from "enzyme";
|
||||
|
@ -14,6 +13,7 @@ import ProductUpdatePage, { ProductUpdatePageProps } from "./ProductUpdatePage";
|
|||
const product = productFixture(placeholderImage);
|
||||
|
||||
import * as _useNavigator from "@saleor/hooks/useNavigator";
|
||||
import { taxClasses } from "@saleor/taxes/fixtures";
|
||||
import Adapter from "@wojtekmaj/enzyme-adapter-react-17";
|
||||
import { act } from "react-dom/test-utils";
|
||||
import { MemoryRouter } from "react-router-dom";
|
||||
|
@ -82,7 +82,8 @@ const props: ProductUpdatePageProps = {
|
|||
referencePages: [],
|
||||
referenceProducts: [],
|
||||
saveButtonBarState: "default",
|
||||
taxTypes,
|
||||
taxClasses,
|
||||
fetchMoreTaxClasses: undefined,
|
||||
variants: product.variants,
|
||||
warehouses: warehouseList,
|
||||
attributeValues: [],
|
||||
|
|
|
@ -35,7 +35,7 @@ import {
|
|||
SearchCollectionsQuery,
|
||||
SearchPagesQuery,
|
||||
SearchProductsQuery,
|
||||
TaxTypeFragment,
|
||||
TaxClassFragment,
|
||||
WarehouseFragment,
|
||||
} from "@saleor/graphql";
|
||||
import { SubmitPromise } from "@saleor/hooks/useForm";
|
||||
|
@ -89,7 +89,8 @@ export interface ProductUpdatePageProps {
|
|||
header: string;
|
||||
saveButtonBarState: ConfirmButtonTransitionState;
|
||||
warehouses: WarehouseFragment[];
|
||||
taxTypes: TaxTypeFragment[];
|
||||
taxClasses: Array<Omit<TaxClassFragment, "countries">>;
|
||||
fetchMoreTaxClasses: FetchMoreProps;
|
||||
referencePages?: RelayToFlat<SearchPagesQuery["search"]>;
|
||||
referenceProducts?: RelayToFlat<SearchProductsQuery["search"]>;
|
||||
assignReferencesAttributeId?: string;
|
||||
|
@ -143,7 +144,8 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
|
|||
saveButtonBarState,
|
||||
variants,
|
||||
warehouses,
|
||||
taxTypes,
|
||||
taxClasses,
|
||||
fetchMoreTaxClasses,
|
||||
referencePages = [],
|
||||
referenceProducts = [],
|
||||
onDelete,
|
||||
|
@ -184,17 +186,17 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
|
|||
getChoices(maybe(() => product.collections, [])),
|
||||
);
|
||||
|
||||
const [selectedTaxType, setSelectedTaxType] = useStateFromProps(
|
||||
product?.taxType.description,
|
||||
const [selectedTaxClass, setSelectedTaxClass] = useStateFromProps(
|
||||
product?.taxClass?.name ?? "",
|
||||
);
|
||||
|
||||
const categories = getChoices(categoryChoiceList);
|
||||
const collections = getChoices(collectionChoiceList);
|
||||
const hasVariants = product?.productType?.hasVariants;
|
||||
const taxTypeChoices =
|
||||
taxTypes?.map(taxType => ({
|
||||
label: taxType.description,
|
||||
value: taxType.taxCode,
|
||||
const taxClassesChoices =
|
||||
taxClasses?.map(taxClass => ({
|
||||
label: taxClass.name,
|
||||
value: taxClass.id,
|
||||
})) || [];
|
||||
|
||||
const canOpenAssignReferencesAttributeDialog = !!assignReferencesAttributeId;
|
||||
|
@ -250,8 +252,8 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
|
|||
selectedCollections={selectedCollections}
|
||||
setSelectedCategory={setSelectedCategory}
|
||||
setSelectedCollections={setSelectedCollections}
|
||||
setSelectedTaxType={setSelectedTaxType}
|
||||
taxTypes={taxTypeChoices}
|
||||
setSelectedTaxClass={setSelectedTaxClass}
|
||||
taxClasses={taxClassesChoices}
|
||||
warehouses={warehouses}
|
||||
hasVariants={hasVariants}
|
||||
referencePages={referencePages}
|
||||
|
@ -420,12 +422,12 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
|
|||
/>
|
||||
<CardSpacer />
|
||||
<ProductTaxes
|
||||
data={data}
|
||||
value={data.taxClassId}
|
||||
disabled={disabled}
|
||||
selectedTaxTypeDisplayName={selectedTaxType}
|
||||
taxTypes={taxTypes}
|
||||
onChange={change}
|
||||
onTaxTypeChange={handlers.selectTaxRate}
|
||||
onChange={handlers.selectTaxClass}
|
||||
taxClassDisplayName={selectedTaxClass}
|
||||
taxClasses={taxClasses}
|
||||
onFetchMore={fetchMoreTaxClasses}
|
||||
/>
|
||||
</div>
|
||||
</Grid>
|
||||
|
|
|
@ -163,10 +163,10 @@ function useProductUpdateForm(
|
|||
attributes.data,
|
||||
triggerChange,
|
||||
);
|
||||
const handleTaxTypeSelect = createSingleAutocompleteSelectHandler(
|
||||
const handleTaxClassSelect = createSingleAutocompleteSelectHandler(
|
||||
handleChange,
|
||||
opts.setSelectedTaxType,
|
||||
opts.taxTypes,
|
||||
opts.setSelectedTaxClass,
|
||||
opts.taxClasses,
|
||||
);
|
||||
const changeMetadata = makeMetadataChangeHandler(handleChange);
|
||||
|
||||
|
@ -295,7 +295,7 @@ function useProductUpdateForm(
|
|||
selectAttributeReference: handleAttributeReferenceChange,
|
||||
selectCategory: handleCategorySelect,
|
||||
selectCollection: handleCollectionSelect,
|
||||
selectTaxRate: handleTaxTypeSelect,
|
||||
selectTaxClass: handleTaxClassSelect,
|
||||
updateChannelList: handleChannelListUpdate,
|
||||
},
|
||||
submit,
|
||||
|
|
|
@ -35,8 +35,7 @@ import { ProductChannelsListingDialogSubmit } from "./ProductChannelsListingsDia
|
|||
|
||||
export interface ProductUpdateFormData extends MetadataFormData {
|
||||
category: string | null;
|
||||
changeTaxCode: boolean;
|
||||
chargeTaxes: boolean;
|
||||
taxClassId: string;
|
||||
collections: string[];
|
||||
isAvailable: boolean;
|
||||
name: string;
|
||||
|
@ -45,7 +44,6 @@ export interface ProductUpdateFormData extends MetadataFormData {
|
|||
seoDescription: string;
|
||||
seoTitle: string;
|
||||
sku: string;
|
||||
taxCode: string;
|
||||
trackInventory: boolean;
|
||||
isPreorder: boolean;
|
||||
globalThreshold: string;
|
||||
|
@ -85,7 +83,7 @@ export interface ProductUpdateHandlers
|
|||
| "changeMetadata"
|
||||
| "selectCategory"
|
||||
| "selectCollection"
|
||||
| "selectTaxRate",
|
||||
| "selectTaxClass",
|
||||
FormChange
|
||||
>,
|
||||
Record<
|
||||
|
@ -119,14 +117,14 @@ export type UseProductUpdateFormRenderProps = Omit<
|
|||
|
||||
export interface UseProductUpdateFormOpts
|
||||
extends Record<
|
||||
"categories" | "collections" | "taxTypes",
|
||||
"categories" | "collections" | "taxClasses",
|
||||
SingleAutocompleteChoiceType[]
|
||||
> {
|
||||
setSelectedCategory: React.Dispatch<React.SetStateAction<string>>;
|
||||
setSelectedCollections: React.Dispatch<
|
||||
React.SetStateAction<MultiAutocompleteChoiceType[]>
|
||||
>;
|
||||
setSelectedTaxType: React.Dispatch<React.SetStateAction<string>>;
|
||||
setSelectedTaxClass: React.Dispatch<React.SetStateAction<string>>;
|
||||
selectedCollections: MultiAutocompleteChoiceType[];
|
||||
warehouses: RelayToFlat<SearchWarehousesQuery["search"]>;
|
||||
hasVariants: boolean;
|
||||
|
|
|
@ -464,10 +464,10 @@ export const product: (
|
|||
},
|
||||
},
|
||||
],
|
||||
taxType: {
|
||||
__typename: "TaxType",
|
||||
description: "standard",
|
||||
taxCode: "standard",
|
||||
taxClass: {
|
||||
__typename: "TaxClass",
|
||||
name: "standard",
|
||||
id: "standard",
|
||||
},
|
||||
variantAttributes: [
|
||||
{
|
||||
|
@ -591,10 +591,10 @@ export const product: (
|
|||
seoTitle: "Seo title",
|
||||
sku: "59661-34207",
|
||||
slug: "Borders",
|
||||
taxType: {
|
||||
__typename: "TaxType",
|
||||
description: "standard",
|
||||
taxCode: "standard",
|
||||
taxClass: {
|
||||
__typename: "TaxClass",
|
||||
name: "standard",
|
||||
id: "standard",
|
||||
},
|
||||
thumbnail: { __typename: "Image" as "Image", url: placeholderImage },
|
||||
url: "/example-url",
|
||||
|
|
|
@ -114,9 +114,6 @@ export const productDetailsQuery = gql`
|
|||
product(id: $id, channel: $channel) {
|
||||
...Product
|
||||
}
|
||||
taxTypes {
|
||||
...TaxType
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
|
@ -149,8 +146,9 @@ export const productTypeQuery = gql`
|
|||
...AttributeValueList
|
||||
}
|
||||
}
|
||||
taxType {
|
||||
...TaxType
|
||||
taxClass {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -201,8 +201,7 @@ export function getProductUpdatePageFormData(
|
|||
|
||||
return {
|
||||
category: maybe(() => product.category.id, ""),
|
||||
changeTaxCode: !!product?.taxType.taxCode,
|
||||
chargeTaxes: maybe(() => product.chargeTaxes, false),
|
||||
taxClassId: product?.taxClass?.id,
|
||||
collections: maybe(
|
||||
() => product.collections.map(collection => collection.id),
|
||||
[],
|
||||
|
@ -224,7 +223,6 @@ export function getProductUpdatePageFormData(
|
|||
"",
|
||||
),
|
||||
slug: product?.slug || "",
|
||||
taxCode: product?.taxType.taxCode,
|
||||
trackInventory: !!variant?.trackInventory,
|
||||
weight: product?.weight?.value.toString() || "",
|
||||
isPreorder: !!variant?.preorder || false,
|
||||
|
|
|
@ -16,7 +16,6 @@ import {
|
|||
useProductDeleteMutation,
|
||||
useProductTypeQuery,
|
||||
useProductVariantChannelListingUpdateMutation,
|
||||
useTaxTypeListQuery,
|
||||
useUpdateMetadataMutation,
|
||||
useUpdatePrivateMetadataMutation,
|
||||
useVariantCreateMutation,
|
||||
|
@ -41,6 +40,7 @@ import useCollectionSearch from "@saleor/searches/useCollectionSearch";
|
|||
import usePageSearch from "@saleor/searches/usePageSearch";
|
||||
import useProductSearch from "@saleor/searches/useProductSearch";
|
||||
import useProductTypeSearch from "@saleor/searches/useProductTypeSearch";
|
||||
import { useTaxClassFetchMore } from "@saleor/taxes/utils/useTaxClassFetchMore";
|
||||
import { getProductErrorMessage } from "@saleor/utils/errors";
|
||||
import useAttributeValueSearchHandler from "@saleor/utils/handlers/attributeValueSearchHandler";
|
||||
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
|
||||
|
@ -123,7 +123,7 @@ export const ProductCreateView: React.FC<ProductCreateProps> = ({ params }) => {
|
|||
} = useAttributeValueSearchHandler(DEFAULT_INITIAL_SEARCH_DATA);
|
||||
const [updateMetadata] = useUpdateMetadataMutation({});
|
||||
const [updatePrivateMetadata] = useUpdatePrivateMetadataMutation({});
|
||||
const taxTypes = useTaxTypeListQuery({});
|
||||
const { taxClasses, fetchMoreTaxClasses } = useTaxClassFetchMore();
|
||||
const { data: selectedProductType } = useProductTypeQuery({
|
||||
variables: {
|
||||
id: selectedProductTypeId,
|
||||
|
@ -355,7 +355,8 @@ export const ProductCreateView: React.FC<ProductCreateProps> = ({ params }) => {
|
|||
fetchMoreCollections={fetchMoreCollections}
|
||||
fetchMoreProductTypes={fetchMoreProductTypes}
|
||||
warehouses={mapEdgesToItems(warehouses?.data?.warehouses) || []}
|
||||
taxTypes={taxTypes.data?.taxTypes || []}
|
||||
taxClasses={taxClasses ?? []}
|
||||
fetchMoreTaxClasses={fetchMoreTaxClasses}
|
||||
weightUnit={shop?.defaultWeightUnit}
|
||||
openChannelsModal={handleChannelsModalOpen}
|
||||
onChannelsChange={setCurrentChannels}
|
||||
|
|
|
@ -113,7 +113,6 @@ export function createHandler(
|
|||
updatedFileAttributes,
|
||||
}),
|
||||
category: formData.category,
|
||||
chargeTaxes: formData.chargeTaxes,
|
||||
collections: formData.collections,
|
||||
description: getParsedDataForJsonStringField(formData.description),
|
||||
name: formData.name,
|
||||
|
@ -124,7 +123,7 @@ export function createHandler(
|
|||
title: formData.seoTitle,
|
||||
},
|
||||
slug: formData.slug,
|
||||
taxCode: formData.changeTaxCode ? formData.taxCode : undefined,
|
||||
taxClass: formData.taxClassId,
|
||||
weight: weight(formData.weight),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -28,12 +28,13 @@ import useCategorySearch from "@saleor/searches/useCategorySearch";
|
|||
import useCollectionSearch from "@saleor/searches/useCollectionSearch";
|
||||
import usePageSearch from "@saleor/searches/usePageSearch";
|
||||
import useProductSearch from "@saleor/searches/useProductSearch";
|
||||
import { useTaxClassFetchMore } from "@saleor/taxes/utils/useTaxClassFetchMore";
|
||||
import { getProductErrorMessage } from "@saleor/utils/errors";
|
||||
import useAttributeValueSearchHandler from "@saleor/utils/handlers/attributeValueSearchHandler";
|
||||
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
|
||||
import { mapEdgesToItems } from "@saleor/utils/maps";
|
||||
import React from "react";
|
||||
import { defineMessages, FormattedMessage, useIntl } from "react-intl";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import { getMutationState } from "../../../misc";
|
||||
import ProductUpdatePage from "../../components/ProductUpdatePage";
|
||||
|
@ -49,30 +50,7 @@ import {
|
|||
createImageUploadHandler,
|
||||
} from "./handlers";
|
||||
import { useProductUpdateHandler } from "./handlers/useProductUpdateHandler";
|
||||
|
||||
const messages = defineMessages({
|
||||
deleteProductDialogTitle: {
|
||||
id: "TWVx7O",
|
||||
defaultMessage: "Delete Product",
|
||||
description: "delete product dialog title",
|
||||
},
|
||||
deleteProductDialogSubtitle: {
|
||||
id: "ZHF4Z9",
|
||||
defaultMessage: "Are you sure you want to delete {name}?",
|
||||
description: "delete product dialog subtitle",
|
||||
},
|
||||
deleteVariantDialogTitle: {
|
||||
id: "6iw4VR",
|
||||
defaultMessage: "Delete Product Variants",
|
||||
description: "delete variant dialog title",
|
||||
},
|
||||
deleteVariantDialogSubtitle: {
|
||||
id: "ukdRUv",
|
||||
defaultMessage:
|
||||
"{counter,plural,one{Are you sure you want to delete this variant?} other{Are you sure you want to delete {displayQuantity} variants?}}",
|
||||
description: "delete variant dialog subtitle",
|
||||
},
|
||||
});
|
||||
import { productUpdatePageMessages as messages } from "./messages";
|
||||
|
||||
interface ProductUpdateProps {
|
||||
id: string;
|
||||
|
@ -310,6 +288,8 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
|
|||
onFetchMore: loadMoreAttributeValues,
|
||||
};
|
||||
|
||||
const { taxClasses, fetchMoreTaxClasses } = useTaxClassFetchMore();
|
||||
|
||||
if (product === null) {
|
||||
return <NotFoundPage onBack={handleBack} />;
|
||||
}
|
||||
|
@ -339,7 +319,8 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
|
|||
placeholderImage={placeholderImg}
|
||||
product={product}
|
||||
warehouses={warehouses}
|
||||
taxTypes={data?.taxTypes}
|
||||
taxClasses={taxClasses ?? []}
|
||||
fetchMoreTaxClasses={fetchMoreTaxClasses}
|
||||
variants={product?.variants}
|
||||
onDelete={() => openModal("remove")}
|
||||
onImageReorder={handleImageReorder}
|
||||
|
|
|
@ -34,7 +34,6 @@ export function getProductUpdateVariables(
|
|||
updatedFileAttributes,
|
||||
}),
|
||||
category: data.category,
|
||||
chargeTaxes: data.chargeTaxes,
|
||||
collections: data.collections,
|
||||
description: getParsedDataForJsonStringField(data.description),
|
||||
name: data.name,
|
||||
|
@ -44,7 +43,7 @@ export function getProductUpdateVariables(
|
|||
title: data.seoTitle,
|
||||
},
|
||||
slug: data.slug,
|
||||
taxCode: data.changeTaxCode ? data.taxCode : null,
|
||||
taxClass: data.taxClassId,
|
||||
},
|
||||
firstValues: VALUES_PAGINATE_BY,
|
||||
};
|
||||
|
|
25
src/products/views/ProductUpdate/messages.ts
Normal file
25
src/products/views/ProductUpdate/messages.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
import { defineMessages } from "react-intl";
|
||||
|
||||
export const productUpdatePageMessages = defineMessages({
|
||||
deleteProductDialogTitle: {
|
||||
id: "TWVx7O",
|
||||
defaultMessage: "Delete Product",
|
||||
description: "delete product dialog title",
|
||||
},
|
||||
deleteProductDialogSubtitle: {
|
||||
id: "ZHF4Z9",
|
||||
defaultMessage: "Are you sure you want to delete {name}?",
|
||||
description: "delete product dialog subtitle",
|
||||
},
|
||||
deleteVariantDialogTitle: {
|
||||
id: "6iw4VR",
|
||||
defaultMessage: "Delete Product Variants",
|
||||
description: "delete variant dialog title",
|
||||
},
|
||||
deleteVariantDialogSubtitle: {
|
||||
id: "ukdRUv",
|
||||
defaultMessage:
|
||||
"{counter,plural,one{Are you sure you want to delete this variant?} other{Are you sure you want to delete {displayQuantity} variants?}}",
|
||||
description: "delete variant dialog subtitle",
|
||||
},
|
||||
});
|
|
@ -0,0 +1,71 @@
|
|||
import { Card, CardContent } from "@material-ui/core";
|
||||
import CardTitle from "@saleor/components/CardTitle";
|
||||
import SingleAutocompleteSelectField from "@saleor/components/SingleAutocompleteSelectField";
|
||||
import { TaxClassFragment } from "@saleor/graphql";
|
||||
import { sectionNames } from "@saleor/intl";
|
||||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
import { taxesMessages } from "@saleor/taxes/messages";
|
||||
import { FetchMoreProps } from "@saleor/types";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import { ShippingZoneRateUpdateFormData } from "../ShippingZoneRatesPage/types";
|
||||
|
||||
interface ShippingMethodTaxesProps {
|
||||
value: string;
|
||||
taxClassDisplayName: string;
|
||||
taxClasses: Array<Omit<TaxClassFragment, "countries">>;
|
||||
disabled: boolean;
|
||||
onChange: (event: React.ChangeEvent<any>) => void;
|
||||
onFetchMore: FetchMoreProps;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(
|
||||
{
|
||||
root: {
|
||||
overflow: "visible",
|
||||
},
|
||||
},
|
||||
{ name: "ShippingMethodTaxes" },
|
||||
);
|
||||
|
||||
const ShippingMethodTaxes: React.FC<ShippingMethodTaxesProps> = props => {
|
||||
const {
|
||||
value,
|
||||
disabled,
|
||||
taxClasses,
|
||||
taxClassDisplayName,
|
||||
onChange,
|
||||
onFetchMore,
|
||||
} = props;
|
||||
const classes = useStyles(props);
|
||||
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<Card className={classes.root}>
|
||||
<CardTitle title={intl.formatMessage(sectionNames.taxes)} />
|
||||
<CardContent>
|
||||
<SingleAutocompleteSelectField
|
||||
emptyOption
|
||||
disabled={disabled}
|
||||
displayValue={taxClassDisplayName}
|
||||
label={intl.formatMessage(taxesMessages.taxClass)}
|
||||
name={"taxClassId" as keyof ShippingZoneRateUpdateFormData}
|
||||
onChange={onChange}
|
||||
value={value}
|
||||
choices={taxClasses.map(choice => ({
|
||||
label: choice.name,
|
||||
value: choice.id,
|
||||
}))}
|
||||
InputProps={{
|
||||
autoComplete: "off",
|
||||
}}
|
||||
{...onFetchMore}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
ShippingMethodTaxes.displayName = "ShippingMethodTaxes";
|
||||
export default ShippingMethodTaxes;
|
2
src/shipping/components/ShippingMethodTaxes/index.ts
Normal file
2
src/shipping/components/ShippingMethodTaxes/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./ShippingMethodTaxes";
|
||||
export * from "./ShippingMethodTaxes";
|
|
@ -3,6 +3,7 @@ import {
|
|||
ShippingMethodTypeEnum,
|
||||
} from "@saleor/graphql";
|
||||
import Decorator from "@saleor/storybook//Decorator";
|
||||
import { taxClasses } from "@saleor/taxes/fixtures";
|
||||
import { storiesOf } from "@storybook/react";
|
||||
import React from "react";
|
||||
|
||||
|
@ -73,6 +74,8 @@ const props: ShippingZoneRatesCreatePageProps = {
|
|||
saveButtonBarState: "default",
|
||||
shippingChannels: defaultChannels,
|
||||
variant: ShippingMethodTypeEnum.PRICE,
|
||||
taxClasses,
|
||||
fetchMoreTaxClasses: undefined,
|
||||
};
|
||||
|
||||
storiesOf("Shipping / ShippingZoneRatesCreatePage page", module)
|
||||
|
|
|
@ -14,22 +14,26 @@ import {
|
|||
ShippingErrorFragment,
|
||||
ShippingMethodTypeEnum,
|
||||
ShippingMethodTypeFragment,
|
||||
TaxClassFragment,
|
||||
} from "@saleor/graphql";
|
||||
import useForm, { SubmitPromise } from "@saleor/hooks/useForm";
|
||||
import useHandleFormSubmit from "@saleor/hooks/useHandleFormSubmit";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import { ConfirmButtonTransitionState } from "@saleor/macaw-ui";
|
||||
import { validatePrice } from "@saleor/products/utils/validation";
|
||||
import { handleTaxClassChange } from "@saleor/productTypes/handlers";
|
||||
import OrderValue from "@saleor/shipping/components/OrderValue";
|
||||
import OrderWeight from "@saleor/shipping/components/OrderWeight";
|
||||
import PricingCard from "@saleor/shipping/components/PricingCard";
|
||||
import ShippingRateInfo from "@saleor/shipping/components/ShippingRateInfo";
|
||||
import { createChannelsChangeHandler } from "@saleor/shipping/handlers";
|
||||
import { FetchMoreProps } from "@saleor/types";
|
||||
import { RichTextContext } from "@saleor/utils/richText/context";
|
||||
import useRichText from "@saleor/utils/richText/useRichText";
|
||||
import React, { FormEventHandler } from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import ShippingMethodTaxes from "../ShippingMethodTaxes";
|
||||
import ShippingZonePostalCodes from "../ShippingZonePostalCodes";
|
||||
import { ShippingZoneRateCommonFormData } from "../ShippingZoneRatesPage/types";
|
||||
|
||||
|
@ -52,6 +56,8 @@ export interface ShippingZoneRatesCreatePageProps extends WithFormId {
|
|||
onChannelsChange: (data: ChannelShippingData[]) => void;
|
||||
openChannelsModal: () => void;
|
||||
variant: ShippingMethodTypeEnum;
|
||||
taxClasses: Array<Omit<TaxClassFragment, "countries">>;
|
||||
fetchMoreTaxClasses: FetchMoreProps;
|
||||
}
|
||||
|
||||
export const ShippingZoneRatesCreatePage: React.FC<ShippingZoneRatesCreatePageProps> = ({
|
||||
|
@ -72,6 +78,8 @@ export const ShippingZoneRatesCreatePage: React.FC<ShippingZoneRatesCreatePagePr
|
|||
variant,
|
||||
postalCodes,
|
||||
formId,
|
||||
taxClasses,
|
||||
fetchMoreTaxClasses,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const navigate = useNavigator();
|
||||
|
@ -87,8 +95,11 @@ export const ShippingZoneRatesCreatePage: React.FC<ShippingZoneRatesCreatePagePr
|
|||
description: null,
|
||||
orderValueRestricted: true,
|
||||
type: null,
|
||||
taxClassId: "",
|
||||
};
|
||||
|
||||
const [taxClassDisplayName, setTaxClassDisplayName] = React.useState("");
|
||||
|
||||
const {
|
||||
change,
|
||||
data: formData,
|
||||
|
@ -207,6 +218,22 @@ export const ShippingZoneRatesCreatePage: React.FC<ShippingZoneRatesCreatePagePr
|
|||
channelsList={data.channelListings}
|
||||
openModal={openChannelsModal}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<ShippingMethodTaxes
|
||||
value={formData.taxClassId}
|
||||
taxClassDisplayName={taxClassDisplayName}
|
||||
taxClasses={taxClasses}
|
||||
disabled={false}
|
||||
onChange={event =>
|
||||
handleTaxClassChange(
|
||||
event,
|
||||
taxClasses,
|
||||
change,
|
||||
setTaxClassDisplayName,
|
||||
)
|
||||
}
|
||||
onFetchMore={fetchMoreTaxClasses}
|
||||
/>
|
||||
</div>
|
||||
</Grid>
|
||||
<Savebar
|
||||
|
|
|
@ -2,6 +2,7 @@ import { ShippingMethodTypeEnum } from "@saleor/graphql";
|
|||
import { shippingZone } from "@saleor/shipping/fixtures";
|
||||
import Decorator from "@saleor/storybook//Decorator";
|
||||
import { PaginatorContextDecorator } from "@saleor/storybook/PaginatorContextDecorator";
|
||||
import { taxClasses } from "@saleor/taxes/fixtures";
|
||||
import { storiesOf } from "@storybook/react";
|
||||
import React from "react";
|
||||
|
||||
|
@ -65,6 +66,8 @@ const props: ShippingZoneRatesPageProps = {
|
|||
toolbar: () => undefined,
|
||||
variant: ShippingMethodTypeEnum.PRICE,
|
||||
formId: Symbol(),
|
||||
taxClasses,
|
||||
fetchMoreTaxClasses: undefined,
|
||||
};
|
||||
|
||||
storiesOf("Views / Shipping / Shipping rate", module)
|
||||
|
|
|
@ -16,19 +16,22 @@ import {
|
|||
ShippingMethodTypeEnum,
|
||||
ShippingMethodTypeFragment,
|
||||
ShippingZoneQuery,
|
||||
TaxClassFragment,
|
||||
} from "@saleor/graphql";
|
||||
import useForm, { SubmitPromise } from "@saleor/hooks/useForm";
|
||||
import useHandleFormSubmit from "@saleor/hooks/useHandleFormSubmit";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import { useStateUpdate } from "@saleor/hooks/useStateUpdate";
|
||||
import { ConfirmButtonTransitionState } from "@saleor/macaw-ui";
|
||||
import { validatePrice } from "@saleor/products/utils/validation";
|
||||
import { handleTaxClassChange } from "@saleor/productTypes/handlers";
|
||||
import OrderValue from "@saleor/shipping/components/OrderValue";
|
||||
import OrderWeight from "@saleor/shipping/components/OrderWeight";
|
||||
import PricingCard from "@saleor/shipping/components/PricingCard";
|
||||
import ShippingMethodProducts from "@saleor/shipping/components/ShippingMethodProducts";
|
||||
import ShippingRateInfo from "@saleor/shipping/components/ShippingRateInfo";
|
||||
import { createChannelsChangeHandler } from "@saleor/shipping/handlers";
|
||||
import { ListActions, ListProps } from "@saleor/types";
|
||||
import { FetchMoreProps, ListActions, ListProps } from "@saleor/types";
|
||||
import { mapEdgesToItems, mapMetadataItemToInput } from "@saleor/utils/maps";
|
||||
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
|
||||
import { RichTextContext } from "@saleor/utils/richText/context";
|
||||
|
@ -36,6 +39,7 @@ import useRichText from "@saleor/utils/richText/useRichText";
|
|||
import React, { FormEventHandler } from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import ShippingMethodTaxes from "../ShippingMethodTaxes";
|
||||
import ShippingZonePostalCodes from "../ShippingZonePostalCodes";
|
||||
import { ShippingZoneRateUpdateFormData } from "./types";
|
||||
|
||||
|
@ -66,6 +70,8 @@ export interface ShippingZoneRatesPageProps
|
|||
onProductAssign: () => void;
|
||||
onProductUnassign: (ids: string[]) => void;
|
||||
variant: ShippingMethodTypeEnum;
|
||||
taxClasses: Array<Omit<TaxClassFragment, "countries">>;
|
||||
fetchMoreTaxClasses: FetchMoreProps;
|
||||
}
|
||||
|
||||
export const ShippingZoneRatesPage: React.FC<ShippingZoneRatesPageProps> = ({
|
||||
|
@ -89,6 +95,8 @@ export const ShippingZoneRatesPage: React.FC<ShippingZoneRatesPageProps> = ({
|
|||
postalCodeRules,
|
||||
variant,
|
||||
formId,
|
||||
taxClasses,
|
||||
fetchMoreTaxClasses,
|
||||
...listProps
|
||||
}) => {
|
||||
const navigate = useNavigator();
|
||||
|
@ -110,6 +118,7 @@ export const ShippingZoneRatesPage: React.FC<ShippingZoneRatesPageProps> = ({
|
|||
orderValueRestricted: !!rate?.channelListings.length,
|
||||
privateMetadata: rate?.privateMetadata.map(mapMetadataItemToInput),
|
||||
type: rate?.type || null,
|
||||
taxClassId: rate?.taxClass?.id || "",
|
||||
}),
|
||||
[shippingChannels, rate],
|
||||
);
|
||||
|
@ -121,6 +130,10 @@ export const ShippingZoneRatesPage: React.FC<ShippingZoneRatesPageProps> = ({
|
|||
triggerChange,
|
||||
} = useForm(initialForm, undefined, { confirmLeave: true, formId });
|
||||
|
||||
const [taxClassDisplayName, setTaxClassDisplayName] = useStateUpdate(
|
||||
rate?.taxClass?.name ?? "",
|
||||
);
|
||||
|
||||
const handleFormSubmit = useHandleFormSubmit({
|
||||
formId,
|
||||
onSubmit,
|
||||
|
@ -239,6 +252,22 @@ export const ShippingZoneRatesPage: React.FC<ShippingZoneRatesPageProps> = ({
|
|||
}))}
|
||||
openModal={openChannelsModal}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<ShippingMethodTaxes
|
||||
value={formData.taxClassId}
|
||||
taxClassDisplayName={taxClassDisplayName}
|
||||
taxClasses={taxClasses}
|
||||
disabled={false}
|
||||
onChange={event =>
|
||||
handleTaxClassChange(
|
||||
event,
|
||||
taxClasses,
|
||||
change,
|
||||
setTaxClassDisplayName,
|
||||
)
|
||||
}
|
||||
onFetchMore={fetchMoreTaxClasses}
|
||||
/>
|
||||
</div>
|
||||
</Grid>
|
||||
<Savebar
|
||||
|
|
|
@ -13,6 +13,7 @@ export interface ShippingZoneRateCommonFormData {
|
|||
minDays: string;
|
||||
maxDays: string;
|
||||
type: ShippingMethodTypeEnum;
|
||||
taxClassId: string;
|
||||
}
|
||||
|
||||
export type ShippingZoneRateUpdateFormData = ShippingZoneRateCommonFormData &
|
||||
|
|
|
@ -1575,6 +1575,11 @@ export const shippingZone: ShippingZoneQuery["shippingZone"] = {
|
|||
shippingMethods: [
|
||||
{
|
||||
__typename: "ShippingMethodType",
|
||||
taxClass: {
|
||||
__typename: "TaxClass",
|
||||
name: "Shipping method",
|
||||
id: "VGV4Q2xhc3M6MQ==",
|
||||
},
|
||||
channelListings: [
|
||||
{
|
||||
__typename: "ShippingMethodChannelListing",
|
||||
|
@ -1670,6 +1675,11 @@ export const shippingZone: ShippingZoneQuery["shippingZone"] = {
|
|||
},
|
||||
{
|
||||
__typename: "ShippingMethodType",
|
||||
taxClass: {
|
||||
__typename: "TaxClass",
|
||||
name: "Shipping method",
|
||||
id: "VGV4Q2xhc3M6MQ==",
|
||||
},
|
||||
channelListings: [],
|
||||
excludedProducts: {
|
||||
__typename: "ProductCountableConnection",
|
||||
|
@ -1735,6 +1745,11 @@ export const shippingZone: ShippingZoneQuery["shippingZone"] = {
|
|||
},
|
||||
{
|
||||
__typename: "ShippingMethodType",
|
||||
taxClass: {
|
||||
__typename: "TaxClass",
|
||||
name: "Shipping method",
|
||||
id: "VGV4Q2xhc3M6MQ==",
|
||||
},
|
||||
channelListings: [],
|
||||
excludedProducts: {
|
||||
__typename: "ProductCountableConnection",
|
||||
|
@ -1800,6 +1815,11 @@ export const shippingZone: ShippingZoneQuery["shippingZone"] = {
|
|||
},
|
||||
{
|
||||
__typename: "ShippingMethodType",
|
||||
taxClass: {
|
||||
__typename: "TaxClass",
|
||||
name: "Shipping method",
|
||||
id: "VGV4Q2xhc3M6MQ==",
|
||||
},
|
||||
channelListings: [],
|
||||
excludedProducts: {
|
||||
__typename: "ProductCountableConnection",
|
||||
|
|
|
@ -134,6 +134,7 @@ export function getUpdateShippingPriceRateVariables(
|
|||
shippingZone: id,
|
||||
type: ShippingMethodTypeEnum.PRICE,
|
||||
description: getParsedDataForJsonStringField(data.description),
|
||||
taxClass: data.taxClassId,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -167,6 +168,7 @@ export function getUpdateShippingWeightRateVariables(
|
|||
shippingZone: id,
|
||||
type: ShippingMethodTypeEnum.WEIGHT,
|
||||
description: getParsedDataForJsonStringField(data.description),
|
||||
taxClass: data.taxClassId,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import {
|
|||
getPostalCodeRuleByMinMax,
|
||||
getRuleObject,
|
||||
} from "@saleor/shipping/views/utils";
|
||||
import { useTaxClassFetchMore } from "@saleor/taxes/utils/useTaxClassFetchMore";
|
||||
import { MinMax } from "@saleor/types";
|
||||
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
|
||||
import React from "react";
|
||||
|
@ -52,6 +53,8 @@ export const RateCreate: React.FC<RateCreateProps> = ({ id, params }) => {
|
|||
variables: { id },
|
||||
});
|
||||
|
||||
const { taxClasses, fetchMoreTaxClasses } = useTaxClassFetchMore();
|
||||
|
||||
const allChannels = createSortedShippingChannels(
|
||||
shippingZoneData?.shippingZone?.channels,
|
||||
);
|
||||
|
@ -164,6 +167,8 @@ export const RateCreate: React.FC<RateCreateProps> = ({ id, params }) => {
|
|||
onPostalCodeUnassign={onPostalCodeUnassign}
|
||||
onPostalCodeInclusionChange={onPostalCodeInclusionChange}
|
||||
variant={params.type}
|
||||
taxClasses={taxClasses ?? []}
|
||||
fetchMoreTaxClasses={fetchMoreTaxClasses}
|
||||
/>
|
||||
<ShippingZonePostalCodeRangeDialog
|
||||
confirmButtonState="default"
|
||||
|
|
|
@ -55,6 +55,7 @@ import {
|
|||
getPostalCodeRuleByMinMax,
|
||||
getRuleObject,
|
||||
} from "@saleor/shipping/views/utils";
|
||||
import { useTaxClassFetchMore } from "@saleor/taxes/utils/useTaxClassFetchMore";
|
||||
import { MinMax } from "@saleor/types";
|
||||
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
|
||||
import createMetadataUpdateHandler from "@saleor/utils/handlers/metadataUpdateHandler";
|
||||
|
@ -166,6 +167,8 @@ export const RateUpdate: React.FC<RateUpdateProps> = ({
|
|||
{ formId: FORM_ID },
|
||||
);
|
||||
|
||||
const { taxClasses, fetchMoreTaxClasses } = useTaxClassFetchMore();
|
||||
|
||||
const [
|
||||
updateShippingRate,
|
||||
updateShippingRateOpts,
|
||||
|
@ -410,6 +413,8 @@ export const RateUpdate: React.FC<RateUpdateProps> = ({
|
|||
onPostalCodeAssign={() => openModal("add-range")}
|
||||
onPostalCodeUnassign={onPostalCodeUnassign}
|
||||
postalCodeRules={state.postalCodeRules}
|
||||
taxClasses={taxClasses ?? []}
|
||||
fetchMoreTaxClasses={fetchMoreTaxClasses}
|
||||
/>
|
||||
<ShippingZonePostalCodeRangeDialog
|
||||
confirmButtonState={"default"}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -138,10 +138,6 @@ function loadStories() {
|
|||
// Site settings
|
||||
require("./stories/siteSettings/SiteSettingsPage");
|
||||
|
||||
// Taxes
|
||||
require("./stories/taxes/CountryListPage");
|
||||
require("./stories/taxes/CountryTaxesPage");
|
||||
|
||||
// Translations
|
||||
require("./stories/translations/TranslationsEntitiesListPage");
|
||||
require("./stories/translations/TranslationsLanguageListPage");
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { ProductTypeKindEnum, WeightUnitsEnum } from "@saleor/graphql";
|
||||
import { formError } from "@saleor/storybook/misc";
|
||||
import { taxClasses } from "@saleor/taxes/fixtures";
|
||||
import { storiesOf } from "@storybook/react";
|
||||
import React from "react";
|
||||
|
||||
|
@ -16,7 +17,8 @@ const props: Omit<ProductTypeCreatePageProps, "classes"> = {
|
|||
onSubmit: () => undefined,
|
||||
pageTitle: "Create product type",
|
||||
saveButtonBarState: "default",
|
||||
taxTypes: [],
|
||||
taxClasses,
|
||||
onFetchMoreTaxClasses: undefined,
|
||||
kind: ProductTypeKindEnum.NORMAL,
|
||||
onChangeKind: () => undefined,
|
||||
};
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { listActionsProps } from "@saleor/fixtures";
|
||||
import { WeightUnitsEnum } from "@saleor/graphql";
|
||||
import { formError } from "@saleor/storybook/misc";
|
||||
import { taxClasses } from "@saleor/taxes/fixtures";
|
||||
import { storiesOf } from "@storybook/react";
|
||||
import React from "react";
|
||||
|
||||
|
@ -25,7 +26,8 @@ const props: Omit<ProductTypeDetailsPageProps, "classes"> = {
|
|||
productAttributeList: listActionsProps,
|
||||
productType,
|
||||
saveButtonBarState: "default",
|
||||
taxTypes: [],
|
||||
taxClasses,
|
||||
onFetchMoreTaxClasses: undefined,
|
||||
variantAttributeList: listActionsProps,
|
||||
setSelectedVariantAttributes: () => undefined,
|
||||
selectedVariantAttributes: [],
|
||||
|
|
|
@ -2,6 +2,7 @@ import { channelsList } from "@saleor/channels/fixtures";
|
|||
import { createChannelsData } from "@saleor/channels/utils";
|
||||
import { fetchMoreProps } from "@saleor/fixtures";
|
||||
import { ProductErrorCode } from "@saleor/graphql";
|
||||
import { taxClasses } from "@saleor/taxes/fixtures";
|
||||
import { warehouseList } from "@saleor/warehouses/fixtures";
|
||||
import { storiesOf } from "@storybook/react";
|
||||
import React from "react";
|
||||
|
@ -15,7 +16,6 @@ import {
|
|||
productTypeSearch,
|
||||
} from "../../../productTypes/fixtures";
|
||||
import Decorator from "../../Decorator";
|
||||
import { taxTypes } from "../taxes/fixtures";
|
||||
|
||||
const product = productFixture("");
|
||||
const channels = createChannelsData(channelsList);
|
||||
|
@ -47,7 +47,8 @@ storiesOf("Views / Products / Create product", module)
|
|||
saveButtonBarState="default"
|
||||
warehouses={warehouseList}
|
||||
onWarehouseConfigure={() => undefined}
|
||||
taxTypes={taxTypes}
|
||||
taxClasses={taxClasses}
|
||||
fetchMoreTaxClasses={undefined}
|
||||
weightUnit="kg"
|
||||
referencePages={[]}
|
||||
referenceProducts={[]}
|
||||
|
@ -83,7 +84,8 @@ storiesOf("Views / Products / Create product", module)
|
|||
saveButtonBarState="default"
|
||||
warehouses={undefined}
|
||||
onWarehouseConfigure={() => undefined}
|
||||
taxTypes={taxTypes}
|
||||
taxClasses={taxClasses}
|
||||
fetchMoreTaxClasses={undefined}
|
||||
weightUnit="kg"
|
||||
referencePages={[]}
|
||||
referenceProducts={[]}
|
||||
|
@ -135,7 +137,8 @@ storiesOf("Views / Products / Create product", module)
|
|||
saveButtonBarState="default"
|
||||
warehouses={warehouseList}
|
||||
onWarehouseConfigure={() => undefined}
|
||||
taxTypes={taxTypes}
|
||||
taxClasses={taxClasses}
|
||||
fetchMoreTaxClasses={undefined}
|
||||
weightUnit="kg"
|
||||
referencePages={[]}
|
||||
referenceProducts={[]}
|
||||
|
|
|
@ -8,12 +8,12 @@ import ProductUpdatePage, {
|
|||
} from "@saleor/products/components/ProductUpdatePage";
|
||||
import { ProductUpdateFormData } from "@saleor/products/components/ProductUpdatePage/types";
|
||||
import { product as productFixture } from "@saleor/products/fixtures";
|
||||
import { taxClasses } from "@saleor/taxes/fixtures";
|
||||
import { warehouseList } from "@saleor/warehouses/fixtures";
|
||||
import { storiesOf } from "@storybook/react";
|
||||
import React from "react";
|
||||
|
||||
import Decorator from "../../Decorator";
|
||||
import { taxTypes } from "../taxes/fixtures";
|
||||
|
||||
const product = productFixture(placeholderImage);
|
||||
|
||||
|
@ -60,7 +60,8 @@ const props: ProductUpdatePageProps = {
|
|||
referencePages: [],
|
||||
referenceProducts: [],
|
||||
saveButtonBarState: "default",
|
||||
taxTypes,
|
||||
taxClasses,
|
||||
fetchMoreTaxClasses: undefined,
|
||||
variants: product.variants,
|
||||
warehouses: warehouseList,
|
||||
attributeValues: [],
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { countries } from "@saleor/fixtures";
|
||||
import { mapCountriesToCountriesCodes } from "@saleor/utils/maps";
|
||||
import { storiesOf } from "@storybook/react";
|
||||
import React from "react";
|
||||
|
@ -6,13 +7,18 @@ import ShippingZoneCountriesAssignDialog, {
|
|||
ShippingZoneCountriesAssignDialogProps,
|
||||
} from "../../../shipping/components/ShippingZoneCountriesAssignDialog";
|
||||
import Decorator from "../../Decorator";
|
||||
import { countries } from "../taxes/fixtures";
|
||||
|
||||
const initialCountries = ["PL", "GB", "DE"];
|
||||
|
||||
const mappedCountries = countries.map(({ code, name }) => ({
|
||||
__typename: "CountryDisplay" as const,
|
||||
code,
|
||||
country: name,
|
||||
}));
|
||||
|
||||
const props: ShippingZoneCountriesAssignDialogProps = {
|
||||
confirmButtonState: "default",
|
||||
countries,
|
||||
countries: mappedCountries,
|
||||
restWorldCountries: mapCountriesToCountriesCodes(countries).filter(
|
||||
countryCode => !initialCountries.includes(countryCode),
|
||||
),
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
import { storiesOf } from "@storybook/react";
|
||||
import React from "react";
|
||||
|
||||
import { pageListProps } from "../../../fixtures";
|
||||
import CountryListPage, {
|
||||
CountryListPageProps,
|
||||
} from "../../../taxes/components/CountryListPage";
|
||||
import Decorator from "../../Decorator";
|
||||
import { countries } from "./fixtures";
|
||||
|
||||
const props: CountryListPageProps = {
|
||||
...pageListProps.default,
|
||||
onSubmit: () => undefined,
|
||||
onTaxFetch: () => undefined,
|
||||
saveButtonBarState: "default",
|
||||
shop: {
|
||||
__typename: "Shop",
|
||||
chargeTaxesOnShipping: false,
|
||||
countries,
|
||||
displayGrossPrices: true,
|
||||
includeTaxesInPrices: false,
|
||||
},
|
||||
};
|
||||
|
||||
storiesOf("Views / Taxes / Country List", module)
|
||||
.addDecorator(Decorator)
|
||||
.add("default", () => <CountryListPage {...props} />)
|
||||
.add("loading", () => (
|
||||
<CountryListPage {...props} shop={undefined} disabled={true} />
|
||||
));
|
|
@ -1,24 +0,0 @@
|
|||
import { storiesOf } from "@storybook/react";
|
||||
import React from "react";
|
||||
|
||||
import CountryTaxesPage, {
|
||||
CountryTaxesPageProps,
|
||||
} from "../../../taxes/components/CountryTaxesPage";
|
||||
import Decorator from "../../Decorator";
|
||||
import { countries } from "./fixtures";
|
||||
|
||||
const props: CountryTaxesPageProps = {
|
||||
countryName: "Austria",
|
||||
taxCategories: countries[0].vat.reducedRates,
|
||||
};
|
||||
|
||||
storiesOf("Views / Taxes / Reduced Tax Categories", module)
|
||||
.addDecorator(Decorator)
|
||||
.add("default", () => <CountryTaxesPage {...props} />)
|
||||
.add("loading", () => (
|
||||
<CountryTaxesPage
|
||||
{...props}
|
||||
countryName={undefined}
|
||||
taxCategories={undefined}
|
||||
/>
|
||||
));
|
File diff suppressed because it is too large
Load diff
|
@ -1,97 +0,0 @@
|
|||
import { Card, TableBody, TableCell, TableHead } from "@material-ui/core";
|
||||
import ResponsiveTable from "@saleor/components/ResponsiveTable";
|
||||
import Skeleton from "@saleor/components/Skeleton";
|
||||
import TableRowLink from "@saleor/components/TableRowLink";
|
||||
import { CountryListQuery } from "@saleor/graphql";
|
||||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
import { countryTaxRatesUrl } from "@saleor/taxes/urls";
|
||||
import classNames from "classnames";
|
||||
import React from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import { maybe, renderCollection } from "../../../misc";
|
||||
|
||||
const useStyles = makeStyles(
|
||||
{
|
||||
tableRow: {
|
||||
cursor: "pointer",
|
||||
},
|
||||
textRight: {
|
||||
textAlign: "right",
|
||||
},
|
||||
},
|
||||
{ name: "CountryList" },
|
||||
);
|
||||
|
||||
interface CountryListProps {
|
||||
countries: CountryListQuery["shop"]["countries"];
|
||||
}
|
||||
|
||||
const CountryList: React.FC<CountryListProps> = props => {
|
||||
const { countries } = props;
|
||||
|
||||
const classes = useStyles(props);
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<ResponsiveTable>
|
||||
<TableHead>
|
||||
<TableRowLink>
|
||||
<TableCell>
|
||||
<FormattedMessage id="07KB2d" defaultMessage="Country Code" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<FormattedMessage id="0GJfWd" defaultMessage="Country Name" />
|
||||
</TableCell>
|
||||
<TableCell className={classes.textRight}>
|
||||
<FormattedMessage
|
||||
id="/JENWS"
|
||||
defaultMessage="Reduced Tax Rates"
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRowLink>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{renderCollection(
|
||||
countries,
|
||||
country => (
|
||||
<TableRowLink
|
||||
className={classNames({
|
||||
[classes.tableRow]: !!country,
|
||||
})}
|
||||
hover={!!country}
|
||||
href={country && countryTaxRatesUrl(country.code)}
|
||||
key={country ? country.code : "skeleton"}
|
||||
>
|
||||
<TableCell>
|
||||
{maybe<React.ReactNode>(() => country.code, <Skeleton />)}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{maybe<React.ReactNode>(() => country.country, <Skeleton />)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.textRight}>
|
||||
{maybe<React.ReactNode>(
|
||||
() => country.vat.reducedRates.length,
|
||||
<Skeleton />,
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRowLink>
|
||||
),
|
||||
() => (
|
||||
<TableRowLink>
|
||||
<TableCell colSpan={3}>
|
||||
<FormattedMessage
|
||||
id="3BTtL2"
|
||||
defaultMessage="No countries found"
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRowLink>
|
||||
),
|
||||
)}
|
||||
</TableBody>
|
||||
</ResponsiveTable>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
CountryList.displayName = "CountryList";
|
||||
export default CountryList;
|
|
@ -1,2 +0,0 @@
|
|||
export { default } from "./CountryList";
|
||||
export * from "./CountryList";
|
|
@ -1,94 +0,0 @@
|
|||
import { Backlink } from "@saleor/components/Backlink";
|
||||
import { Container } from "@saleor/components/Container";
|
||||
import Form from "@saleor/components/Form";
|
||||
import Grid from "@saleor/components/Grid";
|
||||
import PageHeader from "@saleor/components/PageHeader";
|
||||
import Savebar from "@saleor/components/Savebar";
|
||||
import { configurationMenuUrl } from "@saleor/configuration";
|
||||
import { CountryListQuery } from "@saleor/graphql";
|
||||
import { SubmitPromise } from "@saleor/hooks/useForm";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import { sectionNames } from "@saleor/intl";
|
||||
import { ConfirmButtonTransitionState } from "@saleor/macaw-ui";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import CountryList from "../CountryList";
|
||||
import TaxConfiguration from "../TaxConfiguration";
|
||||
|
||||
export interface TaxesConfigurationFormData {
|
||||
includeTax: boolean;
|
||||
showGross: boolean;
|
||||
chargeTaxesOnShipping: boolean;
|
||||
}
|
||||
export interface CountryListPageProps {
|
||||
disabled: boolean;
|
||||
saveButtonBarState: ConfirmButtonTransitionState;
|
||||
shop: CountryListQuery["shop"];
|
||||
onSubmit: (data: TaxesConfigurationFormData) => SubmitPromise;
|
||||
onTaxFetch: () => void;
|
||||
}
|
||||
|
||||
const CountryListPage: React.FC<CountryListPageProps> = ({
|
||||
disabled,
|
||||
saveButtonBarState,
|
||||
shop,
|
||||
onSubmit,
|
||||
onTaxFetch,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const navigate = useNavigator();
|
||||
|
||||
const initialForm: TaxesConfigurationFormData = {
|
||||
chargeTaxesOnShipping: shop?.chargeTaxesOnShipping || false,
|
||||
includeTax: shop?.includeTaxesInPrices || false,
|
||||
showGross: shop?.displayGrossPrices || false,
|
||||
};
|
||||
|
||||
return (
|
||||
<Form
|
||||
confirmLeave
|
||||
initial={initialForm}
|
||||
onSubmit={onSubmit}
|
||||
disabled={disabled}
|
||||
>
|
||||
{({ change, data, isSaveDisabled, submit }) => (
|
||||
<>
|
||||
<Container>
|
||||
<Backlink href={configurationMenuUrl}>
|
||||
{intl.formatMessage(sectionNames.configuration)}
|
||||
</Backlink>
|
||||
<PageHeader
|
||||
title={intl.formatMessage({
|
||||
id: "lnQAos",
|
||||
defaultMessage: "Taxes",
|
||||
description: "header",
|
||||
})}
|
||||
/>
|
||||
<Grid variant="inverted">
|
||||
<div>
|
||||
<TaxConfiguration
|
||||
data={data}
|
||||
disabled={disabled}
|
||||
onChange={event => change(event)}
|
||||
onTaxFetch={onTaxFetch}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<CountryList countries={shop?.countries} />
|
||||
</div>
|
||||
</Grid>
|
||||
</Container>
|
||||
<Savebar
|
||||
disabled={isSaveDisabled}
|
||||
state={saveButtonBarState}
|
||||
onCancel={() => navigate(configurationMenuUrl)}
|
||||
onSubmit={submit}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
CountryListPage.displayName = "CountryListPage";
|
||||
export default CountryListPage;
|
|
@ -1,2 +0,0 @@
|
|||
export { default } from "./CountryListPage";
|
||||
export * from "./CountryListPage";
|
|
@ -1,111 +0,0 @@
|
|||
import { Card, TableBody, TableCell, TableHead } from "@material-ui/core";
|
||||
import { Backlink } from "@saleor/components/Backlink";
|
||||
import { Container } from "@saleor/components/Container";
|
||||
import Grid from "@saleor/components/Grid";
|
||||
import PageHeader from "@saleor/components/PageHeader";
|
||||
import ResponsiveTable from "@saleor/components/ResponsiveTable";
|
||||
import Skeleton from "@saleor/components/Skeleton";
|
||||
import TableRowLink from "@saleor/components/TableRowLink";
|
||||
import { CountryListQuery } from "@saleor/graphql";
|
||||
import { sectionNames } from "@saleor/intl";
|
||||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
import { countryListUrl } from "@saleor/taxes/urls";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import { maybe, renderCollection } from "../../../misc";
|
||||
|
||||
const useStyles = makeStyles(
|
||||
{
|
||||
wideColumn: {
|
||||
width: "80%",
|
||||
},
|
||||
},
|
||||
{ name: "CountryTaxesPage" },
|
||||
);
|
||||
|
||||
export interface CountryTaxesPageProps {
|
||||
countryName: string;
|
||||
taxCategories: CountryListQuery["shop"]["countries"][0]["vat"]["reducedRates"];
|
||||
}
|
||||
|
||||
const CountryTaxesPage: React.FC<CountryTaxesPageProps> = props => {
|
||||
const { countryName, taxCategories } = props;
|
||||
|
||||
const classes = useStyles(props);
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Backlink href={countryListUrl}>
|
||||
{intl.formatMessage(sectionNames.taxes)}
|
||||
</Backlink>
|
||||
<PageHeader
|
||||
title={
|
||||
countryName
|
||||
? intl.formatMessage(
|
||||
{
|
||||
id: "QHB48n",
|
||||
defaultMessage: "Tax Rates in {countryName}",
|
||||
description: "header",
|
||||
},
|
||||
{
|
||||
countryName,
|
||||
},
|
||||
)
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
<Grid>
|
||||
<div>
|
||||
<Card>
|
||||
<ResponsiveTable>
|
||||
<TableHead>
|
||||
<TableRowLink>
|
||||
<TableCell className={classes.wideColumn}>
|
||||
<FormattedMessage id="ccXLVi" defaultMessage="Category" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<FormattedMessage id="la9cZ4" defaultMessage="Tax Rate" />
|
||||
</TableCell>
|
||||
</TableRowLink>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{renderCollection(
|
||||
taxCategories,
|
||||
taxCategory => (
|
||||
<TableRowLink
|
||||
key={taxCategory ? taxCategory.rateType : "skeleton"}
|
||||
>
|
||||
<TableCell>
|
||||
{taxCategory?.rateType ?? <Skeleton />}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{maybe<React.ReactNode>(
|
||||
() => taxCategory.rate,
|
||||
<Skeleton />,
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRowLink>
|
||||
),
|
||||
() => (
|
||||
<TableRowLink>
|
||||
<TableCell colSpan={2}>
|
||||
<FormattedMessage
|
||||
id="Ubath+"
|
||||
defaultMessage="No reduced tax categories found"
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRowLink>
|
||||
),
|
||||
)}
|
||||
</TableBody>
|
||||
</ResponsiveTable>
|
||||
</Card>
|
||||
</div>
|
||||
</Grid>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
CountryTaxesPage.displayName = "CountryTaxesPage";
|
||||
export default CountryTaxesPage;
|
|
@ -1,2 +0,0 @@
|
|||
export { default } from "./CountryTaxesPage";
|
||||
export * from "./CountryTaxesPage";
|
|
@ -1,85 +0,0 @@
|
|||
import { Card, CardActions, CardContent } from "@material-ui/core";
|
||||
import { Button } from "@saleor/components/Button";
|
||||
import CardTitle from "@saleor/components/CardTitle";
|
||||
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
|
||||
import FormSpacer from "@saleor/components/FormSpacer";
|
||||
import { sectionNames } from "@saleor/intl";
|
||||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import { TaxesConfigurationFormData } from "../CountryListPage";
|
||||
|
||||
interface TaxConfigurationProps {
|
||||
data: TaxesConfigurationFormData;
|
||||
disabled: boolean;
|
||||
onChange: (event: React.ChangeEvent<any>) => void;
|
||||
onTaxFetch: () => void;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(
|
||||
{
|
||||
content: {
|
||||
paddingBottom: 0,
|
||||
},
|
||||
},
|
||||
{ name: "TaxConfiguration" },
|
||||
);
|
||||
|
||||
export const TaxConfiguration: React.FC<TaxConfigurationProps> = props => {
|
||||
const { data, disabled, onChange, onTaxFetch } = props;
|
||||
|
||||
const classes = useStyles(props);
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardTitle title={intl.formatMessage(sectionNames.configuration)} />
|
||||
<CardContent className={classes.content}>
|
||||
<ControlledCheckbox
|
||||
name={"includeTax" as keyof FormData}
|
||||
label={intl.formatMessage({
|
||||
id: "4EuJKs",
|
||||
defaultMessage: "All products prices are entered with tax included",
|
||||
})}
|
||||
checked={data.includeTax}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<FormSpacer />
|
||||
<ControlledCheckbox
|
||||
name={"showGross" as keyof FormData}
|
||||
label={intl.formatMessage({
|
||||
id: "98isC5",
|
||||
defaultMessage: "Show gross prices to customers in the storefront",
|
||||
})}
|
||||
checked={data.showGross}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<FormSpacer />
|
||||
<ControlledCheckbox
|
||||
name={"chargeTaxesOnShipping" as keyof FormData}
|
||||
label={intl.formatMessage({
|
||||
id: "FNKhkx",
|
||||
defaultMessage: "Charge taxes on shipping rates",
|
||||
})}
|
||||
checked={data.chargeTaxesOnShipping}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<FormSpacer />
|
||||
</CardContent>
|
||||
<CardActions>
|
||||
<Button disabled={disabled} onClick={onTaxFetch}>
|
||||
<FormattedMessage
|
||||
id="+OV+Gj"
|
||||
defaultMessage="Fetch taxes"
|
||||
description="button"
|
||||
/>
|
||||
</Button>
|
||||
</CardActions>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
export default TaxConfiguration;
|
|
@ -1,2 +0,0 @@
|
|||
export { default } from "./TaxConfiguration";
|
||||
export * from "./TaxConfiguration";
|
105
src/taxes/components/TaxCountryDialog/TaxCountryDialog.tsx
Normal file
105
src/taxes/components/TaxCountryDialog/TaxCountryDialog.tsx
Normal file
|
@ -0,0 +1,105 @@
|
|||
import {
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
Divider,
|
||||
FormControlLabel,
|
||||
InputAdornment,
|
||||
Radio,
|
||||
TextField,
|
||||
} from "@material-ui/core";
|
||||
import VerticalSpacer from "@saleor/apps/components/VerticalSpacer";
|
||||
import { CountryFragment } from "@saleor/graphql";
|
||||
import { useLocalSearch } from "@saleor/hooks/useLocalSearch";
|
||||
import useModalDialogOpen from "@saleor/hooks/useModalDialogOpen";
|
||||
import { buttonMessages } from "@saleor/intl";
|
||||
import { Button, DialogHeader, SearchIcon } from "@saleor/macaw-ui";
|
||||
import { taxesMessages } from "@saleor/taxes/messages";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import { useStyles } from "./styles";
|
||||
|
||||
interface TaxCountryDialogProps {
|
||||
open: boolean;
|
||||
countries: CountryFragment[];
|
||||
onConfirm: (countries: CountryFragment) => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export const TaxCountryDialog: React.FC<TaxCountryDialogProps> = ({
|
||||
open,
|
||||
countries,
|
||||
onConfirm,
|
||||
onClose,
|
||||
}) => {
|
||||
const classes = useStyles();
|
||||
const intl = useIntl();
|
||||
|
||||
const [selectedCountry, setSelectedCountry] = React.useState<
|
||||
CountryFragment
|
||||
>();
|
||||
|
||||
useModalDialogOpen(open, {
|
||||
onClose: () => {
|
||||
setSelectedCountry(undefined);
|
||||
setQuery("");
|
||||
},
|
||||
});
|
||||
|
||||
const { query, setQuery, searchResult: filteredCountries } = useLocalSearch<
|
||||
CountryFragment
|
||||
>(countries, country => country.country);
|
||||
|
||||
return (
|
||||
<Dialog open={open} fullWidth onClose={onClose}>
|
||||
<DialogHeader onClose={onClose}>
|
||||
<FormattedMessage {...taxesMessages.chooseCountryDialogTitle} />
|
||||
</DialogHeader>
|
||||
<DialogContent className={classes.wrapper}>
|
||||
<TextField
|
||||
value={query}
|
||||
onChange={e => setQuery(e.target.value)}
|
||||
variant="outlined"
|
||||
placeholder={intl.formatMessage(taxesMessages.country)}
|
||||
fullWidth
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
inputProps={{ className: classes.inputPadding }}
|
||||
/>
|
||||
<VerticalSpacer spacing={2} />
|
||||
<div className={classes.scrollable}>
|
||||
{filteredCountries.map(country => (
|
||||
<React.Fragment key={country.code}>
|
||||
<FormControlLabel
|
||||
label={country.country}
|
||||
checked={country.code === selectedCountry?.code}
|
||||
onChange={() => setSelectedCountry(country)}
|
||||
control={<Radio />}
|
||||
/>
|
||||
<Divider />
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => {
|
||||
onConfirm(selectedCountry);
|
||||
}}
|
||||
disabled={!selectedCountry}
|
||||
>
|
||||
<FormattedMessage {...buttonMessages.add} />
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default TaxCountryDialog;
|
2
src/taxes/components/TaxCountryDialog/index.ts
Normal file
2
src/taxes/components/TaxCountryDialog/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./TaxCountryDialog";
|
||||
export * from "./TaxCountryDialog";
|
22
src/taxes/components/TaxCountryDialog/styles.ts
Normal file
22
src/taxes/components/TaxCountryDialog/styles.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
|
||||
export const useStyles = makeStyles(
|
||||
theme => ({
|
||||
inputPadding: {
|
||||
padding: theme.spacing(2, 0),
|
||||
},
|
||||
wrapper: {
|
||||
overflowX: "visible",
|
||||
padding: 0,
|
||||
},
|
||||
scrollable: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
overflowY: "scroll",
|
||||
maxHeight: 450,
|
||||
marginLeft: -15,
|
||||
paddingLeft: 15,
|
||||
},
|
||||
}),
|
||||
{ name: "TaxCountryDialog" },
|
||||
);
|
70
src/taxes/components/TaxInput/TaxInput.tsx
Normal file
70
src/taxes/components/TaxInput/TaxInput.tsx
Normal file
|
@ -0,0 +1,70 @@
|
|||
import { InputAdornment, TextField, TextFieldProps } from "@material-ui/core";
|
||||
import { findPriceSeparator } from "@saleor/components/PriceField/utils";
|
||||
import { FormChange } from "@saleor/hooks/useForm";
|
||||
import React from "react";
|
||||
|
||||
import { useStyles } from "./styles";
|
||||
|
||||
interface TaxInputProps {
|
||||
placeholder?: string;
|
||||
value: string | undefined;
|
||||
change: FormChange;
|
||||
}
|
||||
|
||||
export const TaxInput: React.FC<TaxInputProps> = ({
|
||||
placeholder,
|
||||
value,
|
||||
change,
|
||||
}) => {
|
||||
const classes = useStyles();
|
||||
|
||||
const handleChange: FormChange = e => {
|
||||
let value = e.target.value;
|
||||
const splitCharacter = findPriceSeparator(value);
|
||||
const [integerPart, decimalPart] = value.split(splitCharacter);
|
||||
|
||||
if (decimalPart?.length > 3) {
|
||||
const shortenedDecimalPart = decimalPart.slice(0, 3);
|
||||
value = `${integerPart}${splitCharacter}${shortenedDecimalPart}`;
|
||||
}
|
||||
|
||||
change({
|
||||
target: {
|
||||
name: e.target.name,
|
||||
value,
|
||||
},
|
||||
});
|
||||
};
|
||||
const handleKeyDown: TextFieldProps["onKeyDown"] = event => {
|
||||
switch (event.key.toLowerCase()) {
|
||||
case "e":
|
||||
case "-": {
|
||||
event.preventDefault();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<TextField
|
||||
type="number"
|
||||
fullWidth
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
InputProps={{
|
||||
startAdornment: <InputAdornment position="start">%</InputAdornment>,
|
||||
className: classes.hideSpinboxes,
|
||||
}}
|
||||
inputProps={{
|
||||
className: classes.inputPadding,
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 0.001,
|
||||
}}
|
||||
onChange={handleChange}
|
||||
onKeyDown={handleKeyDown}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default TaxInput;
|
2
src/taxes/components/TaxInput/index.ts
Normal file
2
src/taxes/components/TaxInput/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./TaxInput";
|
||||
export * from "./TaxInput";
|
26
src/taxes/components/TaxInput/styles.ts
Normal file
26
src/taxes/components/TaxInput/styles.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
|
||||
export const useStyles = makeStyles(
|
||||
() => ({
|
||||
/**
|
||||
* Spinboxes are up & down arrows that are used to change the value of a number
|
||||
* in html input elements with type=number. There is a different styling for
|
||||
* hiding them, dependent on browser (as of mid-2022).
|
||||
*/
|
||||
hideSpinboxes: {
|
||||
// chrome, safari
|
||||
"& input::-webkit-outer-spin-button, input::-webkit-inner-spin-button": {
|
||||
appearance: "none",
|
||||
margin: 0,
|
||||
},
|
||||
// firefox
|
||||
"& input": {
|
||||
"-moz-appearance": "textfield",
|
||||
},
|
||||
},
|
||||
inputPadding: {
|
||||
padding: "16px 0 16px 0",
|
||||
},
|
||||
}),
|
||||
{ name: "TaxInput" },
|
||||
);
|
195
src/taxes/fixtures.ts
Normal file
195
src/taxes/fixtures.ts
Normal file
|
@ -0,0 +1,195 @@
|
|||
import {
|
||||
TaxCalculationStrategy,
|
||||
TaxClassFragment,
|
||||
TaxConfigurationFragment,
|
||||
TaxCountryConfigurationFragment,
|
||||
} from "@saleor/graphql";
|
||||
|
||||
export const taxConfigurations: TaxConfigurationFragment[] = [
|
||||
{
|
||||
__typename: "TaxConfiguration",
|
||||
id: "taxConf1",
|
||||
channel: {
|
||||
__typename: "Channel",
|
||||
id: "taxChannel1",
|
||||
name: "Channel USD",
|
||||
},
|
||||
displayGrossPrices: true,
|
||||
pricesEnteredWithTax: false,
|
||||
chargeTaxes: true,
|
||||
taxCalculationStrategy: TaxCalculationStrategy.FLAT_RATES,
|
||||
countries: [
|
||||
{
|
||||
__typename: "TaxConfigurationPerCountry",
|
||||
country: {
|
||||
__typename: "CountryDisplay",
|
||||
code: "AF",
|
||||
country: "Afghanistan",
|
||||
},
|
||||
chargeTaxes: false,
|
||||
taxCalculationStrategy: null,
|
||||
displayGrossPrices: false,
|
||||
},
|
||||
{
|
||||
__typename: "TaxConfigurationPerCountry",
|
||||
country: {
|
||||
__typename: "CountryDisplay",
|
||||
code: "AX",
|
||||
country: "Åland Islands",
|
||||
},
|
||||
chargeTaxes: true,
|
||||
taxCalculationStrategy: TaxCalculationStrategy.TAX_APP,
|
||||
displayGrossPrices: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
__typename: "TaxConfiguration",
|
||||
id: "taxConf2",
|
||||
channel: {
|
||||
__typename: "Channel",
|
||||
id: "taxChannel2",
|
||||
name: "Channel PLN",
|
||||
},
|
||||
displayGrossPrices: false,
|
||||
pricesEnteredWithTax: true,
|
||||
chargeTaxes: true,
|
||||
taxCalculationStrategy: TaxCalculationStrategy.TAX_APP,
|
||||
countries: [
|
||||
{
|
||||
__typename: "TaxConfigurationPerCountry",
|
||||
country: {
|
||||
__typename: "CountryDisplay",
|
||||
code: "AL",
|
||||
country: "Albania",
|
||||
},
|
||||
chargeTaxes: true,
|
||||
taxCalculationStrategy: TaxCalculationStrategy.FLAT_RATES,
|
||||
displayGrossPrices: true,
|
||||
},
|
||||
{
|
||||
__typename: "TaxConfigurationPerCountry",
|
||||
country: {
|
||||
__typename: "CountryDisplay",
|
||||
code: "DZ",
|
||||
country: "Algeria",
|
||||
},
|
||||
chargeTaxes: false,
|
||||
taxCalculationStrategy: null,
|
||||
displayGrossPrices: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const taxCountryConfigurations: TaxCountryConfigurationFragment[] = [
|
||||
{
|
||||
__typename: "TaxCountryConfiguration",
|
||||
country: {
|
||||
__typename: "CountryDisplay",
|
||||
code: "AF",
|
||||
country: "Afghanistan",
|
||||
},
|
||||
taxClassCountryRates: [
|
||||
{
|
||||
__typename: "TaxClassCountryRate",
|
||||
rate: 0.31,
|
||||
taxClass: {
|
||||
__typename: "TaxClass",
|
||||
id: "taxCountryConfigurations.0.taxClassCountryRates.0.taxClass.id",
|
||||
name: "Default tax class",
|
||||
},
|
||||
},
|
||||
{
|
||||
__typename: "TaxClassCountryRate",
|
||||
rate: 0.05,
|
||||
taxClass: {
|
||||
__typename: "TaxClass",
|
||||
id: "taxCountryConfigurations.0.taxClassCountryRates.0.taxClass.id",
|
||||
name: "Perfume",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
__typename: "TaxCountryConfiguration",
|
||||
country: {
|
||||
__typename: "CountryDisplay",
|
||||
code: "AX",
|
||||
country: "Åland Islands",
|
||||
},
|
||||
taxClassCountryRates: [
|
||||
{
|
||||
__typename: "TaxClassCountryRate",
|
||||
rate: 0.21,
|
||||
taxClass: {
|
||||
__typename: "TaxClass",
|
||||
id: "taxCountryConfigurations.0.taxClassCountryRates.0.taxClass.id",
|
||||
name: "Default tax class",
|
||||
},
|
||||
},
|
||||
{
|
||||
__typename: "TaxClassCountryRate",
|
||||
rate: 0.05,
|
||||
taxClass: {
|
||||
__typename: "TaxClass",
|
||||
id: "taxCountryConfigurations.0.taxClassCountryRates.0.taxClass.id",
|
||||
name: "Food",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const taxClasses: TaxClassFragment[] = [
|
||||
{
|
||||
__typename: "TaxClass",
|
||||
id: "taxClassNode1",
|
||||
name: "Default tax class",
|
||||
countries: [
|
||||
{
|
||||
__typename: "TaxClassCountryRate",
|
||||
country: {
|
||||
__typename: "CountryDisplay",
|
||||
code: "AX",
|
||||
country: "Åland Islands",
|
||||
},
|
||||
rate: 0.2,
|
||||
},
|
||||
{
|
||||
__typename: "TaxClassCountryRate",
|
||||
country: {
|
||||
__typename: "CountryDisplay",
|
||||
code: "AF",
|
||||
country: "Afghanistan",
|
||||
},
|
||||
rate: 0.15,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
__typename: "TaxClass",
|
||||
id: "taxClassesNode2",
|
||||
name: "Food",
|
||||
countries: [
|
||||
{
|
||||
__typename: "TaxClassCountryRate",
|
||||
country: {
|
||||
__typename: "CountryDisplay",
|
||||
code: "AX",
|
||||
country: "Åland Islands",
|
||||
},
|
||||
rate: 0.05,
|
||||
},
|
||||
{
|
||||
__typename: "TaxClassCountryRate",
|
||||
country: {
|
||||
__typename: "CountryDisplay",
|
||||
code: "AF",
|
||||
country: "Afghanistan",
|
||||
},
|
||||
rate: 0.0,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
|
@ -1,18 +1,50 @@
|
|||
import { sectionNames } from "@saleor/intl";
|
||||
import { parse as parseQs } from "qs";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
import { Route, RouteComponentProps, Switch } from "react-router-dom";
|
||||
|
||||
import { WindowTitle } from "../components/WindowTitle";
|
||||
import { countryListPath, countryTaxRatesPath } from "./urls";
|
||||
import CountryList from "./views/CountryList";
|
||||
import CountryTaxesComponent, {
|
||||
CountryTaxesParams,
|
||||
} from "./views/CountryTaxes";
|
||||
import {
|
||||
taxClassesListUrl,
|
||||
taxConfigurationListPath,
|
||||
taxCountriesListPath,
|
||||
TaxesUrlQueryParams,
|
||||
} from "./urls";
|
||||
import TaxChannelsListComponent from "./views/TaxChannelsList";
|
||||
import TaxClassesListComponent from "./views/TaxClassesList";
|
||||
import TaxCountriesListComponent from "./views/TaxCountriesList";
|
||||
|
||||
const CountryTaxes: React.FC<RouteComponentProps<CountryTaxesParams>> = ({
|
||||
const TaxChannelsList: React.FC<RouteComponentProps<{ id: string }>> = ({
|
||||
match,
|
||||
}) => <CountryTaxesComponent code={match.params.code} />;
|
||||
location,
|
||||
}) => {
|
||||
const qs: TaxesUrlQueryParams = parseQs(location.search.substring(1));
|
||||
|
||||
return (
|
||||
<TaxChannelsListComponent
|
||||
id={decodeURIComponent(match.params.id)}
|
||||
params={qs}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const TaxCountriesList: React.FC<RouteComponentProps<{ id: string }>> = ({
|
||||
match,
|
||||
}) => {
|
||||
const qs: TaxesUrlQueryParams = parseQs(location.search.substring(1));
|
||||
|
||||
return (
|
||||
<TaxCountriesListComponent
|
||||
id={decodeURIComponent(match.params.id)}
|
||||
params={qs}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const TaxClassesList: React.FC<RouteComponentProps<{ id: string }>> = ({
|
||||
match,
|
||||
}) => <TaxClassesListComponent id={decodeURIComponent(match.params.id)} />;
|
||||
|
||||
const Component = () => {
|
||||
const intl = useIntl();
|
||||
|
@ -21,12 +53,18 @@ const Component = () => {
|
|||
<>
|
||||
<WindowTitle title={intl.formatMessage(sectionNames.taxes)} />
|
||||
<Switch>
|
||||
<Route exact path={countryListPath} component={CountryList} />
|
||||
<Route
|
||||
exact
|
||||
path={countryTaxRatesPath(":code")}
|
||||
component={CountryTaxes}
|
||||
path={taxConfigurationListPath(":id")}
|
||||
component={TaxChannelsList}
|
||||
/>
|
||||
<Route path={taxConfigurationListPath()} component={TaxChannelsList} />
|
||||
<Route
|
||||
path={taxCountriesListPath(":id")}
|
||||
component={TaxCountriesList}
|
||||
/>
|
||||
<Route path={taxCountriesListPath()} component={TaxCountriesList} />
|
||||
<Route path={taxClassesListUrl(":id")} component={TaxClassesList} />
|
||||
<Route path={taxClassesListUrl()} component={TaxClassesList} />
|
||||
</Switch>
|
||||
</>
|
||||
);
|
||||
|
|
200
src/taxes/messages.ts
Normal file
200
src/taxes/messages.ts
Normal file
|
@ -0,0 +1,200 @@
|
|||
import { defineMessages } from "react-intl";
|
||||
|
||||
export const taxesMessages = defineMessages({
|
||||
taxClass: {
|
||||
id: "bDBiac",
|
||||
defaultMessage: "Tax class",
|
||||
description: "dropdown or column label",
|
||||
},
|
||||
channelsSection: {
|
||||
id: "b2DlTO",
|
||||
defaultMessage: "Channels",
|
||||
description: "Taxes section title",
|
||||
},
|
||||
countriesSection: {
|
||||
id: "ZAaXfz",
|
||||
defaultMessage: "Countries",
|
||||
description: "Taxes section title",
|
||||
},
|
||||
taxClassesSection: {
|
||||
id: "aJm/by",
|
||||
defaultMessage: "Tax classes",
|
||||
description: "Taxes section title",
|
||||
},
|
||||
channelList: {
|
||||
id: "yLfbSh",
|
||||
defaultMessage: "Channel name",
|
||||
description: "support label",
|
||||
},
|
||||
defaultSettings: {
|
||||
id: "pWClYm",
|
||||
defaultMessage: "Default settings",
|
||||
description: "card title",
|
||||
},
|
||||
countryExceptions: {
|
||||
id: "+2VydL",
|
||||
defaultMessage: "Country exceptions",
|
||||
description: "card title",
|
||||
},
|
||||
enteredPrices: {
|
||||
id: "puRlnN",
|
||||
defaultMessage: "Entered prices",
|
||||
description: "card subtitle",
|
||||
},
|
||||
renderedPrices: {
|
||||
id: "98Nw4g",
|
||||
defaultMessage: "Rendered prices",
|
||||
description: "card subtitle",
|
||||
},
|
||||
chargeTaxes: {
|
||||
id: "EYkW1J",
|
||||
defaultMessage: "Charge taxes for this channel",
|
||||
description: "checkbox label",
|
||||
},
|
||||
pricesWithTaxLabel: {
|
||||
id: "cVjewM",
|
||||
defaultMessage: "Product prices are entered with tax",
|
||||
description: "label for radio button",
|
||||
},
|
||||
pricesWithoutTaxLabel: {
|
||||
id: "QpBqa9",
|
||||
defaultMessage: "Product prices are entered without tax",
|
||||
description: "label for radio button",
|
||||
},
|
||||
addCountryLabel: {
|
||||
id: "/5r4he",
|
||||
defaultMessage: "Add country",
|
||||
description: "label for button",
|
||||
},
|
||||
countryNameHeader: {
|
||||
id: "D5Wtf/",
|
||||
defaultMessage: "Country name",
|
||||
description: "table header column",
|
||||
},
|
||||
chargeTaxesHeader: {
|
||||
id: "kXqn6A",
|
||||
defaultMessage: "Charge taxes",
|
||||
description: "table header column",
|
||||
},
|
||||
showGrossHeader: {
|
||||
id: "skklRz",
|
||||
defaultMessage: "Show gross prices in storefront",
|
||||
description: "table header column",
|
||||
},
|
||||
countryList: {
|
||||
id: "CFT171",
|
||||
defaultMessage: "Country list",
|
||||
description: "card header title",
|
||||
},
|
||||
taxClassRatesHeader: {
|
||||
id: "WIxSDm",
|
||||
defaultMessage: "{country} class rates",
|
||||
description: "card header",
|
||||
},
|
||||
searchTaxClasses: {
|
||||
id: "Ww69SE",
|
||||
defaultMessage: "Search tax classes",
|
||||
description: "search input placeholder",
|
||||
},
|
||||
taxNameHeader: {
|
||||
id: "qbcNjQ",
|
||||
defaultMessage: "Tax name",
|
||||
description: "table header column",
|
||||
},
|
||||
taxRateHeader: {
|
||||
id: "Nj9iSB",
|
||||
defaultMessage: "Tax rate",
|
||||
description: "table header column",
|
||||
},
|
||||
generalInformation: {
|
||||
id: "TfzIXS",
|
||||
defaultMessage: "General information",
|
||||
description: "tax classes card header",
|
||||
},
|
||||
taxRateName: {
|
||||
id: "720c51",
|
||||
defaultMessage: "Tax rate name",
|
||||
description: "tax classes name input placeholder",
|
||||
},
|
||||
taxClassRates: {
|
||||
id: "RqtZQ6",
|
||||
defaultMessage: "Tax class rates",
|
||||
description: "tax classes card header",
|
||||
},
|
||||
taxClassList: {
|
||||
id: "jMzyU8",
|
||||
defaultMessage: "Tax classes",
|
||||
description: "tax classes card header",
|
||||
},
|
||||
addTaxClassLabel: {
|
||||
id: "NlEVVT",
|
||||
defaultMessage: "Create class",
|
||||
description: "label for button",
|
||||
},
|
||||
taxClassNameHeader: {
|
||||
id: "/ILyIf",
|
||||
defaultMessage: "Tax class label",
|
||||
description: "tax classes menu header",
|
||||
},
|
||||
noExceptionsForChannel: {
|
||||
id: "u34css",
|
||||
defaultMessage: "There are no exceptions for this channel",
|
||||
description: "label for empty list in channels list",
|
||||
},
|
||||
chooseCountryDialogTitle: {
|
||||
id: "0V1q0d",
|
||||
defaultMessage: "Choose country you want to add",
|
||||
description: "add country dialog header",
|
||||
},
|
||||
country: {
|
||||
id: "UBuKZ9",
|
||||
defaultMessage: "Country",
|
||||
description: "searchbar placeholder",
|
||||
},
|
||||
noCountriesAssigned: {
|
||||
id: "0a0fLZ",
|
||||
defaultMessage: "There are no countries assigned",
|
||||
description: "countries list menu label when no countries are assigned",
|
||||
},
|
||||
addCountryToAccessClass: {
|
||||
id: "7U/NPm",
|
||||
defaultMessage: "Add country to access tax classes",
|
||||
description: "tax class rates list label when no country is selected",
|
||||
},
|
||||
countryDefaultRate: {
|
||||
id: "lnteBJ",
|
||||
defaultMessage: "Country default rate",
|
||||
description: "country rates list label for the default rate",
|
||||
},
|
||||
noRatesInTaxClass: {
|
||||
id: "ngAgBy",
|
||||
defaultMessage:
|
||||
"There are no countries using this tax class yet, use {tab} tab to assign tax rates.",
|
||||
description: "tax class rates list label when no countries are assigned",
|
||||
},
|
||||
newTaxClass: {
|
||||
id: "8BBMRj",
|
||||
defaultMessage: "New tax class",
|
||||
description: "default tax class name for new tax classes",
|
||||
},
|
||||
taxStrategyTaxApp: {
|
||||
id: "TJ7WHA",
|
||||
defaultMessage: "Use tax app",
|
||||
description: "tax strategy combobox choice",
|
||||
},
|
||||
taxStrategyFlatRates: {
|
||||
id: "4p3bjX",
|
||||
defaultMessage: "Use flat rates",
|
||||
description: "tax strategy combobox choice",
|
||||
},
|
||||
taxStrategyHint: {
|
||||
id: "6HHPFy",
|
||||
defaultMessage: "Select the method of tax calculation",
|
||||
description: "tax strategy combobox hint",
|
||||
},
|
||||
noTaxClasses: {
|
||||
id: "Rfk+8B",
|
||||
defaultMessage: "There are no tax classes",
|
||||
description: "tax classes menu label when there are no tax classes",
|
||||
},
|
||||
});
|
|
@ -1,28 +1,84 @@
|
|||
import { gql } from "@apollo/client";
|
||||
|
||||
export const updateTaxSettings = gql`
|
||||
mutation UpdateTaxSettings($input: ShopSettingsInput!) {
|
||||
shopSettingsUpdate(input: $input) {
|
||||
export const taxConfigurationUpdate = gql`
|
||||
mutation TaxConfigurationUpdate(
|
||||
$id: ID!
|
||||
$input: TaxConfigurationUpdateInput!
|
||||
) {
|
||||
taxConfigurationUpdate(id: $id, input: $input) {
|
||||
errors {
|
||||
...ShopSettingsUpdateErrorFragment
|
||||
...TaxConfigurationUpdateErrorFragment
|
||||
}
|
||||
shop {
|
||||
...ShopTaxes
|
||||
taxConfiguration {
|
||||
...TaxConfiguration
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const fetchTaxes = gql`
|
||||
mutation FetchTaxes {
|
||||
shopFetchTaxRates {
|
||||
export const taxCountryConfigurationUpdate = gql`
|
||||
mutation TaxCountryConfigurationUpdate(
|
||||
$countryCode: CountryCode!
|
||||
$updateTaxClassRates: [TaxClassRateInput!]!
|
||||
) {
|
||||
taxCountryConfigurationUpdate(
|
||||
countryCode: $countryCode
|
||||
updateTaxClassRates: $updateTaxClassRates
|
||||
) {
|
||||
errors {
|
||||
...ShopFetchTaxRatesErrorFragment
|
||||
...TaxCountryConfigurationUpdateErrorFragment
|
||||
}
|
||||
shop {
|
||||
countries {
|
||||
...Country
|
||||
}
|
||||
taxCountryConfiguration {
|
||||
...TaxCountryConfiguration
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const taxCountryConfigurationDelete = gql`
|
||||
mutation TaxCountryConfigurationDelete($countryCode: CountryCode!) {
|
||||
taxCountryConfigurationDelete(countryCode: $countryCode) {
|
||||
errors {
|
||||
...TaxCountryConfigurationDeleteErrorFragment
|
||||
}
|
||||
taxCountryConfiguration {
|
||||
...TaxCountryConfiguration
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const taxClassUpdate = gql`
|
||||
mutation TaxClassUpdate($id: ID!, $input: TaxClassUpdateInput!) {
|
||||
taxClassUpdate(id: $id, input: $input) {
|
||||
errors {
|
||||
...TaxClassUpdateErrorFragment
|
||||
}
|
||||
taxClass {
|
||||
...TaxClass
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const taxClassCreate = gql`
|
||||
mutation TaxClassCreate($input: TaxClassCreateInput!) {
|
||||
taxClassCreate(input: $input) {
|
||||
errors {
|
||||
...TaxClassCreateErrorFragment
|
||||
}
|
||||
taxClass {
|
||||
...TaxClass
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const taxClassDelete = gql`
|
||||
mutation TaxClassDelete($id: ID!) {
|
||||
taxClassDelete(id: $id) {
|
||||
errors {
|
||||
...TaxClassDeleteErrorFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
import { Card, Divider } from "@material-ui/core";
|
||||
import ListItemLink from "@saleor/components/ListItemLink";
|
||||
import Skeleton from "@saleor/components/Skeleton";
|
||||
import { TaxConfigurationFragment } from "@saleor/graphql";
|
||||
import { List, ListHeader, ListItem, ListItemCell } from "@saleor/macaw-ui";
|
||||
import { taxesMessages } from "@saleor/taxes/messages";
|
||||
import { taxConfigurationListUrl } from "@saleor/taxes/urls";
|
||||
import { isLastElement } from "@saleor/taxes/utils/utils";
|
||||
import clsx from "classnames";
|
||||
import React from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import { useStyles } from "./styles";
|
||||
|
||||
interface TaxChannelsMenuProps {
|
||||
configurations: TaxConfigurationFragment[] | undefined;
|
||||
selectedConfigurationId: string;
|
||||
}
|
||||
|
||||
export const TaxChannelsMenu: React.FC<TaxChannelsMenuProps> = ({
|
||||
configurations,
|
||||
selectedConfigurationId,
|
||||
}) => {
|
||||
const classes = useStyles();
|
||||
return (
|
||||
<Card>
|
||||
<List gridTemplate={["1fr"]}>
|
||||
<ListHeader>
|
||||
<ListItem className={classes.tableRow}>
|
||||
<ListItemCell>
|
||||
<FormattedMessage {...taxesMessages.channelList} />
|
||||
</ListItemCell>
|
||||
</ListItem>
|
||||
</ListHeader>
|
||||
<Divider />
|
||||
{configurations?.map((configuration, confIndex) => (
|
||||
<React.Fragment key={configuration.id}>
|
||||
<ListItemLink
|
||||
className={clsx(classes.clickable, classes.tableRow, {
|
||||
[classes.selected]:
|
||||
configuration.id === selectedConfigurationId,
|
||||
})}
|
||||
href={taxConfigurationListUrl(configuration.id)}
|
||||
>
|
||||
<ListItemCell className={classes.ellipsis}>
|
||||
{configuration.channel.name}
|
||||
</ListItemCell>
|
||||
</ListItemLink>
|
||||
{!isLastElement(configurations, confIndex) && <Divider />}
|
||||
</React.Fragment>
|
||||
)) ?? <Skeleton />}
|
||||
</List>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default TaxChannelsMenu;
|
2
src/taxes/pages/TaxChannelsPage/TaxChannelsMenu/index.ts
Normal file
2
src/taxes/pages/TaxChannelsPage/TaxChannelsMenu/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./TaxChannelsMenu";
|
||||
export * from "./TaxChannelsMenu";
|
30
src/taxes/pages/TaxChannelsPage/TaxChannelsMenu/styles.ts
Normal file
30
src/taxes/pages/TaxChannelsPage/TaxChannelsMenu/styles.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
|
||||
export const useStyles = makeStyles(
|
||||
theme => ({
|
||||
clickable: {
|
||||
cursor: "pointer",
|
||||
},
|
||||
ellipsis: {
|
||||
textOverflow: "ellipsis",
|
||||
overflow: "hidden",
|
||||
},
|
||||
tableRow: {
|
||||
minHeight: "48px",
|
||||
"&::after": {
|
||||
display: "none",
|
||||
},
|
||||
},
|
||||
selected: {
|
||||
"&&&:before": {
|
||||
display: "visible",
|
||||
position: "absolute",
|
||||
left: 0,
|
||||
width: "4px",
|
||||
height: "100%",
|
||||
backgroundColor: theme.palette.saleor.active[1],
|
||||
},
|
||||
},
|
||||
}),
|
||||
{ name: "TaxChannelsMenu" },
|
||||
);
|
37
src/taxes/pages/TaxChannelsPage/TaxChannelsPage.stories.tsx
Normal file
37
src/taxes/pages/TaxChannelsPage/TaxChannelsPage.stories.tsx
Normal file
|
@ -0,0 +1,37 @@
|
|||
import { countries } from "@saleor/fixtures";
|
||||
import { CountryFragment } from "@saleor/graphql";
|
||||
import Decorator from "@saleor/storybook/Decorator";
|
||||
import { taxConfigurations } from "@saleor/taxes/fixtures";
|
||||
import { storiesOf } from "@storybook/react";
|
||||
import React from "react";
|
||||
|
||||
import TaxChannelsPage from "./TaxChannelsPage";
|
||||
|
||||
export const castedCountries = countries.map(
|
||||
({ code, name }): CountryFragment => ({
|
||||
code,
|
||||
country: name,
|
||||
__typename: "CountryDisplay",
|
||||
}),
|
||||
);
|
||||
|
||||
const props = {
|
||||
taxConfigurations,
|
||||
selectedConfigurationId: taxConfigurations[0].id,
|
||||
handleTabChange: () => undefined,
|
||||
allCountries: castedCountries,
|
||||
isDialogOpen: false,
|
||||
openDialog: () => undefined,
|
||||
closeDialog: () => undefined,
|
||||
onSubmit: () => undefined,
|
||||
savebarState: "default" as const,
|
||||
disabled: false,
|
||||
};
|
||||
|
||||
storiesOf("Views / Taxes / Channels view", module)
|
||||
.addDecorator(Decorator)
|
||||
.add("loading", () => (
|
||||
<TaxChannelsPage {...props} taxConfigurations={undefined} />
|
||||
))
|
||||
.add("default", () => <TaxChannelsPage {...props} />)
|
||||
.add("add country", () => <TaxChannelsPage {...props} isDialogOpen={true} />);
|
303
src/taxes/pages/TaxChannelsPage/TaxChannelsPage.tsx
Normal file
303
src/taxes/pages/TaxChannelsPage/TaxChannelsPage.tsx
Normal file
|
@ -0,0 +1,303 @@
|
|||
import { Card, CardContent, Divider } from "@material-ui/core";
|
||||
import VerticalSpacer from "@saleor/apps/components/VerticalSpacer";
|
||||
import CardTitle from "@saleor/components/CardTitle";
|
||||
import Container from "@saleor/components/Container";
|
||||
import Form from "@saleor/components/Form";
|
||||
import Grid from "@saleor/components/Grid";
|
||||
import PageHeader from "@saleor/components/PageHeader";
|
||||
import Savebar from "@saleor/components/Savebar";
|
||||
import Skeleton from "@saleor/components/Skeleton";
|
||||
import { configurationMenuUrl } from "@saleor/configuration";
|
||||
import {
|
||||
CountryCode,
|
||||
CountryFragment,
|
||||
TaxCalculationStrategy,
|
||||
TaxConfigurationFragment,
|
||||
TaxConfigurationPerCountryFragment,
|
||||
TaxConfigurationUpdateInput,
|
||||
} from "@saleor/graphql";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import { sectionNames } from "@saleor/intl";
|
||||
import {
|
||||
Button,
|
||||
ConfirmButtonTransitionState,
|
||||
List,
|
||||
ListHeader,
|
||||
ListItem,
|
||||
ListItemCell,
|
||||
PageTab,
|
||||
PageTabs,
|
||||
} from "@saleor/macaw-ui";
|
||||
import TaxCountryDialog from "@saleor/taxes/components/TaxCountryDialog";
|
||||
import { taxesMessages } from "@saleor/taxes/messages";
|
||||
import { isLastElement } from "@saleor/taxes/utils/utils";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import { useStyles } from "./styles";
|
||||
import TaxChannelsMenu from "./TaxChannelsMenu";
|
||||
import TaxCountryExceptionListItem from "./TaxCountryExceptionListItem";
|
||||
import TaxSettingsCard from "./TaxSettingsCard";
|
||||
|
||||
interface TaxChannelsPageProps {
|
||||
taxConfigurations: TaxConfigurationFragment[] | undefined;
|
||||
selectedConfigurationId: string;
|
||||
handleTabChange: (tab: string) => void;
|
||||
allCountries: CountryFragment[] | undefined;
|
||||
isDialogOpen: boolean;
|
||||
openDialog: (action?: string) => void;
|
||||
closeDialog: () => void;
|
||||
onSubmit: (input: TaxConfigurationUpdateInput) => void;
|
||||
savebarState: ConfirmButtonTransitionState;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
export interface TaxConfigurationFormData {
|
||||
chargeTaxes: boolean;
|
||||
taxCalculationStrategy: TaxCalculationStrategy;
|
||||
displayGrossPrices: boolean;
|
||||
pricesEnteredWithTax: boolean;
|
||||
updateCountriesConfiguration: TaxConfigurationPerCountryFragment[];
|
||||
removeCountriesConfiguration: CountryCode[];
|
||||
}
|
||||
|
||||
export const TaxChannelsPage: React.FC<TaxChannelsPageProps> = props => {
|
||||
const {
|
||||
taxConfigurations,
|
||||
selectedConfigurationId,
|
||||
handleTabChange,
|
||||
allCountries,
|
||||
isDialogOpen,
|
||||
openDialog,
|
||||
closeDialog,
|
||||
onSubmit,
|
||||
savebarState,
|
||||
disabled,
|
||||
} = props;
|
||||
|
||||
const intl = useIntl();
|
||||
const classes = useStyles();
|
||||
const navigate = useNavigator();
|
||||
|
||||
const currentTaxConfiguration = taxConfigurations?.find(
|
||||
taxConfigurations => taxConfigurations.id === selectedConfigurationId,
|
||||
);
|
||||
|
||||
const initialForm: TaxConfigurationFormData = {
|
||||
chargeTaxes: currentTaxConfiguration?.chargeTaxes ?? false,
|
||||
taxCalculationStrategy: currentTaxConfiguration?.taxCalculationStrategy,
|
||||
displayGrossPrices: currentTaxConfiguration?.displayGrossPrices ?? false,
|
||||
pricesEnteredWithTax:
|
||||
currentTaxConfiguration?.pricesEnteredWithTax ?? false,
|
||||
updateCountriesConfiguration: currentTaxConfiguration?.countries ?? [],
|
||||
removeCountriesConfiguration: [],
|
||||
};
|
||||
|
||||
const handleSubmit = (data: TaxConfigurationFormData) => {
|
||||
const { updateCountriesConfiguration, removeCountriesConfiguration } = data;
|
||||
const parsedUpdate: TaxConfigurationUpdateInput["updateCountriesConfiguration"] = updateCountriesConfiguration.map(
|
||||
config => ({
|
||||
countryCode: config.country.code as CountryCode,
|
||||
chargeTaxes: config.chargeTaxes,
|
||||
taxCalculationStrategy: config.taxCalculationStrategy,
|
||||
displayGrossPrices: config.displayGrossPrices,
|
||||
}),
|
||||
);
|
||||
onSubmit({
|
||||
chargeTaxes: data.chargeTaxes,
|
||||
taxCalculationStrategy: data.chargeTaxes
|
||||
? data.taxCalculationStrategy
|
||||
: null,
|
||||
displayGrossPrices: data.displayGrossPrices,
|
||||
pricesEnteredWithTax: data.pricesEnteredWithTax,
|
||||
updateCountriesConfiguration: parsedUpdate,
|
||||
removeCountriesConfiguration,
|
||||
});
|
||||
};
|
||||
|
||||
const taxStrategyChoices = [
|
||||
{
|
||||
label: intl.formatMessage(taxesMessages.taxStrategyTaxApp),
|
||||
value: TaxCalculationStrategy.TAX_APP,
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage(taxesMessages.taxStrategyFlatRates),
|
||||
value: TaxCalculationStrategy.FLAT_RATES,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Form initial={initialForm} onSubmit={handleSubmit} mergeData={false}>
|
||||
{({ data, change, submit, set, triggerChange }) => {
|
||||
const countryExceptions = data.updateCountriesConfiguration;
|
||||
|
||||
const handleExceptionChange = (event, index) => {
|
||||
const { name, value } = event.target;
|
||||
const currentExceptions = [...data.updateCountriesConfiguration];
|
||||
const exceptionToChange = {
|
||||
...data.updateCountriesConfiguration[index],
|
||||
[name]: value,
|
||||
};
|
||||
currentExceptions[index] = exceptionToChange;
|
||||
triggerChange();
|
||||
set({ updateCountriesConfiguration: currentExceptions });
|
||||
};
|
||||
|
||||
const handleCountryChange = (country: CountryFragment) => {
|
||||
closeDialog();
|
||||
const input: TaxConfigurationPerCountryFragment = {
|
||||
__typename: "TaxConfigurationPerCountry",
|
||||
country,
|
||||
chargeTaxes: data.chargeTaxes,
|
||||
displayGrossPrices: data.displayGrossPrices,
|
||||
taxCalculationStrategy: data.taxCalculationStrategy,
|
||||
};
|
||||
const currentExceptions = data.updateCountriesConfiguration;
|
||||
triggerChange();
|
||||
set({
|
||||
updateCountriesConfiguration: [input, ...currentExceptions],
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<PageHeader title={intl.formatMessage(sectionNames.taxes)} />
|
||||
<PageTabs value="channels" onChange={handleTabChange}>
|
||||
<PageTab
|
||||
label={intl.formatMessage(taxesMessages.channelsSection)}
|
||||
value="channels"
|
||||
/>
|
||||
<PageTab
|
||||
label={intl.formatMessage(taxesMessages.countriesSection)}
|
||||
value="countries"
|
||||
/>
|
||||
<PageTab
|
||||
label={intl.formatMessage(taxesMessages.taxClassesSection)}
|
||||
value="tax-classes"
|
||||
/>
|
||||
</PageTabs>
|
||||
<VerticalSpacer spacing={2} />
|
||||
<Grid variant="inverted">
|
||||
<div>
|
||||
<TaxChannelsMenu
|
||||
configurations={taxConfigurations}
|
||||
selectedConfigurationId={selectedConfigurationId}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<TaxSettingsCard
|
||||
values={data}
|
||||
strategyChoices={taxStrategyChoices}
|
||||
onChange={change}
|
||||
/>
|
||||
<VerticalSpacer spacing={3} />
|
||||
<Card>
|
||||
<CardTitle
|
||||
className={classes.toolbarMargin}
|
||||
title={intl.formatMessage(taxesMessages.countryExceptions)}
|
||||
toolbar={
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => openDialog("add-country")}
|
||||
>
|
||||
<FormattedMessage {...taxesMessages.addCountryLabel} />
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
{countryExceptions?.length === 0 ? (
|
||||
<CardContent>
|
||||
<FormattedMessage
|
||||
{...taxesMessages.noExceptionsForChannel}
|
||||
/>
|
||||
</CardContent>
|
||||
) : (
|
||||
<List gridTemplate={["4fr 3fr 3fr 1fr"]}>
|
||||
<ListHeader>
|
||||
<ListItem>
|
||||
<ListItemCell>
|
||||
<FormattedMessage
|
||||
{...taxesMessages.countryNameHeader}
|
||||
/>
|
||||
</ListItemCell>
|
||||
<ListItemCell className={classes.left}>
|
||||
<FormattedMessage
|
||||
{...taxesMessages.chargeTaxesHeader}
|
||||
/>
|
||||
</ListItemCell>
|
||||
<ListItemCell className={classes.center}>
|
||||
<FormattedMessage
|
||||
{...taxesMessages.showGrossHeader}
|
||||
/>
|
||||
</ListItemCell>
|
||||
<ListItemCell>
|
||||
{/* This is required for the header row to be aligned with list items */}
|
||||
<div className={classes.dummy}></div>
|
||||
</ListItemCell>
|
||||
</ListItem>
|
||||
</ListHeader>
|
||||
<Divider />
|
||||
{countryExceptions?.map((country, countryIndex) => (
|
||||
<TaxCountryExceptionListItem
|
||||
divider={
|
||||
!isLastElement(countryExceptions, countryIndex)
|
||||
}
|
||||
strategyChoices={taxStrategyChoices}
|
||||
country={country}
|
||||
key={country.country.code}
|
||||
onDelete={() => {
|
||||
const currentRemovals =
|
||||
data.removeCountriesConfiguration;
|
||||
const currentExceptions = [
|
||||
...data.updateCountriesConfiguration,
|
||||
];
|
||||
set({
|
||||
removeCountriesConfiguration: [
|
||||
...currentRemovals,
|
||||
country.country.code as CountryCode,
|
||||
],
|
||||
updateCountriesConfiguration: currentExceptions.filter(
|
||||
exception =>
|
||||
exception.country.code !==
|
||||
country.country.code,
|
||||
),
|
||||
});
|
||||
triggerChange();
|
||||
}}
|
||||
onChange={event =>
|
||||
handleExceptionChange(event, countryIndex)
|
||||
}
|
||||
/>
|
||||
)) ?? <Skeleton />}
|
||||
</List>
|
||||
)}
|
||||
</Card>
|
||||
</div>
|
||||
</Grid>
|
||||
<Savebar
|
||||
state={savebarState}
|
||||
disabled={disabled}
|
||||
onSubmit={submit}
|
||||
onCancel={() => navigate(configurationMenuUrl)}
|
||||
/>
|
||||
{allCountries && (
|
||||
<TaxCountryDialog
|
||||
open={isDialogOpen}
|
||||
countries={allCountries
|
||||
.filter(
|
||||
({ code }) =>
|
||||
!countryExceptions?.some(
|
||||
({ country }) => country.code === code,
|
||||
),
|
||||
)
|
||||
.map(country => ({ checked: false, ...country }))}
|
||||
onConfirm={handleCountryChange}
|
||||
onClose={closeDialog}
|
||||
/>
|
||||
)}
|
||||
</Container>
|
||||
);
|
||||
}}
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
export default TaxChannelsPage;
|
|
@ -0,0 +1,75 @@
|
|||
import { Divider } from "@material-ui/core";
|
||||
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
|
||||
import SingleSelectField, {
|
||||
Choice,
|
||||
} from "@saleor/components/SingleSelectField";
|
||||
import {
|
||||
TaxConfigurationPerCountryFragment,
|
||||
TaxConfigurationUpdateInput,
|
||||
} from "@saleor/graphql";
|
||||
import { FormChange } from "@saleor/hooks/useForm";
|
||||
import {
|
||||
DeleteIcon,
|
||||
IconButton,
|
||||
ListItem,
|
||||
ListItemCell,
|
||||
} from "@saleor/macaw-ui";
|
||||
import React from "react";
|
||||
|
||||
import { useStyles } from "../styles";
|
||||
|
||||
interface TaxCountryExceptionListItemProps {
|
||||
country: TaxConfigurationPerCountryFragment | undefined;
|
||||
onDelete: () => void;
|
||||
onChange: FormChange;
|
||||
divider: boolean;
|
||||
strategyChoices: Choice[];
|
||||
}
|
||||
|
||||
export const TaxCountryExceptionListItem: React.FC<TaxCountryExceptionListItemProps> = ({
|
||||
country,
|
||||
onDelete,
|
||||
onChange,
|
||||
strategyChoices,
|
||||
divider = true,
|
||||
}) => {
|
||||
const classes = useStyles();
|
||||
return (
|
||||
<>
|
||||
<ListItem hover={false} className={classes.noDivider}>
|
||||
<ListItemCell>{country.country.country}</ListItemCell>
|
||||
<ListItemCell className={classes.center}>
|
||||
<ControlledCheckbox
|
||||
className={classes.center}
|
||||
checked={country.chargeTaxes}
|
||||
name={"chargeTaxes" as keyof TaxConfigurationUpdateInput}
|
||||
onChange={onChange}
|
||||
/>
|
||||
<SingleSelectField
|
||||
className={classes.selectField}
|
||||
choices={strategyChoices}
|
||||
disabled={!country.chargeTaxes}
|
||||
value={country.taxCalculationStrategy}
|
||||
name={"taxCalculationStrategy" as keyof TaxConfigurationUpdateInput}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</ListItemCell>
|
||||
<ListItemCell className={classes.center}>
|
||||
<ControlledCheckbox
|
||||
className={classes.center}
|
||||
checked={country.displayGrossPrices}
|
||||
name={"displayGrossPrices" as keyof TaxConfigurationUpdateInput}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</ListItemCell>
|
||||
<ListItemCell>
|
||||
<IconButton onClick={onDelete} variant="secondary">
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</ListItemCell>
|
||||
</ListItem>
|
||||
{divider && <Divider />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default TaxCountryExceptionListItem;
|
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./TaxCountryExceptionListItem";
|
||||
export * from "./TaxCountryExceptionListItem";
|
|
@ -0,0 +1,117 @@
|
|||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
Divider,
|
||||
FormControlLabel,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
Typography,
|
||||
} from "@material-ui/core";
|
||||
import CardTitle from "@saleor/components/CardTitle";
|
||||
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
|
||||
import Grid from "@saleor/components/Grid";
|
||||
import SingleSelectField, {
|
||||
Choice,
|
||||
} from "@saleor/components/SingleSelectField";
|
||||
import { TaxConfigurationUpdateInput } from "@saleor/graphql";
|
||||
import { FormChange } from "@saleor/hooks/useForm";
|
||||
import { taxesMessages } from "@saleor/taxes/messages";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import { TaxConfigurationFormData } from "../TaxChannelsPage";
|
||||
import { useStyles } from "./styles";
|
||||
|
||||
export interface TaxSettingsCardProps {
|
||||
values: TaxConfigurationFormData;
|
||||
strategyChoices: Choice[];
|
||||
onChange: FormChange;
|
||||
}
|
||||
|
||||
export const TaxSettingsCard: React.FC<TaxSettingsCardProps> = ({
|
||||
values,
|
||||
strategyChoices,
|
||||
onChange,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardTitle title={intl.formatMessage(taxesMessages.defaultSettings)} />
|
||||
<CardContent>
|
||||
<Typography className={classes.supportHeader}>
|
||||
<FormattedMessage {...taxesMessages.chargeTaxesHeader} />
|
||||
</Typography>
|
||||
<div className={classes.taxStrategySection}>
|
||||
<ControlledCheckbox
|
||||
checked={values.chargeTaxes}
|
||||
name={"chargeTaxes" as keyof TaxConfigurationUpdateInput}
|
||||
onChange={onChange}
|
||||
label={intl.formatMessage(taxesMessages.chargeTaxes)}
|
||||
/>
|
||||
<div className={classes.singleSelectWrapper}>
|
||||
<span className={classes.hint}>
|
||||
<FormattedMessage {...taxesMessages.taxStrategyHint} />{" "}
|
||||
</span>
|
||||
<SingleSelectField
|
||||
className={classes.singleSelectField}
|
||||
choices={strategyChoices}
|
||||
disabled={!values.chargeTaxes}
|
||||
value={values.taxCalculationStrategy}
|
||||
name={
|
||||
"taxCalculationStrategy" as keyof TaxConfigurationUpdateInput
|
||||
}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
<Divider />
|
||||
<CardContent>
|
||||
<Grid variant="uniform">
|
||||
<RadioGroup
|
||||
value={values.pricesEnteredWithTax}
|
||||
name={"pricesEnteredWithTax" as keyof TaxConfigurationUpdateInput}
|
||||
onChange={e => {
|
||||
onChange({
|
||||
target: {
|
||||
name: e.target.name,
|
||||
value: e.target.value === "true",
|
||||
},
|
||||
});
|
||||
}}
|
||||
className={classes.showCheckboxShadows}
|
||||
>
|
||||
<Typography className={classes.supportHeader}>
|
||||
<FormattedMessage {...taxesMessages.enteredPrices} />
|
||||
</Typography>
|
||||
<FormControlLabel
|
||||
value={true}
|
||||
control={<Radio />}
|
||||
label={intl.formatMessage(taxesMessages.pricesWithTaxLabel)}
|
||||
/>
|
||||
<FormControlLabel
|
||||
value={false}
|
||||
control={<Radio />}
|
||||
label={intl.formatMessage(taxesMessages.pricesWithoutTaxLabel)}
|
||||
/>
|
||||
</RadioGroup>
|
||||
<div className={classes.showCheckboxShadows}>
|
||||
<Typography className={classes.supportHeader}>
|
||||
<FormattedMessage {...taxesMessages.renderedPrices} />
|
||||
</Typography>
|
||||
<ControlledCheckbox
|
||||
label={intl.formatMessage(taxesMessages.showGrossHeader)}
|
||||
name={"displayGrossPrices" as keyof TaxConfigurationUpdateInput}
|
||||
checked={values.displayGrossPrices}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</div>
|
||||
</Grid>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default TaxSettingsCard;
|
2
src/taxes/pages/TaxChannelsPage/TaxSettingsCard/index.ts
Normal file
2
src/taxes/pages/TaxChannelsPage/TaxSettingsCard/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./TaxSettingsCard";
|
||||
export * from "./TaxSettingsCard";
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue