@l10nmonster/server 3.0.0-alpha.1 → 3.0.0-alpha.11

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 (43) hide show
  1. package/.releaserc.json +31 -0
  2. package/CHANGELOG.md +6 -0
  3. package/CLAUDE.md +740 -0
  4. package/README.md +90 -23
  5. package/index.js +111 -33
  6. package/package.json +38 -10
  7. package/routes/dispatcher.js +77 -0
  8. package/routes/info.js +24 -0
  9. package/routes/providers.js +19 -0
  10. package/routes/sources.js +14 -0
  11. package/routes/status.js +30 -0
  12. package/routes/tm.js +71 -0
  13. package/ui/dist/assets/Cart-jHOAGjwC.js +1 -0
  14. package/ui/dist/assets/Job-vnJpBXcp.js +1 -0
  15. package/ui/dist/assets/Providers-DUp_y6mi.js +1 -0
  16. package/ui/dist/assets/Sources-CC5dBiir.js +1 -0
  17. package/ui/dist/assets/Status-SRPm28Cg.js +1 -0
  18. package/ui/dist/assets/StatusDetail-DqXzi16U.js +1 -0
  19. package/ui/dist/assets/TM-CUqywRKC.js +1 -0
  20. package/ui/dist/assets/TMDetail-Bl4xHL_X.js +1 -0
  21. package/ui/dist/assets/Welcome-D33rOKBc.js +1 -0
  22. package/ui/dist/assets/api-CQ_emPlI.js +1 -0
  23. package/ui/dist/assets/badge-D0XxOmqE.js +1 -0
  24. package/ui/dist/assets/grid-c9ySqPWI.js +1 -0
  25. package/ui/dist/assets/index-BHll-Wur.js +1 -0
  26. package/ui/dist/assets/index-CE6JgXwC.js +76 -0
  27. package/ui/dist/assets/input-C_b-hL0i.js +1 -0
  28. package/ui/dist/assets/link-BJrTt4Ej.js +1 -0
  29. package/ui/dist/assets/namespace-DqVzlNZ0.js +1 -0
  30. package/ui/dist/assets/select-B7OxVhQ5.js +1 -0
  31. package/ui/dist/assets/tooltip-DsbPmc5I.js +1 -0
  32. package/ui/dist/assets/use-field-context-DPgnX2eS.js +1 -0
  33. package/ui/dist/assets/use-presence-context-w7qGDwCc.js +1 -0
  34. package/ui/dist/assets/useQuery-DJATtsyt.js +1 -0
  35. package/ui/dist/assets/v-stack-BLV93Nj6.js +1 -0
  36. package/ui/dist/favicon.ico +0 -0
  37. package/ui/dist/index.html +1 -1
  38. package/ui/dist/logo.svg +123 -0
  39. package/ui/dist/manifest.json +3 -8
  40. package/mockData.js +0 -67
  41. package/ui/dist/assets/index-DexglpRr.js +0 -253
  42. package/ui/dist/logo192.png +0 -0
  43. package/ui/dist/logo512.png +0 -0
package/CLAUDE.md ADDED
@@ -0,0 +1,740 @@
1
+ # Chakra UI v3 Development Guide
2
+
3
+ This file provides specific guidance for working with Chakra UI v3.x in this project. **IMPORTANT: Chakra UI v3 is NOT backward compatible with v2.x - always check the v3 documentation.**
4
+
5
+ ## Current Version
6
+
7
+ This project uses Chakra UI v3.2.0. Always verify component APIs in the official v3 documentation.
8
+
9
+ ## Key Breaking Changes from v2 to v3
10
+
11
+ ### 1. Compound Component Pattern
12
+ Most components now use a compound pattern instead of single components:
13
+
14
+ ```jsx
15
+ // ❌ v2 Pattern (DON'T USE)
16
+ <Switch isChecked={value} onChange={setValue} />
17
+
18
+ // ✅ v3 Pattern (CORRECT)
19
+ <Switch.Root checked={value} onCheckedChange={(details) => setValue(details.checked)}>
20
+ <Switch.HiddenInput />
21
+ <Switch.Control>
22
+ <Switch.Thumb />
23
+ </Switch.Control>
24
+ <Switch.Label>Label text</Switch.Label>
25
+ </Switch.Root>
26
+ ```
27
+
28
+ ### 2. Event Handler Changes
29
+ Event handlers often receive objects instead of direct values:
30
+
31
+ ```jsx
32
+ // ❌ v2: Direct value
33
+ onChange={(value) => setValue(value)}
34
+
35
+ // ✅ v3: Object with details
36
+ onCheckedChange={(details) => setValue(details.checked)}
37
+ onValueChange={(details) => setValue(details.value)}
38
+ ```
39
+
40
+ ### 3. Prop Name Changes
41
+ Many prop names have changed:
42
+
43
+ ```jsx
44
+ // ❌ v2 Props
45
+ isChecked, isOpen, isDisabled, isInvalid
46
+
47
+ // ✅ v3 Props
48
+ checked, open, disabled, invalid
49
+ ```
50
+
51
+ ### 4. Color System Changes
52
+ Chakra UI v3 uses semantic color tokens instead of numbered scales:
53
+
54
+ ```jsx
55
+ // ❌ v2 Color System (DON'T USE)
56
+ bg="gray.50" // Light gray background
57
+ bg="gray.100" // Slightly darker gray
58
+ color="gray.500" // Medium gray text
59
+ color="gray.600" // Darker gray text
60
+ color="blue.600" // Blue text
61
+ bg="blue.50" // Light blue background
62
+
63
+ // ✅ v3 Semantic Colors (CORRECT)
64
+ bg="bg.muted" // Light background
65
+ bg="bg.subtle" // Subtle background
66
+ color="fg.muted" // Muted text
67
+ color="fg.default" // Default text
68
+ color="blue.600" // Blue text (some numbered colors still work)
69
+ bg="blue.subtle" // Light blue background
70
+ bg="blue.muted" // Muted blue background
71
+ borderColor="border.default" // Default border color
72
+ ```
73
+
74
+ **Common v3 semantic tokens:**
75
+ - `bg.default` - Default background
76
+ - `bg.muted` - Muted background
77
+ - `bg.subtle` - Subtle background
78
+ - `fg.default` - Default text
79
+ - `fg.muted` - Muted text
80
+ - `border.default` - Default border
81
+ - `blue.subtle` - Light blue
82
+ - `blue.muted` - Muted blue
83
+ - `gray.subtle` - Light gray
84
+ - `gray.muted` - Muted gray
85
+
86
+ ## Common Components Reference
87
+
88
+ ### Switch
89
+ ```jsx
90
+ <Switch.Root checked={value} onCheckedChange={(details) => setValue(details.checked)}>
91
+ <Switch.HiddenInput />
92
+ <Switch.Control>
93
+ <Switch.Thumb />
94
+ </Switch.Control>
95
+ <Switch.Label>Switch Label</Switch.Label>
96
+ </Switch.Root>
97
+ ```
98
+
99
+ ### Select
100
+ ```jsx
101
+ <Select.Root
102
+ value={selectedValue ? [selectedValue] : []}
103
+ onValueChange={(details) => {
104
+ // Note: onValueChange may not work reliably in Chakra UI v3
105
+ // Use direct onClick on Select.Item as workaround
106
+ }}
107
+ positioning={{
108
+ strategy: "absolute",
109
+ placement: "bottom-start",
110
+ flip: true,
111
+ gutter: 4
112
+ }}
113
+ >
114
+ <Select.Trigger>
115
+ {/* Use Text component for reliable value display */}
116
+ <Text fontSize="sm" flex="1" textAlign="left">
117
+ {selectedValue || "Select option"}
118
+ </Text>
119
+ <Select.Indicator />
120
+ </Select.Trigger>
121
+ <Select.Positioner>
122
+ <Select.Content
123
+ zIndex={1000}
124
+ bg="white"
125
+ borderWidth="1px"
126
+ borderColor="border.default"
127
+ borderRadius="md"
128
+ shadow="lg"
129
+ maxH="200px"
130
+ overflow="auto"
131
+ >
132
+ {options.map((option) => (
133
+ <Select.Item
134
+ key={option.id}
135
+ item={option.id}
136
+ value={option.id}
137
+ onClick={() => setSelectedValue(option.id)}
138
+ >
139
+ <Select.ItemText>{option.label}</Select.ItemText>
140
+ <Select.ItemIndicator />
141
+ </Select.Item>
142
+ ))}
143
+ </Select.Content>
144
+ </Select.Positioner>
145
+ </Select.Root>
146
+ ```
147
+
148
+ **Select Component Known Issues:**
149
+ - `onValueChange` may not trigger reliably in Chakra UI v3
150
+ - `Select.ValueText` doesn't display selected values consistently
151
+ - **Workarounds:**
152
+ - Use `Text` component in trigger for value display
153
+ - Use direct `onClick` on `Select.Item` for selection handling
154
+ - Keep `value` prop for maintaining component state
155
+
156
+ ### Button
157
+ ```jsx
158
+ // Simple button
159
+ <Button variant="solid" size="md">Click me</Button>
160
+
161
+ // Button with active state
162
+ <Button data-active>Active Button</Button>
163
+ ```
164
+
165
+ ### Input with Field
166
+ ```jsx
167
+ <Field.Root invalid={hasError}>
168
+ <Field.Label>Email</Field.Label>
169
+ <Input placeholder="Enter email" />
170
+ <Field.ErrorText>This field is required</Field.ErrorText>
171
+ </Field.Root>
172
+ ```
173
+
174
+ ### Accordion
175
+ ```jsx
176
+ <Accordion.Root>
177
+ <Accordion.Item>
178
+ <Accordion.ItemTrigger />
179
+ <Accordion.ItemContent />
180
+ </Accordion.Item>
181
+ </Accordion.Root>
182
+ ```
183
+
184
+ ### Modal/Dialog
185
+ ```jsx
186
+ <Dialog.Root open={isOpen} onOpenChange={(details) => setIsOpen(details.open)}>
187
+ <Dialog.Trigger asChild>
188
+ <Button>Open Dialog</Button>
189
+ </Dialog.Trigger>
190
+ <Dialog.Backdrop />
191
+ <Dialog.Positioner>
192
+ <Dialog.Content>
193
+ <Dialog.Header>
194
+ <Dialog.Title>Title</Dialog.Title>
195
+ <Dialog.CloseTrigger />
196
+ </Dialog.Header>
197
+ <Dialog.Body>Content</Dialog.Body>
198
+ </Dialog.Content>
199
+ </Dialog.Positioner>
200
+ </Dialog.Root>
201
+ ```
202
+
203
+ ## Development Guidelines
204
+
205
+ ### 1. Always Check Documentation First
206
+ - **Before using any component**, check the official Chakra UI v3 documentation
207
+ - **Never assume** v2 patterns will work in v3
208
+ - Use the MCP Context7 tool to get up-to-date documentation
209
+
210
+ ### 2. Common Pitfalls to Avoid
211
+ - Don't use `isChecked`, use `checked`
212
+ - Don't use `onChange` for switches, use `onCheckedChange`
213
+ - Don't use single components like `<Switch />`, use compound patterns
214
+ - Don't assume event handlers receive direct values - they often receive objects
215
+
216
+ ### 3. Testing Patterns
217
+ - Always test both states (on/off, open/closed, etc.)
218
+ - Check browser console for any React warnings about invalid props
219
+ - If you get "Element type is invalid" errors, check import paths and component names
220
+
221
+ ### 4. Import Patterns
222
+ ```jsx
223
+ // ✅ Correct v3 imports
224
+ import { Switch, Button, Field, Dialog } from '@chakra-ui/react'
225
+
226
+ // Use compound components
227
+ <Switch.Root>
228
+ <Field.Root>
229
+ <Dialog.Root>
230
+ ```
231
+
232
+ ### 5. Checkbox Component (Compound Pattern)
233
+ Checkbox follows the compound component pattern in v3:
234
+
235
+ ```jsx
236
+ // ✅ v3: Checkbox compound component pattern
237
+ import { Checkbox } from '@chakra-ui/react'
238
+
239
+ // Basic checkbox
240
+ <Checkbox.Root
241
+ checked={isChecked}
242
+ onCheckedChange={(details) => setIsChecked(details.checked)}
243
+ >
244
+ <Checkbox.HiddenInput />
245
+ <Checkbox.Control />
246
+ <Checkbox.Label>Check me</Checkbox.Label>
247
+ </Checkbox.Root>
248
+
249
+ // Checkbox with indeterminate state
250
+ <Checkbox.Root
251
+ checked={isAllSelected}
252
+ indeterminate={isIndeterminate}
253
+ onCheckedChange={(details) => handleSelectAll(details.checked)}
254
+ >
255
+ <Checkbox.HiddenInput />
256
+ <Checkbox.Control />
257
+ </Checkbox.Root>
258
+
259
+ // ❌ Alternative: Native input (if compound pattern doesn't work)
260
+ <Box
261
+ as="input"
262
+ type="checkbox"
263
+ checked={isChecked}
264
+ onChange={(e) => setIsChecked(e.target.checked)}
265
+ cursor="pointer"
266
+ />
267
+ ```
268
+
269
+ ## Debugging Tips
270
+
271
+ 1. **Invalid Element Type Errors**: Usually means wrong import or component name
272
+ 2. **Props Not Working**: Check if prop names changed from v2 to v3
273
+ 3. **Event Handlers Not Firing**: Check if handler expects an object instead of direct value
274
+ 4. **Styling Issues**: v3 uses different CSS architecture - check if style props changed
275
+ 5. **Missing Component**: If a component doesn't exist, use native HTML elements with `Box as="element"`
276
+
277
+ ## Resources
278
+
279
+ - [Chakra UI v3 Migration Guide](https://v3.chakra-ui.com/docs/migration)
280
+ - [Chakra UI v3 Components](https://v3.chakra-ui.com/docs/components)
281
+ - Use MCP Context7 tool with library ID `/llmstxt/chakra-ui-llms-full.txt` for latest docs
282
+
283
+ ## Data Fetching Architecture
284
+
285
+ ### React Query Implementation
286
+ **IMPORTANT**: This project uses React Query (@tanstack/react-query) for all data fetching. Never use manual useEffect patterns for API calls.
287
+
288
+ ```jsx
289
+ // ✅ Correct: Use React Query
290
+ import { useQuery, useInfiniteQuery } from '@tanstack/react-query';
291
+
292
+ // Simple data fetching
293
+ const { data, isLoading, error } = useQuery({
294
+ queryKey: ['tmStats'],
295
+ queryFn: () => fetchApi('/api/tm/stats'),
296
+ });
297
+
298
+ // Infinite scrolling with pagination
299
+ const {
300
+ data: infiniteData,
301
+ isLoading,
302
+ fetchNextPage,
303
+ hasNextPage,
304
+ isFetchingNextPage,
305
+ } = useInfiniteQuery({
306
+ queryKey: ['tmSearch', sourceLang, targetLang, filters],
307
+ queryFn: async ({ pageParam = 1 }) => {
308
+ const queryParams = new URLSearchParams({
309
+ sourceLang,
310
+ targetLang,
311
+ page: pageParam.toString(),
312
+ limit: '100',
313
+ ...Object.fromEntries(Object.entries(filters).filter(([_, value]) => value.trim() !== ''))
314
+ });
315
+ return fetchApi(`/api/tm/search?${queryParams}`);
316
+ },
317
+ getNextPageParam: (lastPage, allPages) => {
318
+ const hasMore = lastPage.data.length === parseInt(lastPage.limit);
319
+ return hasMore ? allPages.length + 1 : undefined;
320
+ },
321
+ staleTime: 30000, // 30 seconds for search results
322
+ });
323
+
324
+ // Flatten infinite query data
325
+ const data = useMemo(() => {
326
+ return infiniteData?.pages.flatMap(page => page.data) || [];
327
+ }, [infiniteData]);
328
+ ```
329
+
330
+ ### Query Key Patterns
331
+ Use consistent query keys that include all relevant parameters:
332
+
333
+ ```jsx
334
+ // ✅ Good query keys - include all parameters that affect the data
335
+ queryKey: ['status']
336
+ queryKey: ['tmStats']
337
+ queryKey: ['activeContentStats']
338
+ queryKey: ['info']
339
+ queryKey: ['tmSearch', sourceLang, targetLang, filters]
340
+
341
+ // ❌ Bad - missing parameters
342
+ queryKey: ['tmSearch'] // Missing language and filter context
343
+ ```
344
+
345
+ ### Configuration
346
+ React Query is configured globally in App.jsx:
347
+
348
+ ```jsx
349
+ const queryClient = new QueryClient({
350
+ defaultOptions: {
351
+ queries: {
352
+ staleTime: 5 * 60 * 1000, // 5 minutes - data considered fresh
353
+ gcTime: 10 * 60 * 1000, // 10 minutes - cache retention
354
+ },
355
+ },
356
+ });
357
+ ```
358
+
359
+ ### Automatic Benefits
360
+ React Query provides automatic:
361
+ - **Request deduplication** - Multiple identical requests become one
362
+ - **Background refetching** - Data stays fresh without user seeing loading
363
+ - **Caching** - Data persists across navigation and component unmounting
364
+ - **Error handling** - Consistent error states across components
365
+ - **Loading states** - Built-in loading indicators
366
+
367
+ ### Filter Integration
368
+ For filtered data (like search), include filters in query key:
369
+
370
+ ```jsx
371
+ // ✅ Filters in query key trigger automatic refetch
372
+ const queryKey = useMemo(() => [
373
+ 'tmSearch',
374
+ sourceLang,
375
+ targetLang,
376
+ filters
377
+ ], [sourceLang, targetLang, filters]);
378
+
379
+ // Debounced filter updates
380
+ const handleFilterChange = (column, value) => {
381
+ const newFilters = { ...filters, [column]: value };
382
+ setSelectedRows(new Set());
383
+
384
+ clearTimeout(window.tmFilterTimeout);
385
+ window.tmFilterTimeout = setTimeout(() => {
386
+ setFilters(newFilters); // This triggers React Query refetch automatically
387
+ }, 300);
388
+ };
389
+ ```
390
+
391
+ ### Legacy Patterns to Avoid
392
+ ❌ **Don't use these patterns anymore:**
393
+
394
+ ```jsx
395
+ // ❌ Manual useEffect data fetching
396
+ useEffect(() => {
397
+ const fetchData = async () => {
398
+ try {
399
+ const data = await fetchApi('/api/endpoint');
400
+ setData(data);
401
+ } catch (err) {
402
+ setError(err);
403
+ } finally {
404
+ setLoading(false);
405
+ }
406
+ };
407
+ fetchData();
408
+ }, []);
409
+
410
+ // ❌ Manual loading/error state management
411
+ const [data, setData] = useState(null);
412
+ const [loading, setLoading] = useState(true);
413
+ const [error, setError] = useState(null);
414
+
415
+ // ❌ Location-based API guards
416
+ if (location.pathname !== '/target-path') return;
417
+
418
+ // ❌ Manual AbortController management
419
+ const abortControllerRef = useRef(null);
420
+
421
+ // ❌ Manual fetch guards
422
+ const fetchedRef = useRef(false);
423
+ if (fetchedRef.current) return;
424
+ ```
425
+
426
+ ## Navigation Architecture
427
+
428
+ ### React Router Implementation
429
+ **IMPORTANT**: This project uses pure React Router navigation. No tab systems or conditional rendering based on location.
430
+
431
+ ```jsx
432
+ // ✅ Correct: Pure React Router structure
433
+ function MainLayout() {
434
+ return (
435
+ <Box minH="100vh" bg="gray.50">
436
+ {/* Header with navigation buttons */}
437
+ <Box bg="white" borderBottom="1px" borderColor="gray.200">
438
+ <Container maxWidth="6xl" py={3}>
439
+ <Flex align="center" justify="space-between">
440
+ {/* Navigation Links */}
441
+ <Flex align="center" gap={4}>
442
+ <Box
443
+ cursor="pointer"
444
+ px={3}
445
+ py={1}
446
+ borderRadius="md"
447
+ bg={isActiveRoute('/') ? "blue.100" : "transparent"}
448
+ _hover={{ bg: isActiveRoute('/') ? "blue.100" : "gray.100" }}
449
+ onClick={() => navigate('/')}
450
+ >
451
+ <Text fontSize="sm" fontWeight="medium">Home</Text>
452
+ </Box>
453
+ {/* More nav items... */}
454
+ </Flex>
455
+ </Flex>
456
+ </Container>
457
+ </Box>
458
+
459
+ {/* Route-based Content */}
460
+ <Suspense fallback={<Spinner />}>
461
+ <Routes>
462
+ <Route path="/" element={<Welcome />} />
463
+ <Route path="/status" element={<Status />} />
464
+ <Route path="/sources" element={<Sources />} />
465
+ <Route path="/tm" element={<TM />} />
466
+ <Route path="/tm/:sourceLang/:targetLang" element={<TMDetail />} />
467
+ <Route path="/cart" element={<Cart />} />
468
+ </Routes>
469
+ </Suspense>
470
+ </Box>
471
+ );
472
+ }
473
+
474
+ // App.jsx routing setup
475
+ function App() {
476
+ return (
477
+ <QueryClientProvider client={queryClient}>
478
+ <Router>
479
+ <Routes>
480
+ <Route path="/*" element={<MainLayout />} />
481
+ <Route path="*" element={<NotFoundPage />} />
482
+ </Routes>
483
+ </Router>
484
+ </QueryClientProvider>
485
+ );
486
+ }
487
+ ```
488
+
489
+ ### Active Route Detection
490
+ Simple helper function for navigation state:
491
+
492
+ ```jsx
493
+ // ✅ Clean active route detection
494
+ const isActiveRoute = (path) => {
495
+ if (path === '/') return location.pathname === '/';
496
+ return location.pathname.startsWith(path);
497
+ };
498
+ ```
499
+
500
+ ### Navigation Benefits
501
+ This architecture provides:
502
+ - **Route-based mounting** - Only current route component mounts
503
+ - **Proper lazy loading** - Components load only when visited
504
+ - **Clean URLs** - Standard web navigation patterns
505
+ - **Browser back/forward** - Natural browser behavior
506
+ - **SEO friendly** - Proper route-based structure
507
+
508
+ ### Legacy Patterns to Avoid
509
+ ❌ **Don't use these patterns anymore:**
510
+
511
+ ```jsx
512
+ // ❌ Tab system with conditional rendering
513
+ <Tabs.Root value={activeTab} onValueChange={handleTabChange}>
514
+ <Tabs.Content value="tm">
515
+ {location.pathname.startsWith('/tm/') ? <TMDetail /> : <TM />}
516
+ </Tabs.Content>
517
+ </Tabs.Root>
518
+
519
+ // ❌ Complex tab change handlers
520
+ const handleTabChange = (details) => {
521
+ const value = typeof details === 'object' ? details.value : details;
522
+ setActiveTab(value);
523
+ if (value === 'home') {
524
+ navigate('/');
525
+ } else if (value === 'tm') {
526
+ navigate('/tm');
527
+ } else {
528
+ navigate(`/${value}`);
529
+ }
530
+ };
531
+
532
+ // ❌ Location-based conditional rendering
533
+ if (location.pathname === '/cart') {
534
+ return <CartSpecialLayout />;
535
+ }
536
+
537
+ // ❌ Manual active tab state management
538
+ const [activeTab, setActiveTab] = useState(getTabFromPath(location.pathname));
539
+ ```
540
+
541
+ ## Session Storage for Cart Functionality
542
+ Store cart data grouped by keys for persistence across navigation:
543
+
544
+ ```jsx
545
+ // ✅ Session storage cart pattern
546
+ const getCart = () => {
547
+ const cartData = sessionStorage.getItem('cartKey');
548
+ return cartData ? JSON.parse(cartData) : {};
549
+ };
550
+
551
+ const saveCart = (cart) => {
552
+ sessionStorage.setItem('cartKey', JSON.stringify(cart));
553
+ };
554
+
555
+ const addToCart = (items, groupKey) => {
556
+ const cart = getCart();
557
+ if (!cart[groupKey]) cart[groupKey] = [];
558
+ cart[groupKey].push(...items);
559
+ saveCart(cart);
560
+ };
561
+ ```
562
+
563
+ ## Architecture Summary
564
+
565
+ This application now follows modern React best practices:
566
+
567
+ ### Current Stack
568
+ - **React 19** with functional components and hooks
569
+ - **Chakra UI v3** with compound component patterns
570
+ - **React Router v6** for navigation (no tab system)
571
+ - **React Query** for all data fetching and caching
572
+ - **TypeScript** for type safety (where applicable)
573
+
574
+ ### Key Architectural Decisions
575
+ 1. **Pure React Router Navigation** - No mixing with tab systems
576
+ 2. **React Query for All Data** - No manual useEffect data fetching
577
+ 3. **Route-based Component Mounting** - Components only mount when visited
578
+ 4. **Session Storage for Cart** - Persistent cart across navigation
579
+ 5. **Compound Components** - Chakra UI v3 patterns throughout
580
+
581
+ ### Performance Characteristics
582
+ - **Zero duplicate API calls** - React Query deduplication
583
+ - **Automatic caching** - Data persists across navigation
584
+ - **Route-based code splitting** - Only load what's needed
585
+ - **Background refetching** - Data stays fresh without user awareness
586
+ - **Memory efficient** - Unused components unmount properly
587
+
588
+ ### Development Guidelines
589
+ 1. **Always use React Query** for API calls
590
+ 2. **Always use Chakra UI v3 patterns** (compound components)
591
+ 3. **Never use manual useEffect** for data fetching
592
+ 4. **Never mix tab systems** with React Router
593
+ 5. **Always include relevant parameters** in query keys
594
+ 6. **Use debouncing** for filter inputs (300ms)
595
+ 7. **Handle both old and new data formats** for backward compatibility
596
+
597
+ ## API Routes Documentation
598
+
599
+ The L10n Monster server provides RESTful API endpoints for managing translation projects. All routes are prefixed with `/api`.
600
+
601
+ ### Route Structure
602
+
603
+ Routes are organized by functionality in separate files under `/server/routes/`:
604
+
605
+ ```
606
+ /server/routes/
607
+ ├── info.js # System information endpoints
608
+ ├── status.js # Project status endpoints
609
+ ├── sources.js # Content source endpoints
610
+ ├── tm.js # Translation memory endpoints
611
+ ├── providers.js # Translation provider endpoints
612
+ └── dispatcher.js # Job creation endpoints
613
+ ```
614
+
615
+ ### Available Endpoints
616
+
617
+ #### System Information
618
+ - **GET `/api/info`** - Returns system information including:
619
+ - `version`: Server version
620
+ - `providers`: Array of available provider IDs
621
+ - `config`: System configuration
622
+
623
+ #### Project Status
624
+ - **GET `/api/status`** - Returns project status and statistics
625
+
626
+ #### Content Sources
627
+ - **GET `/api/activeContentStats`** - Returns statistics about active content sources grouped by channel
628
+
629
+ #### Translation Memory
630
+ - **GET `/api/tm/stats`** - Returns translation memory statistics for all language pairs
631
+ - **GET `/api/tm/search`** - Search translation memory entries
632
+ - **Query Parameters:**
633
+ - `sourceLang`, `targetLang` (required)
634
+ - `page`, `limit` (pagination)
635
+ - `guid`, `jobGuid`, `rid`, `sid` (filtering)
636
+ - `nsrc`, `ntgt` (source/target text filtering)
637
+ - `q` (quality score filtering)
638
+ - `translationProvider` (provider filtering)
639
+ - **Response:** `{ data: [...], page: number, limit: number }`
640
+
641
+ #### Translation Providers
642
+ - **GET `/api/providers`** - Returns detailed provider information (slower)
643
+ - Use `/api/info` for just provider IDs (faster)
644
+
645
+ #### Job Management
646
+ - **POST `/api/dispatcher/createJobs`** - Create translation jobs
647
+ - **Body:**
648
+ ```json
649
+ {
650
+ "sourceLang": "en",
651
+ "targetLang": "es",
652
+ "tus": [...], // Array of translation units
653
+ "providerList": [...] // Array of provider IDs
654
+ }
655
+ ```
656
+ - **Response:** Array of created job objects with properties like `jobGuid`, `tus`, `estimatedCost`, `translationProvider`
657
+ - **Options:** Automatically applies `skipQualityCheck: true` and `skipGroupCheck: true`
658
+
659
+ - **POST `/api/dispatcher/startJobs`** - Start created jobs
660
+ - **Body:**
661
+ ```json
662
+ {
663
+ "jobs": [...], // Array of job objects from createJobs
664
+ "instructions": "..." // Optional job-specific instructions
665
+ }
666
+ ```
667
+ - **Response:** Array of job status objects: `{ sourceLang, targetLang, jobGuid, translationProvider, status }`
668
+
669
+ ### Route Implementation Pattern
670
+
671
+ Each route file exports a setup function:
672
+
673
+ ```javascript
674
+ // routes/example.js
675
+ import { logInfo, logVerbose } from '@l10nmonster/core';
676
+
677
+ export function setupExampleRoutes(router, mm) {
678
+ router.get('/example', async (req, res) => {
679
+ logInfo`/example`;
680
+ try {
681
+ // Use MonsterManager (mm) to access core functionality
682
+ const result = await mm.someMethod();
683
+ logVerbose`Processed ${result.length} items`;
684
+ res.json(result);
685
+ } catch (error) {
686
+ logInfo`Error: ${error.message}`;
687
+ res.status(500).json({ error: error.message });
688
+ }
689
+ });
690
+ }
691
+ ```
692
+
693
+ ### Integration in main server (index.js):
694
+
695
+ ```javascript
696
+ import { setupExampleRoutes } from './routes/example.js';
697
+
698
+ // In API setup
699
+ setupExampleRoutes(apiRouter, mm);
700
+ ```
701
+
702
+ ### Error Handling Standards
703
+
704
+ - **400**: Bad Request (missing/invalid parameters)
705
+ - **500**: Internal Server Error (caught exceptions)
706
+ - Always use try/catch blocks for async operations
707
+ - Log requests with `logInfo` and details with `logVerbose`
708
+ - Return JSON error objects: `{ error: "message", details?: "..." }`
709
+
710
+ ### MonsterManager Access
711
+
712
+ Routes receive the MonsterManager instance (`mm`) which provides access to:
713
+ - `mm.tmm` - Translation Memory Manager
714
+ - `mm.dispatcher` - Job Dispatcher (`createJobs`, `startJobs`)
715
+ - `mm.rm` - Resource Manager
716
+ - `mm.ops` - Operations Manager
717
+ - `mm.currencyFormatter` - Currency formatting utility
718
+
719
+ ### Job Workflow
720
+
721
+ The job management system follows a two-step process:
722
+
723
+ 1. **Create Jobs** (`/api/dispatcher/createJobs`):
724
+ - Takes TUs and provider list
725
+ - Returns job objects with estimated costs and accepted TUs
726
+ - Jobs are created but not yet started
727
+ - Providers may accept some, all, or none of the submitted TUs
728
+
729
+ 2. **Start Jobs** (`/api/dispatcher/startJobs`):
730
+ - Takes job objects from step 1 and optional instructions
731
+ - Actually initiates the translation process
732
+ - Returns status information for tracking job progress
733
+
734
+ ## Remember
735
+
736
+ **When in doubt:**
737
+ 1. **Check React Query docs** for data fetching patterns
738
+ 2. **Check Chakra UI v3 docs** for component usage
739
+ 3. **Use pure React Router** for navigation
740
+ 4. **Never guess based on v2 Chakra UI knowledge**