feat: Translations UI (#14217)

* Add Translations route and guard it with feature flag. Empty TranslationsList main component to test route.

* Translation list component

* Add translations namespace to js-sdk

* Translations hook

* Avoid incorrectly throwing when updating and locale not included

* Translations bulk editor component v1

* Add batch method to translations namespace in js-sdk

* Protect translations edit route with feature flag

* Handle reference_id search param

* Replace entity_type entity_id for reference reference_id

* Manage translations from product detail page

* Dynamically resolve base hook for retrieving translations

* Fix navigation from outside settings/translations

* Navigation to bulk editor from product list

* Add Translations to various product module types

* Type useVariants hook

* Handle product module entities translations in bulk editor

* Fix categories issue in datagrid due to column clash

* Translations bulk navigation from remaining entities detail pages

* Add remaining bulk editor navigation for list components. Fix invalidation query for variants

* Expandable text cell v1

* Popover approach

* Add *supported_locales.locale to default fields in stores list endpoint

* Make popover more aligned to excell approach

* Correctly tie the focused cell anchor to popover

* Rework translations main component UI

* Fix link def export

* Swap axis for translations datagrid

* Add original column to translations data grid

* Remove is_default store locale from backend

* Remove ldefault locale from ui

* Type

* Add changeset

* Comments

* Remove unused import

* Add translations to admin product categories endpoint allowed fields

* Default locale removal

* Lazy loading with infinite scroll data grid

* Infinite list hook and implementation for products and variants

* Translation bulk editor lazy loaded datagrid

* Prevent scroll when forcing focus, to avoid scrollTop reset on infinite loading

* Confgiure placeholder data

* Cleanup logs and refactor

* Infinite query hooks for translatable entities

* Batch requests for translation batch endpoint

* Clean up

* Update icon

* Add query param validator in settings endpoint

* Settings endpoint param type

* JS sdk methods for translation settings and statistics

* Retrieve translatable fields and entities dynamically. Remove hardcoded information from tranlations list

* Resolve translation aggregate completion dynamically

* Format label

* Resolve bulk editor header label dynamically

* Include type and collection in translations config

* Avoid showing product option and option values in translatable entities list

* Translations

* Make translations bulk editor content columns wider

* Disable hiding Original column in translations bulk editor

* Adjust translations completion styles

* Fix translations config screen

* Locale selector switcher with conditional locale column rendering

* Batch one locale at a time

* Hooks save actions to footer buttons

* Reset dirty state on save

* Dynamic row heights for translations bulk editor. Replace expandable cell for text cell, with additional isMultiLine config

* Make columns take as much responsive width as possible and divide equally

* more padding to avoid unnecessary horizontal scrollbar

* Update statistics graphs

* Translations

* Statistics graphs translations

* Translation, text sizes and weight in stat graphs

* Conditionally show/hide column visibility dropdown in datagrid

* Allow to pass component to place in DataGrid header and use it in translations bulk editor

* Center text regardless of multiLine config

* Apply full height to datagrid cell regardles of multiSelect config

* Colors and fonts

* Handle key down for text area in text cell

* MultilineCell with special keydown handling

* Rework form schema to match new single locale edit flow

* Update created translations to include id, to avoid duplication issue on subsequent calls

* Handle space key for text cells

* Finish hooking up multiline cell with key and mouse events

* Disable remaining buttons when batch is ongoing

* Style updates

* Update style

* Refactor to make form updates and sync/comparison with server data more comprehensive and robust

* Update styles

* Bars and labels alignment

* Add languages tooltip

* Styles and translation

* Navigation update

* Disable edit translations button when no reference count

* Invert colors

---------

Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>
Co-authored-by: Adrien de Peretti <adrien.deperetti@gmail.com>
This commit is contained in:
Nicolas Gorga
2025-12-17 09:36:50 -03:00
committed by GitHub
parent c1a5390fc6
commit 3d1330ebb9
69 changed files with 3595 additions and 112 deletions

View File

@@ -8,7 +8,7 @@ import { RouteModalProvider } from "../route-modal-provider/route-provider"
import { StackedModalProvider } from "../stacked-modal-provider"
type RouteFocusModalProps = PropsWithChildren<{
prev?: string | Partial<Path>
prev?: string | Partial<Path> | number
}>
const Root = ({ prev = "..", children }: RouteFocusModalProps) => {
@@ -16,7 +16,8 @@ const Root = ({ prev = "..", children }: RouteFocusModalProps) => {
const [open, setOpen] = useState(false)
const [stackedModalOpen, onStackedModalOpen] = useState(false)
const to = useStateAwareTo(prev)
const to: string | Partial<Path> | number =
typeof prev === "number" ? prev : useStateAwareTo(prev)
/**
* Open the modal when the component mounts. This
@@ -34,7 +35,11 @@ const Root = ({ prev = "..", children }: RouteFocusModalProps) => {
const handleOpenChange = (open: boolean) => {
if (!open) {
document.body.style.pointerEvents = "auto"
navigate(to, { replace: true })
if (typeof to === "number") {
navigate(to)
} else {
navigate(to, { replace: true })
}
return
}

View File

@@ -24,7 +24,12 @@ export const RouteModalForm = <TFieldValues extends FieldValues = any>({
} = form
const blocker = useBlocker(({ currentLocation, nextLocation }) => {
const { isSubmitSuccessful } = nextLocation.state || {}
// Check both nextLocation and currentLocation state for successful submission
// This handles browser history navigation (-1) where we set state on current location
const { isSubmitSuccessful: nextIsSuccessful } = nextLocation.state || {}
const { isSubmitSuccessful: currentIsSuccessful } =
currentLocation.state || {}
const isSubmitSuccessful = nextIsSuccessful || currentIsSuccessful
if (isSubmitSuccessful) {
onClose?.(true)

View File

@@ -1,9 +1,9 @@
import { PropsWithChildren, useCallback, useMemo, useState } from "react"
import { Path, useNavigate } from "react-router-dom"
import { Path, useLocation, useNavigate } from "react-router-dom"
import { RouteModalProviderContext } from "./route-modal-context"
type RouteModalProviderProps = PropsWithChildren<{
prev: string | Partial<Path>
prev: string | Partial<Path> | number
}>
export const RouteModalProvider = ({
@@ -11,15 +11,27 @@ export const RouteModalProvider = ({
children,
}: RouteModalProviderProps) => {
const navigate = useNavigate()
const location = useLocation()
const [closeOnEscape, setCloseOnEscape] = useState(true)
const handleSuccess = useCallback(
(path?: string) => {
const to = path || prev
navigate(to, { replace: true, state: { isSubmitSuccessful: true } })
if (typeof to === "number") {
// Replace current location with success state, then navigate back
navigate(location.pathname + location.search, {
replace: true,
state: { ...location.state, isSubmitSuccessful: true },
})
setTimeout(() => {
navigate(to)
}, 0)
} else {
navigate(to, { replace: true, state: { isSubmitSuccessful: true } })
}
},
[navigate, prev]
[navigate, prev, location]
)
const value = useMemo(