@spree/dashboard-core 6.0.3 → 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 +19 -19
- 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 +1 -1
- 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 +189 -323
- 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 +19 -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 +120 -14
- package/src/locales/de.json +120 -14
- package/src/locales/en.json +120 -14
- package/src/locales/es.json +120 -14
- package/src/locales/fr.json +120 -14
- package/src/locales/ja.json +120 -14
- package/src/locales/pl.json +120 -14
- package/src/locales/vi.json +120 -14
- package/src/locales/zh-CN.json +120 -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 +201 -70
- package/src/products/product-form-mapping.ts +1 -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,6 +494,11 @@
|
|
|
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": "تجاهل",
|
|
@@ -501,6 +512,40 @@
|
|
|
501
512
|
"in_stock_short": "في المخزون",
|
|
502
513
|
"variants_one": "{{count}} البديل",
|
|
503
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": "إبلاغ"
|
|
504
549
|
}
|
|
505
550
|
},
|
|
506
551
|
"currency_amounts": {
|
|
@@ -971,6 +1016,24 @@
|
|
|
971
1016
|
"digital_asset_authorized_days": {
|
|
972
1017
|
"label": "الأيام المتاحة"
|
|
973
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
|
+
}
|
|
974
1037
|
}
|
|
975
1038
|
},
|
|
976
1039
|
"image_upload": {
|
|
@@ -1075,15 +1138,35 @@
|
|
|
1075
1138
|
"signed_in_as_invitee": ". أنت مسجّل الدخول باسم {{email}}."
|
|
1076
1139
|
},
|
|
1077
1140
|
"media_picker": {
|
|
1141
|
+
"any": "الكل",
|
|
1078
1142
|
"confirm": "إضافة المحدد",
|
|
1079
1143
|
"description": "اختر ملفات موجودة في مكتبتك، أو ارفع ملفات جديدة.",
|
|
1144
|
+
"drop_hint_images": "أو اسحب الصور وأفلتها هنا",
|
|
1145
|
+
"drop_hint_media": "أو اسحب الصور والفيديوهات وأفلتها هنا",
|
|
1080
1146
|
"empty": "مكتبتك فارغة. ارفع ملفًا للبدء.",
|
|
1147
|
+
"filter_type": "النوع",
|
|
1148
|
+
"filter_usage": "الاستخدام",
|
|
1081
1149
|
"no_results": "لا توجد ملفات تطابق بحثك.",
|
|
1082
1150
|
"search_placeholder": "البحث بالاسم…",
|
|
1083
1151
|
"selected_count_one": "تم تحديد {{count}}",
|
|
1084
1152
|
"selected_count_other": "تم تحديد {{count}}",
|
|
1153
|
+
"sort_date_added": "تاريخ الإضافة",
|
|
1154
|
+
"sort_file_name": "اسم الملف",
|
|
1155
|
+
"sort_file_size": "حجم الملف",
|
|
1085
1156
|
"title": "مكتبة الوسائط",
|
|
1086
|
-
"
|
|
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}}"
|
|
1087
1170
|
},
|
|
1088
1171
|
"messages": {
|
|
1089
1172
|
"address_removed": "تمت إزالة العنوان",
|
|
@@ -1576,12 +1659,18 @@
|
|
|
1576
1659
|
"add_video_title": "إضافة رابط فيديو",
|
|
1577
1660
|
"assigned_variants_help": "اختر المتغيرات التي تمثلها هذه الصورة. اتركه فارغًا ليتم تطبيقه على كافة المتغيرات.",
|
|
1578
1661
|
"assigned_variants_label": "المتغيرات المخصصة",
|
|
1579
|
-
"delete_confirm": "
|
|
1662
|
+
"delete_confirm": "إزالة هذه الوسائط؟ ستُحذف عند حفظ المنتج.",
|
|
1663
|
+
"delete_selected_confirm_one": "إزالة الوسائط المحددة؟ ستُحذف عند حفظ المنتج.",
|
|
1664
|
+
"delete_selected_confirm_other": "إزالة الوسائط المحددة ({{count}})؟ ستُحذف عند حفظ المنتج.",
|
|
1580
1665
|
"drop_hint": "قم بسحب وإفلات الوسائط هنا، أو انقر للتصفح",
|
|
1581
1666
|
"edit_title": "تحرير الوسائط",
|
|
1582
1667
|
"file_types_hint": "صور (PNG، JPG، WebP) وفيديو (MP4، WebM، MOV)",
|
|
1583
1668
|
"focal_point_help": "انقر على الصورة لتحديد الجزء الذي يبقى ظاهراً عند الاقتصاص.",
|
|
1584
1669
|
"focal_point_reset": "إعادة تعيين",
|
|
1670
|
+
"select_all": "تحديد كل الوسائط",
|
|
1671
|
+
"select_item": "تحديد {{name}}",
|
|
1672
|
+
"selected_count_one": "تم تحديد {{count}}",
|
|
1673
|
+
"selected_count_other": "تم تحديد {{count}}",
|
|
1585
1674
|
"unsupported_file": "لم تتم إضافة {{count}} ملف — الصور والفيديو فقط مدعومة.",
|
|
1586
1675
|
"upload_failed": "فشل تحميل {{name}}: {{message}}",
|
|
1587
1676
|
"upload_status_failed": "فشل",
|
|
@@ -1691,6 +1780,10 @@
|
|
|
1691
1780
|
"failed_to_create_option_type": "فشل إنشاء نوع الخيار",
|
|
1692
1781
|
"failed_to_create_value": "فشل إنشاء القيمة"
|
|
1693
1782
|
},
|
|
1783
|
+
"images_aria": "صور {{name}}",
|
|
1784
|
+
"images_description": "حدد صور المنتج التي تعرض هذا المتغير. تظهر بترتيب بطاقة الوسائط، وتصبح أول صورة محددة صورته المصغرة — اسحب الصور في بطاقة الوسائط لتغييرها.",
|
|
1785
|
+
"images_title": "صور {{name}}",
|
|
1786
|
+
"images_unsaved": "احفظ المنتج لإضافة صور إلى هذا المتغير",
|
|
1694
1787
|
"keep": "احتفظ بالكل",
|
|
1695
1788
|
"option_type_label": "نوع الخيار",
|
|
1696
1789
|
"option_type_placeholder": "اختر نوع الخيار",
|
|
@@ -1712,6 +1805,7 @@
|
|
|
1712
1805
|
"carton_placeholder": "بدون كرتونة",
|
|
1713
1806
|
"pending_save": "تُحفظ التغييرات هنا مع المنتج."
|
|
1714
1807
|
},
|
|
1808
|
+
"thumbnail_badge": "الصورة المصغرة",
|
|
1715
1809
|
"title": "المتغيرات",
|
|
1716
1810
|
"variant_label": "البديل"
|
|
1717
1811
|
}
|
|
@@ -1800,7 +1894,8 @@
|
|
|
1800
1894
|
"value_count_other": "{{count}} قيم"
|
|
1801
1895
|
},
|
|
1802
1896
|
"media_library": {
|
|
1803
|
-
"download": "تنزيل"
|
|
1897
|
+
"download": "تنزيل",
|
|
1898
|
+
"choose_from_library": "اختر من المكتبة"
|
|
1804
1899
|
},
|
|
1805
1900
|
"catalogs": {
|
|
1806
1901
|
"terms": {
|
|
@@ -2128,6 +2223,17 @@
|
|
|
2128
2223
|
"replace": "استبدال الملف",
|
|
2129
2224
|
"replace_description": "سيُحذف \"{{name}}\" وسيحمّل كل من اشتراه الملف الجديد من روابطه الحالية. يسري فورًا.",
|
|
2130
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": "غير محدد"
|
|
2131
2237
|
}
|
|
2132
2238
|
}
|
|
2133
2239
|
}
|