@spree/dashboard-core 6.0.2 → 6.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/vite/index.js +81 -5
- package/package.json +24 -24
- package/src/api-client.ts +7 -1
- package/src/client.ts +2 -0
- package/src/components/address-form-dialog.tsx +75 -28
- package/src/components/app-shell.tsx +5 -4
- package/src/components/app-sidebar.tsx +2 -1
- package/src/components/bulk-action-bar.tsx +6 -5
- package/src/components/country-state-fields.tsx +3 -0
- package/src/components/currency-select.tsx +6 -4
- package/src/components/developer-inspector-drawer.tsx +322 -0
- package/src/components/file-upload-field.tsx +64 -37
- package/src/components/image-upload-field.tsx +4 -0
- package/src/components/import-wizard-dialog.tsx +14 -12
- package/src/components/invitation-acceptance.tsx +15 -11
- package/src/components/media-editor-dialog.tsx +131 -0
- package/src/components/media-picker-dialog.tsx +866 -0
- package/src/components/media-picker-sheet.tsx +13 -300
- package/src/components/mobile-top-bar.tsx +1 -1
- package/src/components/nav-main.tsx +13 -2
- package/src/components/page-header.tsx +57 -13
- package/src/components/page-tabs.tsx +1 -1
- package/src/components/product-stock-summary.tsx +58 -0
- package/src/components/read-row.tsx +38 -0
- package/src/components/resource-combobox.tsx +1 -1
- package/src/components/resource-image-field.tsx +150 -0
- package/src/components/resource-picker-sheet.tsx +2 -2
- package/src/components/resource-table.tsx +161 -40
- package/src/components/section-heading.tsx +2 -2
- package/src/components/settings-container.tsx +19 -0
- package/src/components/settings-fields.tsx +165 -0
- package/src/components/settings-section.tsx +46 -0
- package/src/components/settings-sidebar.tsx +35 -18
- package/src/components/sheet-top-bar.tsx +7 -8
- package/src/components/sidebar-search.tsx +11 -12
- package/src/components/sidebar-user.tsx +39 -44
- package/src/components/skip-link.tsx +1 -1
- package/src/components/store-switcher.tsx +1 -1
- package/src/components/table-toolbar.tsx +194 -331
- package/src/components/timezone-combobox.tsx +119 -0
- package/src/components/top-bar-breadcrumbs.tsx +1 -1
- package/src/hooks/use-stock-locations.ts +6 -2
- package/src/hooks/use-table-density.ts +50 -0
- package/src/index.ts +20 -2
- package/src/lib/curl.test.ts +18 -0
- package/src/lib/curl.ts +28 -0
- package/src/lib/developer-inspector.test.ts +169 -0
- package/src/lib/developer-inspector.ts +134 -0
- package/src/lib/docs.ts +3 -2
- package/src/lib/fulfillment-items.test.ts +51 -0
- package/src/lib/fulfillment-items.ts +53 -0
- package/src/lib/request-snippets.ts +41 -0
- package/src/lib/table-registry.ts +13 -6
- package/src/lib/timestamp-columns.tsx +1 -1
- package/src/locales/ar.json +128 -14
- package/src/locales/de.json +128 -14
- package/src/locales/en.json +128 -14
- package/src/locales/es.json +128 -14
- package/src/locales/fr.json +128 -14
- package/src/locales/ja.json +128 -14
- package/src/locales/pl.json +130 -14
- package/src/locales/vi.json +128 -14
- package/src/locales/zh-CN.json +128 -14
- package/src/pages/settings-index.tsx +79 -48
- package/src/pages/stock-locations.tsx +14 -6
- package/src/products/digital-asset-edit-sheet.tsx +1 -1
- package/src/products/digital-asset-provider-sheet.tsx +1 -1
- package/src/products/digital-assets-card.tsx +7 -7
- package/src/products/inventory-section.tsx +2 -2
- package/src/products/media-edit-sheet.tsx +136 -133
- package/src/products/product-form-cards.tsx +204 -73
- package/src/products/product-form-mapping.test.ts +42 -1
- package/src/products/product-form-mapping.ts +20 -0
- package/src/products/product-schema.ts +8 -0
- package/src/products/product-types.ts +2 -0
- package/src/products/use-product-form-data.ts +30 -3
- package/src/products/variant-edit-sheet.tsx +3 -3
- package/src/products/variants-options-builder.tsx +13 -8
- package/src/products/variants-section.tsx +196 -4
- package/src/providers/auth-provider.tsx +2 -0
- package/src/providers/developer-inspector-provider.tsx +145 -0
- package/src/providers/json-link-resolver-provider.tsx +27 -0
- package/src/sellers/seller-schema.ts +107 -0
- package/src/tables/package-types.tsx +3 -3
- package/src/tables/stock-locations.tsx +15 -14
- package/src/virtual.d.ts +4 -0
- package/src/vite/index.ts +103 -5
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
fulfillmentItemRows,
|
|
5
5
|
type GroupableFulfillment,
|
|
6
6
|
type GroupableLineItem,
|
|
7
|
+
postSaleItemRow,
|
|
7
8
|
unfulfilledItemRows,
|
|
8
9
|
} from './fulfillment-items'
|
|
9
10
|
|
|
@@ -16,6 +17,7 @@ function lineItem(overrides: Partial<GroupableLineItem> = {}): GroupableLineItem
|
|
|
16
17
|
thumbnail_url: 'https://example.test/shirt.jpg',
|
|
17
18
|
price: '10.00',
|
|
18
19
|
currency: 'USD',
|
|
20
|
+
product_id: 'prod_1',
|
|
19
21
|
...overrides,
|
|
20
22
|
}
|
|
21
23
|
}
|
|
@@ -45,6 +47,7 @@ describe('fulfillmentItemRows', () => {
|
|
|
45
47
|
optionsText: 'Blue / M',
|
|
46
48
|
thumbnailUrl: 'https://example.test/shirt.jpg',
|
|
47
49
|
displayPrice: '$10.00',
|
|
50
|
+
productId: 'prod_1',
|
|
48
51
|
quantity: 1,
|
|
49
52
|
},
|
|
50
53
|
])
|
|
@@ -107,6 +110,7 @@ describe('fulfillmentItemRows', () => {
|
|
|
107
110
|
optionsText: 'White',
|
|
108
111
|
thumbnailUrl: null,
|
|
109
112
|
displayPrice: null,
|
|
113
|
+
productId: null,
|
|
110
114
|
quantity: 1,
|
|
111
115
|
},
|
|
112
116
|
])
|
|
@@ -241,3 +245,50 @@ describe('fulfilledQuantities', () => {
|
|
|
241
245
|
expect(quantities.size).toBe(0)
|
|
242
246
|
})
|
|
243
247
|
})
|
|
248
|
+
|
|
249
|
+
describe('postSaleItemRow', () => {
|
|
250
|
+
it('takes the name, image, price and product from the order line it came from', () => {
|
|
251
|
+
const row = postSaleItemRow(
|
|
252
|
+
'rli_1',
|
|
253
|
+
{ line_item_id: 'li_1', variant_id: 'variant_1', quantity: 1 },
|
|
254
|
+
[lineItem({ variant_id: 'variant_1' })],
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
expect(row).toMatchObject({
|
|
258
|
+
key: 'rli_1',
|
|
259
|
+
name: 'Shirt',
|
|
260
|
+
optionsText: 'Blue / M',
|
|
261
|
+
thumbnailUrl: 'https://example.test/shirt.jpg',
|
|
262
|
+
displayPrice: '$10.00',
|
|
263
|
+
productId: 'prod_1',
|
|
264
|
+
quantity: 1,
|
|
265
|
+
})
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
it('falls back to the variant it names when the order line is gone', () => {
|
|
269
|
+
const row = postSaleItemRow(
|
|
270
|
+
'rli_1',
|
|
271
|
+
{
|
|
272
|
+
line_item_id: 'li_gone',
|
|
273
|
+
variant_id: 'variant_1',
|
|
274
|
+
variant: {
|
|
275
|
+
product_name: 'Mug',
|
|
276
|
+
options_text: 'Red',
|
|
277
|
+
thumbnail_url: 'https://example.test/mug.jpg',
|
|
278
|
+
product_id: 'prod_2',
|
|
279
|
+
},
|
|
280
|
+
quantity: 2,
|
|
281
|
+
},
|
|
282
|
+
[lineItem()],
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
expect(row).toMatchObject({
|
|
286
|
+
name: 'Mug',
|
|
287
|
+
optionsText: 'Red',
|
|
288
|
+
thumbnailUrl: 'https://example.test/mug.jpg',
|
|
289
|
+
displayPrice: null,
|
|
290
|
+
productId: 'prod_2',
|
|
291
|
+
quantity: 2,
|
|
292
|
+
})
|
|
293
|
+
})
|
|
294
|
+
})
|
|
@@ -20,6 +20,7 @@ export interface GroupableLineItem {
|
|
|
20
20
|
thumbnail_url?: string | null
|
|
21
21
|
price?: string | null
|
|
22
22
|
currency?: string | null
|
|
23
|
+
product_id?: string | null
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
/** The fulfillment-item fields the grouping reads. */
|
|
@@ -53,6 +54,8 @@ export interface FulfillmentItemRow {
|
|
|
53
54
|
optionsText: string | null
|
|
54
55
|
thumbnailUrl: string | null
|
|
55
56
|
displayPrice: string | null
|
|
57
|
+
/** The product to link the row to, when the line still names one. */
|
|
58
|
+
productId: string | null
|
|
56
59
|
quantity: number
|
|
57
60
|
}
|
|
58
61
|
|
|
@@ -144,6 +147,7 @@ function buildRow(
|
|
|
144
147
|
name: copy?.name ?? fallback?.name ?? '',
|
|
145
148
|
optionsText: copy?.options_text ?? fallback?.options_text ?? null,
|
|
146
149
|
thumbnailUrl: copy?.thumbnail_url ?? null,
|
|
150
|
+
productId: copy?.product_id ?? null,
|
|
147
151
|
displayPrice:
|
|
148
152
|
copy?.price && copy.currency
|
|
149
153
|
? formatMoney(copy.price, copy.currency, i18n.language || 'en', { unitPrice: true })
|
|
@@ -186,6 +190,55 @@ export function fulfillmentItemRows(
|
|
|
186
190
|
return [...rows.values()]
|
|
187
191
|
}
|
|
188
192
|
|
|
193
|
+
/** A return, claim or exchange line, as the item row reads it. */
|
|
194
|
+
export interface PostSaleLine {
|
|
195
|
+
line_item_id?: string | null
|
|
196
|
+
variant_id?: string | null
|
|
197
|
+
/** The line's own copy of the product name, where the panel's API sends one. */
|
|
198
|
+
name?: string | null
|
|
199
|
+
variant?: {
|
|
200
|
+
product_name?: string | null
|
|
201
|
+
options_text?: string | null
|
|
202
|
+
thumbnail_url?: string | null
|
|
203
|
+
sku?: string | null
|
|
204
|
+
product_id?: string | null
|
|
205
|
+
} | null
|
|
206
|
+
quantity: number
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* A return, claim or exchange line as the same row a fulfillment shows: the
|
|
211
|
+
* order line it came from gives the name, image and price; the variant it
|
|
212
|
+
* names fills in when the order line is gone.
|
|
213
|
+
*/
|
|
214
|
+
export function postSaleItemRow(
|
|
215
|
+
key: string,
|
|
216
|
+
line: PostSaleLine,
|
|
217
|
+
lineItems: GroupableLineItem[],
|
|
218
|
+
): FulfillmentItemRow {
|
|
219
|
+
const lineItem = line.line_item_id
|
|
220
|
+
? lineItems.find((item) => item.id === line.line_item_id)
|
|
221
|
+
: undefined
|
|
222
|
+
const variant = line.variant
|
|
223
|
+
const row = buildRow(
|
|
224
|
+
key,
|
|
225
|
+
lineItem,
|
|
226
|
+
{
|
|
227
|
+
variant_id: line.variant_id,
|
|
228
|
+
name: line.name ?? variant?.product_name ?? null,
|
|
229
|
+
options_text: variant?.options_text ?? null,
|
|
230
|
+
},
|
|
231
|
+
line.quantity,
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
return {
|
|
235
|
+
...row,
|
|
236
|
+
name: row.name || variant?.sku || line.variant_id || '',
|
|
237
|
+
thumbnailUrl: row.thumbnailUrl ?? variant?.thumbnail_url ?? null,
|
|
238
|
+
productId: row.productId ?? variant?.product_id ?? null,
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
189
242
|
/**
|
|
190
243
|
* Line-item units no fulfillment has claimed: the ordered quantity minus what
|
|
191
244
|
* every fulfillment holds. Only positive remainders are returned, so an order
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { buildCurlCommand } from './curl'
|
|
2
|
+
import { type DeveloperInspectorEntry, SENSITIVE_HEADERS } from './developer-inspector'
|
|
3
|
+
|
|
4
|
+
const ADMIN_API_PREFIX = '/api/v3/admin'
|
|
5
|
+
// Credentials are replaced by the placeholder; a replayed idempotency key
|
|
6
|
+
// would return the cached response instead of running the request again.
|
|
7
|
+
const OMITTED_HEADERS = new Set([...SENSITIVE_HEADERS, 'idempotency-key'])
|
|
8
|
+
|
|
9
|
+
function isSendableBody(body: unknown): boolean {
|
|
10
|
+
return body !== undefined && body !== null && typeof body !== 'string'
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** A curl command replaying the request, with a placeholder for the token. */
|
|
14
|
+
export function entryToCurl(entry: DeveloperInspectorEntry): string {
|
|
15
|
+
const headers = Object.fromEntries(
|
|
16
|
+
Object.entries(entry.requestHeaders).filter(
|
|
17
|
+
([name]) => !OMITTED_HEADERS.has(name.toLowerCase()),
|
|
18
|
+
),
|
|
19
|
+
)
|
|
20
|
+
return buildCurlCommand(entry.url, headers, {
|
|
21
|
+
method: entry.method,
|
|
22
|
+
body: isSendableBody(entry.requestBody) ? entry.requestBody : undefined,
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The same request through `@spree/admin-sdk`. Uses the low-level
|
|
28
|
+
* `adminClient.request`, which always matches the wire call; the resource
|
|
29
|
+
* method names would be a guess.
|
|
30
|
+
*/
|
|
31
|
+
export function entryToSdkCall(entry: DeveloperInspectorEntry): string {
|
|
32
|
+
const path = entry.path.startsWith(ADMIN_API_PREFIX)
|
|
33
|
+
? entry.path.slice(ADMIN_API_PREFIX.length)
|
|
34
|
+
: entry.path
|
|
35
|
+
const options: Record<string, unknown> = {}
|
|
36
|
+
if (entry.query && Object.keys(entry.query).length > 0) options.params = entry.query
|
|
37
|
+
if (isSendableBody(entry.requestBody)) options.body = entry.requestBody
|
|
38
|
+
const args = [JSON.stringify(entry.method), JSON.stringify(path)]
|
|
39
|
+
if (Object.keys(options).length > 0) args.push(JSON.stringify(options, null, 2))
|
|
40
|
+
return `await adminClient.request(${args.join(', ')})`
|
|
41
|
+
}
|
|
@@ -178,6 +178,10 @@ export interface TableDef<T = any> {
|
|
|
178
178
|
columns: ColumnDef<T>[]
|
|
179
179
|
searchParam?: string
|
|
180
180
|
searchPlaceholder?: string
|
|
181
|
+
/**
|
|
182
|
+
* Whether the toolbar offers a free-text search. On by default.
|
|
183
|
+
*/
|
|
184
|
+
searchable?: boolean
|
|
181
185
|
defaultSort?: SortOption
|
|
182
186
|
emptyIcon?: ReactNode
|
|
183
187
|
emptyMessage?: string
|
|
@@ -262,18 +266,21 @@ export const tables = new Proxy({} as Record<string, TableMutator>, {
|
|
|
262
266
|
})
|
|
263
267
|
|
|
264
268
|
interface TableMutator {
|
|
265
|
-
|
|
269
|
+
/** `after` places the column right after that one, instead of last. */
|
|
270
|
+
addColumn<T = any>(column: ColumnDef<T>, options?: { after?: string }): void
|
|
266
271
|
removeColumn(key: string): void
|
|
267
272
|
updateColumn<T = any>(key: string, updates: Partial<ColumnDef<T>>): void
|
|
268
273
|
}
|
|
269
274
|
|
|
270
|
-
function applyAddColumn(table: TableDef, column: ColumnDef) {
|
|
275
|
+
function applyAddColumn(table: TableDef, column: ColumnDef, after?: string) {
|
|
271
276
|
if (table.columns.some((c) => c.key === column.key)) {
|
|
272
277
|
throw new Error(
|
|
273
278
|
`Column "${column.key}" already exists in table "${table.key}". Use updateColumn() instead.`,
|
|
274
279
|
)
|
|
275
280
|
}
|
|
276
|
-
table.columns.
|
|
281
|
+
const index = after ? table.columns.findIndex((c) => c.key === after) : -1
|
|
282
|
+
if (index === -1) table.columns.push(column)
|
|
283
|
+
else table.columns.splice(index + 1, 0, column)
|
|
277
284
|
}
|
|
278
285
|
|
|
279
286
|
function applyRemoveColumn(table: TableDef, key: string) {
|
|
@@ -288,10 +295,10 @@ function applyUpdateColumn(table: TableDef, key: string, updates: Partial<Column
|
|
|
288
295
|
|
|
289
296
|
function createMutator(tableKey: string): TableMutator {
|
|
290
297
|
return {
|
|
291
|
-
addColumn(column) {
|
|
298
|
+
addColumn(column, options) {
|
|
292
299
|
const table = registry.get(tableKey)
|
|
293
|
-
if (table) applyAddColumn(table, column as ColumnDef)
|
|
294
|
-
else enqueue(tableKey, (t) => applyAddColumn(t, column as ColumnDef))
|
|
300
|
+
if (table) applyAddColumn(table, column as ColumnDef, options?.after)
|
|
301
|
+
else enqueue(tableKey, (t) => applyAddColumn(t, column as ColumnDef, options?.after))
|
|
295
302
|
},
|
|
296
303
|
removeColumn(key) {
|
|
297
304
|
const table = registry.get(tableKey)
|
|
@@ -6,7 +6,7 @@ const TIMESTAMP_KEYS = ['created_at', 'updated_at'] as const
|
|
|
6
6
|
|
|
7
7
|
type TimestampKey = (typeof TIMESTAMP_KEYS)[number]
|
|
8
8
|
|
|
9
|
-
const TIMESTAMP_CLASS = 'text-
|
|
9
|
+
const TIMESTAMP_CLASS = 'text-base text-muted-foreground whitespace-nowrap'
|
|
10
10
|
|
|
11
11
|
function createTimestampColumn(key: TimestampKey, visibleByDefault = false): ColumnDef {
|
|
12
12
|
return {
|
package/src/locales/ar.json
CHANGED
|
@@ -37,7 +37,6 @@
|
|
|
37
37
|
},
|
|
38
38
|
"account": {
|
|
39
39
|
"community": "المجتمع",
|
|
40
|
-
"contact_support": "الاتصال بالدعم",
|
|
41
40
|
"documentation": "التوثيق",
|
|
42
41
|
"edit_profile": "تعديل الملف الشخصي",
|
|
43
42
|
"language": {
|
|
@@ -45,8 +44,8 @@
|
|
|
45
44
|
"update_failed": "تعذّر حفظ تفضيل اللغة. يرجى المحاولة مجددًا."
|
|
46
45
|
},
|
|
47
46
|
"log_out": "تسجيل الخروج",
|
|
48
|
-
"
|
|
49
|
-
"
|
|
47
|
+
"report_issue": "الإبلاغ عن مشكلة",
|
|
48
|
+
"send_feedback": "إرسال ملاحظات"
|
|
50
49
|
},
|
|
51
50
|
"actions": {
|
|
52
51
|
"actions_menu": "الإجراءات",
|
|
@@ -102,7 +101,11 @@
|
|
|
102
101
|
"default_shipping": "الشحن الافتراضي",
|
|
103
102
|
"inherited_from": "موروث من {{name}}",
|
|
104
103
|
"set_default_billing": "تعيين كعنوان فوترة افتراضي",
|
|
105
|
-
"set_default_shipping": "تعيين كعنوان شحن افتراضي"
|
|
104
|
+
"set_default_shipping": "تعيين كعنوان شحن افتراضي",
|
|
105
|
+
"columns": {
|
|
106
|
+
"address": "العنوان",
|
|
107
|
+
"defaults": "افتراضي لـ"
|
|
108
|
+
}
|
|
106
109
|
},
|
|
107
110
|
"auth": {
|
|
108
111
|
"back_to_login": "العودة إلى تسجيل الدخول",
|
|
@@ -190,7 +193,8 @@
|
|
|
190
193
|
"unsaved_changes": "لديك تغييرات غير محفوظة.",
|
|
191
194
|
"yes": "نعم",
|
|
192
195
|
"done": "تم",
|
|
193
|
-
"error": "حدث خطأ ما."
|
|
196
|
+
"error": "حدث خطأ ما.",
|
|
197
|
+
"unsaved_label": "تغييرات غير محفوظة"
|
|
194
198
|
},
|
|
195
199
|
"components": {
|
|
196
200
|
"address_block": {
|
|
@@ -321,7 +325,8 @@
|
|
|
321
325
|
"drop_label": "أسقط ملفًا هنا، أو انقر للاستعراض",
|
|
322
326
|
"invalid_type": "نوع الملف هذا غير مقبول (المتوقع: {{accept}})",
|
|
323
327
|
"upload_failed": "فشل الرفع: {{message}}",
|
|
324
|
-
"uploading": "جارٍ الرفع…"
|
|
328
|
+
"uploading": "جارٍ الرفع…",
|
|
329
|
+
"replace": "استبدال الملف"
|
|
325
330
|
},
|
|
326
331
|
"import_button": {
|
|
327
332
|
"delimiter_label": "فاصل الأعمدة",
|
|
@@ -342,7 +347,8 @@
|
|
|
342
347
|
"json_preview_drawer": {
|
|
343
348
|
"collapse_depth": "عمق الطي",
|
|
344
349
|
"copied": "تم النسخ",
|
|
345
|
-
"
|
|
350
|
+
"copy_curl": "نسخ كـ curl",
|
|
351
|
+
"copy_json": "نسخ JSON",
|
|
346
352
|
"depth": "العمق {{level}}",
|
|
347
353
|
"failed_to_load": "فشل التحميل: {{message}}",
|
|
348
354
|
"open_raw": "فتح الاستجابة الخام",
|
|
@@ -437,11 +443,8 @@
|
|
|
437
443
|
"this_month": "هذا الشهر",
|
|
438
444
|
"today": "اليوم"
|
|
439
445
|
},
|
|
440
|
-
"date_range_end": "إلى",
|
|
441
|
-
"date_range_start": "من",
|
|
442
446
|
"descending": "تنازلي",
|
|
443
447
|
"filter_by": "تصفية حسب…",
|
|
444
|
-
"filter_date_placeholder": "اختر تاريخًا",
|
|
445
448
|
"filter_select_placeholder": "اختر…",
|
|
446
449
|
"filter_text_placeholder": "تصفية…",
|
|
447
450
|
"filter_to_placeholder": "التصفية إلى…",
|
|
@@ -475,7 +478,10 @@
|
|
|
475
478
|
"select_all": "تحديد الكل",
|
|
476
479
|
"sort_by": "ترتيب حسب",
|
|
477
480
|
"sort_tooltip": "ترتيب",
|
|
478
|
-
"visible_columns": "الأعمدة المرئية"
|
|
481
|
+
"visible_columns": "الأعمدة المرئية",
|
|
482
|
+
"row_density": "كثافة الصفوف",
|
|
483
|
+
"density_comfortable": "مريحة",
|
|
484
|
+
"density_compact": "مضغوطة"
|
|
479
485
|
},
|
|
480
486
|
"tag_combobox": {
|
|
481
487
|
"create_option": "إنشاء «{{tag}}»",
|
|
@@ -488,11 +494,58 @@
|
|
|
488
494
|
"light": "فاتح",
|
|
489
495
|
"system": "النظام"
|
|
490
496
|
},
|
|
497
|
+
"timezone_combobox": {
|
|
498
|
+
"empty": "لم يتم العثور على مناطق زمنية",
|
|
499
|
+
"placeholder": "اختر منطقة زمنية",
|
|
500
|
+
"search_placeholder": "ابحث عن المناطق الزمنية…"
|
|
501
|
+
},
|
|
491
502
|
"wizard": {
|
|
492
503
|
"discard_cancel": "متابعة التحرير",
|
|
493
504
|
"discard_confirm": "تجاهل",
|
|
494
505
|
"discard_message": "سيُفقد كل ما أدخلته حتى الآن، ولا يمكن التراجع عن ذلك.",
|
|
495
506
|
"discard_title": "تجاهل هذه المسودة؟"
|
|
507
|
+
},
|
|
508
|
+
"product_stock": {
|
|
509
|
+
"out_of_stock": "إنتهى من المخزن",
|
|
510
|
+
"on_backorder": "على المؤجل",
|
|
511
|
+
"in_stock": "{{count}} في المخزون",
|
|
512
|
+
"in_stock_short": "في المخزون",
|
|
513
|
+
"variants_one": "{{count}} البديل",
|
|
514
|
+
"variants_other": "{{count}} متغيرات"
|
|
515
|
+
},
|
|
516
|
+
"developer_inspector": {
|
|
517
|
+
"title": "أدوات المطوّر",
|
|
518
|
+
"description": "طلبات Admin API من علامة التبويب هذه، محفوظة في الذاكرة فقط. الرموز وكلمات المرور مخفية.",
|
|
519
|
+
"system_info": "معلومات النظام",
|
|
520
|
+
"clear": "مسح",
|
|
521
|
+
"tabs": {
|
|
522
|
+
"requests": "الطلبات",
|
|
523
|
+
"errors": "الأخطاء"
|
|
524
|
+
},
|
|
525
|
+
"empty_requests": "لا توجد طلبات بعد. استخدم لوحة التحكم وستظهر هنا.",
|
|
526
|
+
"empty_errors": "لا توجد طلبات فاشلة.",
|
|
527
|
+
"select_request": "اختر طلبًا لعرض تفاصيله.",
|
|
528
|
+
"network_error": "خطأ في الشبكة",
|
|
529
|
+
"status": "الحالة",
|
|
530
|
+
"duration": "المدة",
|
|
531
|
+
"duration_ms": "{{value}} مللي ثانية",
|
|
532
|
+
"request_id": "معرّف الطلب",
|
|
533
|
+
"error_code": "رمز الخطأ",
|
|
534
|
+
"query": "معلمات الاستعلام",
|
|
535
|
+
"request_body": "نص الطلب",
|
|
536
|
+
"response_body": "نص الاستجابة",
|
|
537
|
+
"no_body": "لا يوجد نص",
|
|
538
|
+
"copy_curl": "نسخ كأمر curl",
|
|
539
|
+
"copy_sdk": "نسخ كاستدعاء SDK",
|
|
540
|
+
"copy_response": "نسخ الاستجابة",
|
|
541
|
+
"copy_request_id": "نسخ معرّف الطلب",
|
|
542
|
+
"copied": "تم النسخ",
|
|
543
|
+
"menu_open": "أدوات المطوّر"
|
|
544
|
+
},
|
|
545
|
+
"server_error": {
|
|
546
|
+
"title": "حدث خطأ في الخادم",
|
|
547
|
+
"request_id": "الطلب {{id}}",
|
|
548
|
+
"report": "إبلاغ"
|
|
496
549
|
}
|
|
497
550
|
},
|
|
498
551
|
"currency_amounts": {
|
|
@@ -963,6 +1016,24 @@
|
|
|
963
1016
|
"digital_asset_authorized_days": {
|
|
964
1017
|
"label": "الأيام المتاحة"
|
|
965
1018
|
}
|
|
1019
|
+
},
|
|
1020
|
+
"seller": {
|
|
1021
|
+
"about": {
|
|
1022
|
+
"label": "نبذة",
|
|
1023
|
+
"help": "تظهر في ملف البائع بالمتجر."
|
|
1024
|
+
},
|
|
1025
|
+
"holiday_mode_until": {
|
|
1026
|
+
"label": "غائب حتى",
|
|
1027
|
+
"placeholder": "اختر تاريخاً",
|
|
1028
|
+
"help": "يبقى الكتالوج ظاهراً لكن لا يمكن الشراء حتى ذلك الحين."
|
|
1029
|
+
},
|
|
1030
|
+
"contact_email": {
|
|
1031
|
+
"help": "تصل إليه الدعوات وإشعارات السوق."
|
|
1032
|
+
},
|
|
1033
|
+
"billing_email": {
|
|
1034
|
+
"label": "بريد الفوترة",
|
|
1035
|
+
"help": "تصل إليه فواتير العمولة وإشعارات الدفع إن اختلف."
|
|
1036
|
+
}
|
|
966
1037
|
}
|
|
967
1038
|
},
|
|
968
1039
|
"image_upload": {
|
|
@@ -1067,15 +1138,35 @@
|
|
|
1067
1138
|
"signed_in_as_invitee": ". أنت مسجّل الدخول باسم {{email}}."
|
|
1068
1139
|
},
|
|
1069
1140
|
"media_picker": {
|
|
1141
|
+
"any": "الكل",
|
|
1070
1142
|
"confirm": "إضافة المحدد",
|
|
1071
1143
|
"description": "اختر ملفات موجودة في مكتبتك، أو ارفع ملفات جديدة.",
|
|
1144
|
+
"drop_hint_images": "أو اسحب الصور وأفلتها هنا",
|
|
1145
|
+
"drop_hint_media": "أو اسحب الصور والفيديوهات وأفلتها هنا",
|
|
1072
1146
|
"empty": "مكتبتك فارغة. ارفع ملفًا للبدء.",
|
|
1147
|
+
"filter_type": "النوع",
|
|
1148
|
+
"filter_usage": "الاستخدام",
|
|
1073
1149
|
"no_results": "لا توجد ملفات تطابق بحثك.",
|
|
1074
1150
|
"search_placeholder": "البحث بالاسم…",
|
|
1075
1151
|
"selected_count_one": "تم تحديد {{count}}",
|
|
1076
1152
|
"selected_count_other": "تم تحديد {{count}}",
|
|
1153
|
+
"sort_date_added": "تاريخ الإضافة",
|
|
1154
|
+
"sort_file_name": "اسم الملف",
|
|
1155
|
+
"sort_file_size": "حجم الملف",
|
|
1077
1156
|
"title": "مكتبة الوسائط",
|
|
1078
|
-
"
|
|
1157
|
+
"type_image": "الصور",
|
|
1158
|
+
"type_video": "الفيديوهات",
|
|
1159
|
+
"upload": "رفع",
|
|
1160
|
+
"usage_unused": "غير مستخدم",
|
|
1161
|
+
"usage_used": "مستخدم",
|
|
1162
|
+
"view": "العرض",
|
|
1163
|
+
"view_grid": "عرض شبكي",
|
|
1164
|
+
"view_list": "عرض قائمة"
|
|
1165
|
+
},
|
|
1166
|
+
"media_editor": {
|
|
1167
|
+
"information": "المعلومات",
|
|
1168
|
+
"details": "التفاصيل",
|
|
1169
|
+
"added": "أُضيف في {{date}}"
|
|
1079
1170
|
},
|
|
1080
1171
|
"messages": {
|
|
1081
1172
|
"address_removed": "تمت إزالة العنوان",
|
|
@@ -1568,12 +1659,18 @@
|
|
|
1568
1659
|
"add_video_title": "إضافة رابط فيديو",
|
|
1569
1660
|
"assigned_variants_help": "اختر المتغيرات التي تمثلها هذه الصورة. اتركه فارغًا ليتم تطبيقه على كافة المتغيرات.",
|
|
1570
1661
|
"assigned_variants_label": "المتغيرات المخصصة",
|
|
1571
|
-
"delete_confirm": "
|
|
1662
|
+
"delete_confirm": "إزالة هذه الوسائط؟ ستُحذف عند حفظ المنتج.",
|
|
1663
|
+
"delete_selected_confirm_one": "إزالة الوسائط المحددة؟ ستُحذف عند حفظ المنتج.",
|
|
1664
|
+
"delete_selected_confirm_other": "إزالة الوسائط المحددة ({{count}})؟ ستُحذف عند حفظ المنتج.",
|
|
1572
1665
|
"drop_hint": "قم بسحب وإفلات الوسائط هنا، أو انقر للتصفح",
|
|
1573
1666
|
"edit_title": "تحرير الوسائط",
|
|
1574
1667
|
"file_types_hint": "صور (PNG، JPG، WebP) وفيديو (MP4، WebM، MOV)",
|
|
1575
1668
|
"focal_point_help": "انقر على الصورة لتحديد الجزء الذي يبقى ظاهراً عند الاقتصاص.",
|
|
1576
1669
|
"focal_point_reset": "إعادة تعيين",
|
|
1670
|
+
"select_all": "تحديد كل الوسائط",
|
|
1671
|
+
"select_item": "تحديد {{name}}",
|
|
1672
|
+
"selected_count_one": "تم تحديد {{count}}",
|
|
1673
|
+
"selected_count_other": "تم تحديد {{count}}",
|
|
1577
1674
|
"unsupported_file": "لم تتم إضافة {{count}} ملف — الصور والفيديو فقط مدعومة.",
|
|
1578
1675
|
"upload_failed": "فشل تحميل {{name}}: {{message}}",
|
|
1579
1676
|
"upload_status_failed": "فشل",
|
|
@@ -1683,6 +1780,10 @@
|
|
|
1683
1780
|
"failed_to_create_option_type": "فشل إنشاء نوع الخيار",
|
|
1684
1781
|
"failed_to_create_value": "فشل إنشاء القيمة"
|
|
1685
1782
|
},
|
|
1783
|
+
"images_aria": "صور {{name}}",
|
|
1784
|
+
"images_description": "حدد صور المنتج التي تعرض هذا المتغير. تظهر بترتيب بطاقة الوسائط، وتصبح أول صورة محددة صورته المصغرة — اسحب الصور في بطاقة الوسائط لتغييرها.",
|
|
1785
|
+
"images_title": "صور {{name}}",
|
|
1786
|
+
"images_unsaved": "احفظ المنتج لإضافة صور إلى هذا المتغير",
|
|
1686
1787
|
"keep": "احتفظ بالكل",
|
|
1687
1788
|
"option_type_label": "نوع الخيار",
|
|
1688
1789
|
"option_type_placeholder": "اختر نوع الخيار",
|
|
@@ -1704,6 +1805,7 @@
|
|
|
1704
1805
|
"carton_placeholder": "بدون كرتونة",
|
|
1705
1806
|
"pending_save": "تُحفظ التغييرات هنا مع المنتج."
|
|
1706
1807
|
},
|
|
1808
|
+
"thumbnail_badge": "الصورة المصغرة",
|
|
1707
1809
|
"title": "المتغيرات",
|
|
1708
1810
|
"variant_label": "البديل"
|
|
1709
1811
|
}
|
|
@@ -1792,7 +1894,8 @@
|
|
|
1792
1894
|
"value_count_other": "{{count}} قيم"
|
|
1793
1895
|
},
|
|
1794
1896
|
"media_library": {
|
|
1795
|
-
"download": "تنزيل"
|
|
1897
|
+
"download": "تنزيل",
|
|
1898
|
+
"choose_from_library": "اختر من المكتبة"
|
|
1796
1899
|
},
|
|
1797
1900
|
"catalogs": {
|
|
1798
1901
|
"terms": {
|
|
@@ -2120,6 +2223,17 @@
|
|
|
2120
2223
|
"replace": "استبدال الملف",
|
|
2121
2224
|
"replace_description": "سيُحذف \"{{name}}\" وسيحمّل كل من اشتراه الملف الجديد من روابطه الحالية. يسري فورًا.",
|
|
2122
2225
|
"replace_title": "استبدال هذا الملف؟"
|
|
2226
|
+
},
|
|
2227
|
+
"sellers": {
|
|
2228
|
+
"images": {
|
|
2229
|
+
"cover_photo_label": "صورة الغلاف",
|
|
2230
|
+
"cover_photo_help": "لافتة عريضة أعلى ملف البائع.",
|
|
2231
|
+
"logo_label": "الشعار",
|
|
2232
|
+
"logo_help": "يُفضَّل الشكل المربّع. يظهر بجوار البائع في كل مكان يرد فيه.",
|
|
2233
|
+
"square_logo_label": "شعار مربّع",
|
|
2234
|
+
"square_logo_help": "للمواضع التي تتطلب مربّعاً دقيقاً، مثل الصورة الرمزية."
|
|
2235
|
+
},
|
|
2236
|
+
"not_provided": "غير محدد"
|
|
2123
2237
|
}
|
|
2124
2238
|
}
|
|
2125
2239
|
}
|