gt-sanity 0.0.5 → 1.0.0

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.
Files changed (108) hide show
  1. package/LICENSE.md +1 -8
  2. package/README.md +5 -5
  3. package/dist/index.d.mts +122 -95
  4. package/dist/index.d.ts +122 -95
  5. package/dist/index.js +9089 -1119
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +9099 -1100
  8. package/dist/index.mjs.map +1 -1
  9. package/package.json +11 -4
  10. package/src/adapter/core.ts +111 -9
  11. package/src/adapter/createTask.ts +1 -1
  12. package/src/adapter/getLocales.ts +2 -2
  13. package/src/adapter/types.ts +9 -0
  14. package/src/components/TranslationsProvider.tsx +942 -0
  15. package/src/components/page/BatchProgress.tsx +27 -0
  16. package/src/components/page/ImportAllDialog.tsx +51 -0
  17. package/src/components/page/ImportMissingDialog.tsx +55 -0
  18. package/src/components/page/TranslateAllDialog.tsx +55 -0
  19. package/src/components/page/TranslationsTable.tsx +81 -0
  20. package/src/components/page/TranslationsTool.tsx +338 -0
  21. package/src/components/shared/BaseTranslationWrapper.tsx +82 -0
  22. package/src/components/{LanguageStatus.tsx → shared/LanguageStatus.tsx} +2 -0
  23. package/src/components/shared/LocaleCheckbox.tsx +47 -0
  24. package/src/components/{ProgressBar.tsx → shared/ProgressBar.tsx} +2 -0
  25. package/src/components/shared/SingleDocumentView.tsx +108 -0
  26. package/src/components/tab/TranslationView.tsx +379 -0
  27. package/src/components/tab/TranslationsTab.tsx +25 -0
  28. package/src/configuration/baseDocumentLevelConfig/documentLevelPatch.ts +21 -11
  29. package/src/configuration/baseDocumentLevelConfig/helpers/createI18nDocAndPatchMetadata.ts +57 -23
  30. package/src/configuration/baseDocumentLevelConfig/helpers/createTranslationMetadata.ts +2 -0
  31. package/src/configuration/baseDocumentLevelConfig/helpers/getOrCreateTranslationMetadata.ts +2 -0
  32. package/src/configuration/baseDocumentLevelConfig/helpers/getTranslationMetadata.ts +2 -0
  33. package/src/configuration/baseDocumentLevelConfig/helpers/patchI18nDoc.ts +31 -8
  34. package/src/configuration/baseDocumentLevelConfig/index.ts +18 -101
  35. package/src/configuration/baseFieldLevelConfig.ts +19 -51
  36. package/src/configuration/utils/checkSerializationVersion.ts +2 -0
  37. package/src/configuration/utils/findDocumentAtRevision.ts +2 -0
  38. package/src/configuration/utils/findLatestDraft.ts +2 -0
  39. package/src/hooks/useClient.ts +3 -1
  40. package/src/hooks/useSecrets.ts +2 -0
  41. package/src/index.ts +91 -67
  42. package/src/sanity-api/findDocuments.ts +44 -0
  43. package/src/sanity-api/publishDocuments.ts +49 -0
  44. package/src/sanity-api/resolveRefs.ts +146 -0
  45. package/src/serialization/BaseDocumentMerger.ts +138 -0
  46. package/src/serialization/BaseSerializationConfig.ts +220 -0
  47. package/src/serialization/__tests__/BaseDocumentDeserializer/__snapshots__/documentLevelDeserialization.test.ts.snap +189 -0
  48. package/src/serialization/__tests__/BaseDocumentDeserializer/__snapshots__/fieldLevelDeserialization.test.ts.snap +107 -0
  49. package/src/serialization/__tests__/BaseDocumentDeserializer/baseDeserialization.test.ts +397 -0
  50. package/src/serialization/__tests__/BaseDocumentDeserializer/documentLevelDeserialization.test.ts +107 -0
  51. package/src/serialization/__tests__/BaseDocumentDeserializer/fieldLevelDeserialization.test.ts +107 -0
  52. package/src/serialization/__tests__/BaseDocumentMerger/__snapshots__/documentLevelMerge.test.ts.snap +193 -0
  53. package/src/serialization/__tests__/BaseDocumentMerger/__snapshots__/fieldLevelMerge.test.ts.snap +97 -0
  54. package/src/serialization/__tests__/BaseDocumentMerger/baseMerge.test.ts +36 -0
  55. package/src/serialization/__tests__/BaseDocumentMerger/documentLevelMerge.test.ts +96 -0
  56. package/src/serialization/__tests__/BaseDocumentMerger/fieldLevelMerge.test.ts +142 -0
  57. package/src/serialization/__tests__/BaseDocumentMerger/utils.ts +52 -0
  58. package/src/serialization/__tests__/BaseDocumentSerializer/__snapshots__/documentInlineMarks.test.ts.snap +39 -0
  59. package/src/serialization/__tests__/BaseDocumentSerializer/__snapshots__/documentLevelSerialization.test.ts.snap +8 -0
  60. package/src/serialization/__tests__/BaseDocumentSerializer/__snapshots__/fieldLevelSerialization.test.ts.snap +8 -0
  61. package/src/serialization/__tests__/BaseDocumentSerializer/baseSerialization.test.ts +345 -0
  62. package/src/serialization/__tests__/BaseDocumentSerializer/documentInlineMarks.test.ts +53 -0
  63. package/src/serialization/__tests__/BaseDocumentSerializer/documentLevelSerialization.test.ts +120 -0
  64. package/src/serialization/__tests__/BaseDocumentSerializer/fieldLevelSerialization.test.ts +153 -0
  65. package/src/serialization/__tests__/BaseDocumentSerializer/utils.ts +27 -0
  66. package/src/serialization/__tests__/README +2 -0
  67. package/src/serialization/__tests__/__fixtures__/annotationAndInlineBlocks.json +140 -0
  68. package/src/serialization/__tests__/__fixtures__/customStyles.json +62 -0
  69. package/src/serialization/__tests__/__fixtures__/documentInlineMarks.json +70 -0
  70. package/src/serialization/__tests__/__fixtures__/documentLevelArticle.json +185 -0
  71. package/src/serialization/__tests__/__fixtures__/fieldLevelArticle.json +107 -0
  72. package/src/serialization/__tests__/__fixtures__/inlineDocumentLevelArticle.json +134 -0
  73. package/src/serialization/__tests__/__fixtures__/inlineSchema.ts +270 -0
  74. package/src/serialization/__tests__/__fixtures__/messy-html.html +26 -0
  75. package/src/serialization/__tests__/__fixtures__/nestedLanguageFields.json +54 -0
  76. package/src/serialization/__tests__/__fixtures__/schema.ts +310 -0
  77. package/src/serialization/__tests__/global.setup.ts +40 -0
  78. package/src/serialization/__tests__/helpers.ts +132 -0
  79. package/src/serialization/data.ts +82 -0
  80. package/src/serialization/deserialize/BaseDocumentDeserializer.ts +171 -0
  81. package/src/serialization/deserialize/helpers.ts +42 -0
  82. package/src/serialization/helpers.ts +18 -0
  83. package/src/serialization/index.ts +11 -0
  84. package/src/serialization/serialize/fieldFilters.ts +124 -0
  85. package/src/serialization/serialize/index.ts +284 -0
  86. package/src/serialization/types.ts +41 -0
  87. package/src/translation/checkTranslationStatus.ts +42 -0
  88. package/src/translation/createJobs.ts +16 -0
  89. package/src/translation/downloadTranslations.ts +68 -0
  90. package/src/translation/importDocument.ts +23 -0
  91. package/src/translation/initProject.ts +61 -0
  92. package/src/translation/uploadFiles.ts +32 -0
  93. package/src/types.ts +7 -20
  94. package/src/utils/applyDocuments.ts +72 -0
  95. package/src/utils/batchProcessor.ts +111 -0
  96. package/src/utils/importUtils.ts +95 -0
  97. package/src/utils/serialize.ts +52 -0
  98. package/src/utils/shared.ts +1 -0
  99. package/src/adapter/index.ts +0 -13
  100. package/src/components/NewTask.tsx +0 -249
  101. package/src/components/TaskView.tsx +0 -255
  102. package/src/components/TranslationContext.tsx +0 -19
  103. package/src/components/TranslationView.tsx +0 -82
  104. package/src/components/TranslationsTab.tsx +0 -177
  105. package/src/configuration/baseDocumentLevelConfig/helpers/index.ts +0 -5
  106. package/src/configuration/baseDocumentLevelConfig/legacyDocumentLevelPatch.ts +0 -69
  107. package/src/configuration/index.ts +0 -18
  108. package/src/configuration/utils/index.ts +0 -3
@@ -0,0 +1,47 @@
1
+ import { useCallback } from 'react';
2
+ import { Button, Flex, Switch, Box, Text } from '@sanity/ui';
3
+ import styled from 'styled-components';
4
+ import { TranslationLocale } from '../../types';
5
+
6
+ const WrapText = styled(Box)`
7
+ white-space: normal;
8
+ `;
9
+
10
+ type LocaleCheckboxProps = {
11
+ locale: TranslationLocale;
12
+ toggle: (locale: string, shouldEnable: boolean) => void;
13
+ checked: boolean;
14
+ };
15
+
16
+ export const LocaleCheckbox = ({
17
+ locale,
18
+ toggle,
19
+ checked,
20
+ }: LocaleCheckboxProps) => {
21
+ const onClick = useCallback(
22
+ () => toggle(locale.localeId, !checked),
23
+ [locale.localeId, toggle, checked]
24
+ );
25
+
26
+ return (
27
+ <Button
28
+ mode='ghost'
29
+ onClick={onClick}
30
+ style={{ cursor: 'pointer' }}
31
+ radius={2}
32
+ >
33
+ <Flex align='center' gap={3}>
34
+ <Switch
35
+ style={{ pointerEvents: 'none' }}
36
+ onChange={onClick}
37
+ checked={checked}
38
+ />
39
+ <WrapText>
40
+ <Text size={1} weight='semibold'>
41
+ {locale.description}
42
+ </Text>
43
+ </WrapText>
44
+ </Flex>
45
+ </Button>
46
+ );
47
+ };
@@ -1,3 +1,5 @@
1
+ // adapted from https://github.com/sanity-io/sanity-translations-tab. See LICENSE.md for more details.
2
+
1
3
  import { Card, Flex, Label } from '@sanity/ui';
2
4
 
3
5
  export default function ProgressBar({ progress }: { progress: number }) {
@@ -0,0 +1,108 @@
1
+ import React from 'react';
2
+ import { Stack, Box, Card, Text, Flex, Spinner } from '@sanity/ui';
3
+ import { LanguageStatus } from './LanguageStatus';
4
+ import { useTranslations } from '../TranslationsProvider';
5
+ import { pluginConfig } from '../../adapter/core';
6
+
7
+ export const SingleDocumentView: React.FC = () => {
8
+ const {
9
+ documents,
10
+ locales,
11
+ loadingDocuments,
12
+ translationStatuses,
13
+ downloadStatus,
14
+ importedTranslations,
15
+ handleImportDocument,
16
+ } = useTranslations();
17
+
18
+ // Get the first (and only) document in single document mode
19
+ const document = documents[0];
20
+
21
+ if (loadingDocuments) {
22
+ return (
23
+ <Flex align='center' justify='center' padding={4}>
24
+ <Spinner />
25
+ </Flex>
26
+ );
27
+ }
28
+
29
+ if (!document) {
30
+ return (
31
+ <Card padding={4} tone='caution'>
32
+ <Text>No document found</Text>
33
+ </Card>
34
+ );
35
+ }
36
+
37
+ // Check if this is a source language document
38
+ const currentDocumentLanguage =
39
+ document[pluginConfig.getLanguageField()] || pluginConfig.getSourceLocale();
40
+ const shouldShowTranslationComponents =
41
+ currentDocumentLanguage === pluginConfig.getSourceLocale();
42
+
43
+ if (!shouldShowTranslationComponents) {
44
+ return (
45
+ <Card padding={4} tone='neutral' border>
46
+ <Text size={1} muted>
47
+ Translation tools are only available for{' '}
48
+ <code>{pluginConfig.getSourceLocale()}</code> documents.
49
+ </Text>
50
+ </Card>
51
+ );
52
+ }
53
+
54
+ return (
55
+ <Box>
56
+ <Stack space={4}>
57
+ <Card shadow={1} padding={3}>
58
+ <Stack space={3}>
59
+ <Flex justify='space-between' align='flex-start'>
60
+ <Box flex={1}>
61
+ <Text weight='semibold' size={1}>
62
+ {document._id?.replace('drafts.', '') || document._id}
63
+ </Text>
64
+ <Text size={0} muted style={{ marginTop: '2px' }}>
65
+ {document._type}
66
+ </Text>
67
+ </Box>
68
+ </Flex>
69
+
70
+ <Stack space={2}>
71
+ {locales.length > 0 ? (
72
+ locales
73
+ .filter((locale) => locale.enabled !== false)
74
+ .map((locale) => {
75
+ const documentId =
76
+ document._id?.replace('drafts.', '') || document._id;
77
+ const key = `${documentId}:${locale.localeId}`;
78
+ const status = translationStatuses.get(key);
79
+ const isDownloaded = downloadStatus.downloaded.has(key);
80
+ const isImported = importedTranslations.has(key);
81
+
82
+ return (
83
+ <LanguageStatus
84
+ key={`${document._id}-${locale.localeId}`}
85
+ title={locale.description || locale.localeId}
86
+ progress={status?.progress || 0}
87
+ isImported={isImported || isDownloaded}
88
+ importFile={async () => {
89
+ await handleImportDocument(
90
+ documentId,
91
+ locale.localeId
92
+ );
93
+ }}
94
+ />
95
+ );
96
+ })
97
+ ) : (
98
+ <Text size={1} muted>
99
+ No locales configured
100
+ </Text>
101
+ )}
102
+ </Stack>
103
+ </Stack>
104
+ </Card>
105
+ </Stack>
106
+ </Box>
107
+ );
108
+ };
@@ -0,0 +1,379 @@
1
+ // adapted from https://github.com/sanity-io/sanity-translations-tab. See LICENSE.md for more details.
2
+
3
+ /**
4
+ * Add cleanup function to cancel async tasks
5
+ */
6
+
7
+ import { useMemo, useState, useCallback, useEffect } from 'react';
8
+ import {
9
+ Stack,
10
+ Text,
11
+ Card,
12
+ Button,
13
+ Grid,
14
+ Box,
15
+ Flex,
16
+ Switch,
17
+ Tooltip,
18
+ } from '@sanity/ui';
19
+ import { pluginConfig } from '../../adapter/core';
20
+ import { useTranslations } from '../TranslationsProvider';
21
+ import { LanguageStatus } from '../shared/LanguageStatus';
22
+ import { LocaleCheckbox } from '../shared/LocaleCheckbox';
23
+ import { DownloadIcon, LinkIcon } from '@sanity/icons';
24
+
25
+ export const TranslationView = () => {
26
+ const {
27
+ documents,
28
+ locales,
29
+ translationStatuses,
30
+ isBusy,
31
+ handleTranslateAll,
32
+ handleImportDocument,
33
+ handleRefreshAll,
34
+ isRefreshing,
35
+ importedTranslations,
36
+ setLocales,
37
+ handlePatchDocumentReferences,
38
+ } = useTranslations();
39
+
40
+ const [autoImport, setAutoImport] = useState(false);
41
+ const [isImporting, setIsImporting] = useState(false);
42
+ const [autoRefresh, setAutoRefresh] = useState(true);
43
+
44
+ // Get the single document (first document in single document mode)
45
+ const document = documents[0];
46
+
47
+ // Extract the current document's language from the language field
48
+ const currentDocumentLanguage = useMemo(() => {
49
+ if (!document) return null;
50
+
51
+ // Get the language from the document's language field
52
+ const languageField = pluginConfig.getLanguageField();
53
+ const documentLanguage = document[languageField];
54
+
55
+ // If no language field is set, assume it's the source language
56
+ return documentLanguage || pluginConfig.getSourceLocale();
57
+ }, [document]);
58
+
59
+ // Only show translation components if we're on a source language document
60
+ const shouldShowTranslationComponents = useMemo(() => {
61
+ if (!currentDocumentLanguage) return false;
62
+ return currentDocumentLanguage === pluginConfig.getSourceLocale();
63
+ }, [currentDocumentLanguage]);
64
+
65
+ // Get available locales (excluding source locale)
66
+ const availableLocales = useMemo(() => {
67
+ const sourceLocale = pluginConfig.getSourceLocale();
68
+ return locales.filter(
69
+ (locale) => locale.enabled !== false && locale.localeId !== sourceLocale
70
+ );
71
+ }, [locales]);
72
+
73
+ // Get document ID for status tracking
74
+ const documentId = useMemo(() => {
75
+ if (!document) return null;
76
+ return document._id?.replace('drafts.', '') || document._id;
77
+ }, [document]);
78
+
79
+ // Auto import functionality
80
+ const checkAndImportCompletedTranslations = useCallback(async () => {
81
+ if (!autoImport || isImporting || !documentId) return;
82
+
83
+ const completedTranslations = availableLocales.filter((locale) => {
84
+ const key = `${documentId}:${locale.localeId}`;
85
+ const status = translationStatuses.get(key);
86
+ return (
87
+ (status?.progress || 0) >= 100 &&
88
+ status?.isReady &&
89
+ !importedTranslations.has(key)
90
+ );
91
+ });
92
+
93
+ if (completedTranslations.length === 0) return;
94
+
95
+ setIsImporting(true);
96
+ try {
97
+ for (const locale of completedTranslations) {
98
+ await handleImportDocument(documentId, locale.localeId);
99
+ }
100
+ } finally {
101
+ setIsImporting(false);
102
+ }
103
+ }, [
104
+ autoImport,
105
+ isImporting,
106
+ documentId,
107
+ availableLocales,
108
+ translationStatuses,
109
+ importedTranslations,
110
+ handleImportDocument,
111
+ ]);
112
+
113
+ const handleImportAll = useCallback(async () => {
114
+ if (isImporting || !documentId) return;
115
+
116
+ setIsImporting(true);
117
+ try {
118
+ const readyTranslations = availableLocales.filter((locale) => {
119
+ const key = `${documentId}:${locale.localeId}`;
120
+ const status = translationStatuses.get(key);
121
+ return status?.isReady && !importedTranslations.has(key);
122
+ });
123
+
124
+ for (const locale of readyTranslations) {
125
+ await handleImportDocument(documentId, locale.localeId);
126
+ }
127
+ } finally {
128
+ setIsImporting(false);
129
+ }
130
+ }, [
131
+ isImporting,
132
+ documentId,
133
+ availableLocales,
134
+ translationStatuses,
135
+ importedTranslations,
136
+ handleImportDocument,
137
+ ]);
138
+
139
+ // Check for completed translations on status updates
140
+ useEffect(() => {
141
+ checkAndImportCompletedTranslations();
142
+ }, [checkAndImportCompletedTranslations]);
143
+
144
+ // Auto refresh functionality
145
+ useEffect(() => {
146
+ if (!autoRefresh || !documentId || availableLocales.length === 0) return;
147
+
148
+ const interval = setInterval(async () => {
149
+ await handleRefreshAll();
150
+ await checkAndImportCompletedTranslations();
151
+ }, 10000);
152
+
153
+ return () => clearInterval(interval);
154
+ }, [
155
+ autoRefresh,
156
+ documentId,
157
+ availableLocales.length,
158
+ handleRefreshAll,
159
+ checkAndImportCompletedTranslations,
160
+ ]);
161
+
162
+ useEffect(() => {
163
+ const initialRefresh = async () => {
164
+ await handleRefreshAll();
165
+ await checkAndImportCompletedTranslations();
166
+ };
167
+ initialRefresh();
168
+ }, []);
169
+
170
+ // Locale toggle functionality
171
+ const toggleLocale = useCallback(
172
+ (localeId: string, shouldEnable: boolean) => {
173
+ const updatedLocales = locales.map((locale) =>
174
+ locale.localeId === localeId
175
+ ? { ...locale, enabled: shouldEnable }
176
+ : locale
177
+ );
178
+ setLocales(updatedLocales);
179
+ },
180
+ [locales, setLocales]
181
+ );
182
+
183
+ const toggleAllLocales = useCallback(() => {
184
+ const sourceLocale = pluginConfig.getSourceLocale();
185
+ const nonSourceLocales = locales.filter(
186
+ (locale) => locale.localeId !== sourceLocale
187
+ );
188
+ const allEnabled = nonSourceLocales.every(
189
+ (locale) => locale.enabled === true || locale.enabled === undefined
190
+ );
191
+
192
+ const updatedLocales = locales.map((locale) =>
193
+ locale.localeId === sourceLocale
194
+ ? locale // Don't change source locale
195
+ : { ...locale, enabled: !allEnabled }
196
+ );
197
+ setLocales(updatedLocales);
198
+ }, [locales, setLocales]);
199
+
200
+ // Show message if we're not on a source language document
201
+ if (!shouldShowTranslationComponents) {
202
+ return (
203
+ <Card padding={4} tone='neutral' border>
204
+ <Text size={1} muted>
205
+ Translation tools are only available for{' '}
206
+ <code>{pluginConfig.getSourceLocale()}</code> documents.
207
+ </Text>
208
+ </Card>
209
+ );
210
+ }
211
+
212
+ return (
213
+ <Stack space={6} padding={4}>
214
+ {/* Generate Translations Section */}
215
+ <Stack space={4}>
216
+ <Text as='h2' weight='semibold' size={2}>
217
+ Generate Translations
218
+ </Text>
219
+
220
+ {/* Locale Selection */}
221
+ <Stack space={3}>
222
+ <Flex align='center' justify='space-between'>
223
+ <Text weight='semibold' size={1}>
224
+ {availableLocales.length === 1
225
+ ? 'Select locale'
226
+ : 'Select locales'}
227
+ </Text>
228
+ <Button
229
+ fontSize={1}
230
+ padding={2}
231
+ text='Toggle All'
232
+ onClick={toggleAllLocales}
233
+ />
234
+ </Flex>
235
+
236
+ <Grid columns={[1, 1, 2, 3]} gap={1}>
237
+ {locales
238
+ .filter(
239
+ (locale) => locale.localeId !== pluginConfig.getSourceLocale()
240
+ )
241
+ .map((locale) => (
242
+ <LocaleCheckbox
243
+ key={locale.localeId}
244
+ locale={locale}
245
+ toggle={toggleLocale}
246
+ checked={
247
+ locale.enabled === true || locale.enabled === undefined
248
+ }
249
+ />
250
+ ))}
251
+ </Grid>
252
+ </Stack>
253
+
254
+ <Button
255
+ onClick={() => {
256
+ setAutoImport(true);
257
+ handleTranslateAll();
258
+ }}
259
+ disabled={isBusy || !availableLocales.length}
260
+ tone='positive'
261
+ text={isBusy ? 'Creating translations...' : 'Generate Translations'}
262
+ />
263
+ </Stack>
264
+
265
+ {/* Translation Status Section */}
266
+ {documentId && availableLocales.length > 0 && (
267
+ <Stack space={4}>
268
+ <Flex align='center' justify='space-between'>
269
+ <Text as='h2' weight='semibold' size={2}>
270
+ Translation Status
271
+ </Text>
272
+ <Flex gap={3} align='center'>
273
+ <Flex gap={2} align='center'>
274
+ <Text size={1}>Auto-refresh</Text>
275
+ <Switch
276
+ checked={autoRefresh}
277
+ onChange={() => setAutoRefresh(!autoRefresh)}
278
+ />
279
+ </Flex>
280
+ <Button
281
+ fontSize={1}
282
+ padding={2}
283
+ text='Refresh Status'
284
+ onClick={handleRefreshAll}
285
+ disabled={isRefreshing}
286
+ />
287
+ </Flex>
288
+ </Flex>
289
+
290
+ <Box>
291
+ {availableLocales.map((locale) => {
292
+ const key = `${documentId}:${locale.localeId}`;
293
+ const status = translationStatuses.get(key);
294
+ const progress = status?.progress || 0;
295
+ const isImported = importedTranslations.has(key);
296
+
297
+ return (
298
+ <LanguageStatus
299
+ key={key}
300
+ title={locale.description}
301
+ progress={progress}
302
+ isImported={isImported}
303
+ importFile={async () => {
304
+ if (!isImported && status?.isReady) {
305
+ await handleImportDocument(documentId, locale.localeId);
306
+ }
307
+ }}
308
+ />
309
+ );
310
+ })}
311
+ </Box>
312
+
313
+ {/* Import Controls */}
314
+ <Stack space={3}>
315
+ <Flex gap={3} align='center' justify='space-between'>
316
+ <Flex gap={2} align='center'>
317
+ <Button
318
+ mode='ghost'
319
+ onClick={handleImportAll}
320
+ text={isImporting ? 'Importing...' : 'Import All'}
321
+ icon={DownloadIcon}
322
+ disabled={
323
+ isImporting ||
324
+ availableLocales.every((locale) => {
325
+ const key = `${documentId}:${locale.localeId}`;
326
+ const status = translationStatuses.get(key);
327
+ return !status?.isReady || importedTranslations.has(key);
328
+ })
329
+ }
330
+ />
331
+ <Text size={1} muted>
332
+ Imported{' '}
333
+ {
334
+ availableLocales.filter((locale) => {
335
+ const key = `${documentId}:${locale.localeId}`;
336
+ return importedTranslations.has(key);
337
+ }).length
338
+ }
339
+ /
340
+ {
341
+ availableLocales.filter((locale) => {
342
+ const key = `${documentId}:${locale.localeId}`;
343
+ const status = translationStatuses.get(key);
344
+ return status?.isReady;
345
+ }).length
346
+ }
347
+ </Text>
348
+ </Flex>
349
+ <Flex gap={2} align='center' style={{ whiteSpace: 'nowrap' }}>
350
+ <Text size={1}>Auto-import when complete</Text>
351
+ <Switch
352
+ checked={autoImport}
353
+ onChange={() => setAutoImport(!autoImport)}
354
+ disabled={isImporting}
355
+ />
356
+ </Flex>
357
+ </Flex>
358
+
359
+ <Flex justify='flex-start'>
360
+ <Tooltip
361
+ placement='top'
362
+ content={`Replaces references to ${pluginConfig.getSourceLocale()} documents in this document with the corresponding translated document reference`}
363
+ >
364
+ <Button
365
+ mode='ghost'
366
+ tone='caution'
367
+ onClick={handlePatchDocumentReferences}
368
+ text={isBusy ? 'Patching...' : 'Patch Document References'}
369
+ icon={isBusy ? null : LinkIcon}
370
+ disabled={isBusy}
371
+ />
372
+ </Tooltip>
373
+ </Flex>
374
+ </Stack>
375
+ </Stack>
376
+ )}
377
+ </Stack>
378
+ );
379
+ };
@@ -0,0 +1,25 @@
1
+ import React from 'react';
2
+ import { SanityDocument } from 'sanity';
3
+ import { BaseTranslationWrapper } from '../shared/BaseTranslationWrapper';
4
+ import { TranslationsProvider } from '../TranslationsProvider';
5
+ import { TranslationView } from './TranslationView';
6
+
7
+ type TranslationTabProps = {
8
+ document: {
9
+ displayed: SanityDocument;
10
+ };
11
+ };
12
+
13
+ const TranslationTab = (props: TranslationTabProps) => {
14
+ const { displayed } = props.document;
15
+
16
+ return (
17
+ <BaseTranslationWrapper showContainer={false}>
18
+ <TranslationsProvider singleDocument={displayed}>
19
+ <TranslationView />
20
+ </TranslationsProvider>
21
+ </BaseTranslationWrapper>
22
+ );
23
+ };
24
+
25
+ export default TranslationTab;
@@ -1,14 +1,15 @@
1
+ // adapted from https://github.com/sanity-io/sanity-translations-tab. See LICENSE.md for more details.
2
+
1
3
  import { SanityClient, SanityDocument, SanityDocumentLike } from 'sanity';
2
- import { BaseDocumentMerger } from 'sanity-naive-html-serializer';
4
+ import { BaseDocumentMerger } from '../../serialization';
3
5
 
4
- import { findLatestDraft, findDocumentAtRevision } from '../utils';
5
- import {
6
- createI18nDocAndPatchMetadata,
7
- getOrCreateTranslationMetadata,
8
- patchI18nDoc,
9
- } from './helpers';
6
+ import { findLatestDraft } from '../utils/findLatestDraft';
7
+ import { findDocumentAtRevision } from '../utils/findDocumentAtRevision';
8
+ import { createI18nDocAndPatchMetadata } from './helpers/createI18nDocAndPatchMetadata';
9
+ import { getOrCreateTranslationMetadata } from './helpers/getOrCreateTranslationMetadata';
10
+ import { patchI18nDoc } from './helpers/patchI18nDoc';
10
11
  import type { GTFile } from '../../types';
11
- import { gtConfig } from '../../adapter/core';
12
+ import { pluginConfig } from '../../adapter/core';
12
13
 
13
14
  export const documentLevelPatch = async (
14
15
  docInfo: GTFile,
@@ -18,7 +19,7 @@ export const documentLevelPatch = async (
18
19
  languageField: string = 'language',
19
20
  mergeWithTargetLocale: boolean = false
20
21
  ): Promise<void> => {
21
- const baseLanguage = gtConfig.getSourceLocale();
22
+ const baseLanguage = pluginConfig.getSourceLocale();
22
23
  //this is the document we use to merge with the translated fields
23
24
  let baseDoc: SanityDocument | null = null;
24
25
 
@@ -92,16 +93,25 @@ export const documentLevelPatch = async (
92
93
  ) as SanityDocumentLike;
93
94
 
94
95
  if (i18nDoc) {
95
- patchI18nDoc(docInfo.documentId, merged, translatedFields, client);
96
+ await patchI18nDoc(
97
+ docInfo.documentId,
98
+ i18nDoc._id,
99
+ baseDoc,
100
+ merged,
101
+ translatedFields,
102
+ client
103
+ );
96
104
  }
97
105
  //otherwise, create a new document
98
106
  //and add the document reference to the metadata document
99
107
  else {
100
- createI18nDocAndPatchMetadata(
108
+ await createI18nDocAndPatchMetadata(
109
+ baseDoc,
101
110
  merged,
102
111
  localeId,
103
112
  client,
104
113
  translationMetadata,
114
+ docInfo.documentId,
105
115
  languageField
106
116
  );
107
117
  }