@l10nmonster/server 3.0.0-alpha.9 → 3.1.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 (36) hide show
  1. package/.releaserc.json +1 -1
  2. package/CHANGELOG.md +38 -0
  3. package/CLAUDE.md +808 -0
  4. package/index.js +121 -17
  5. package/package.json +9 -8
  6. package/routes/dispatcher.js +116 -0
  7. package/routes/info.js +25 -0
  8. package/routes/providers.js +17 -0
  9. package/routes/sources.js +49 -7
  10. package/routes/status.js +27 -27
  11. package/routes/tm.js +156 -6
  12. package/ui/dist/assets/Cart-CiY5V__G.js +1 -0
  13. package/ui/dist/assets/Job-D-5ikxga.js +1 -0
  14. package/ui/dist/assets/Providers-BZVmclS1.js +1 -0
  15. package/ui/dist/assets/Sources-BwZ8Vub0.js +1 -0
  16. package/ui/dist/assets/SourcesDetail-CXgslRDb.js +1 -0
  17. package/ui/dist/assets/SourcesResource-Br3Bspz2.js +1 -0
  18. package/ui/dist/assets/Status-Bx0Ui7d2.js +1 -0
  19. package/ui/dist/assets/StatusDetail-BzJ2TIme.js +1 -0
  20. package/ui/dist/assets/TMByProvider-B2MrTxO0.js +1 -0
  21. package/ui/dist/assets/TMDetail-BxbKr57p.js +1 -0
  22. package/ui/dist/assets/TMToc-CQ1zhmPh.js +1 -0
  23. package/ui/dist/assets/Welcome-Tp-UfiIW.js +1 -0
  24. package/ui/dist/assets/index-543A5WcJ.js +1 -0
  25. package/ui/dist/assets/index-CPrLFF-N.js +2 -0
  26. package/ui/dist/assets/vendor-BVgSJH5C.js +19 -0
  27. package/ui/dist/index.html +3 -1
  28. package/ui/dist/assets/Sources-D0R-Sgwf.js +0 -1
  29. package/ui/dist/assets/Status-XBRD-MuK.js +0 -1
  30. package/ui/dist/assets/TM-DZ2x6--n.js +0 -1
  31. package/ui/dist/assets/Welcome-p4gi31Lo.js +0 -1
  32. package/ui/dist/assets/api-DXOYnFyU.js +0 -1
  33. package/ui/dist/assets/badge-CveKztw5.js +0 -1
  34. package/ui/dist/assets/grid-DetiGbYY.js +0 -1
  35. package/ui/dist/assets/index-Ce8PP-0Z.js +0 -76
  36. package/ui/dist/assets/v-stack-CQ6LIfdw.js +0 -1
package/CLAUDE.md ADDED
@@ -0,0 +1,808 @@
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
+ - **Use the Chakra UI MCP server** - This project has access to specialized Chakra UI MCP tools for getting component examples, props, themes, and migration guidance
210
+
211
+ ### 2. Common Pitfalls to Avoid
212
+ - Don't use `isChecked`, use `checked`
213
+ - Don't use `onChange` for switches, use `onCheckedChange`
214
+ - Don't use single components like `<Switch />`, use compound patterns
215
+ - Don't assume event handlers receive direct values - they often receive objects
216
+
217
+ ### 3. Testing Patterns
218
+ - Always test both states (on/off, open/closed, etc.)
219
+ - Check browser console for any React warnings about invalid props
220
+ - If you get "Element type is invalid" errors, check import paths and component names
221
+
222
+ ### 4. Import Patterns
223
+ ```jsx
224
+ // ✅ Correct v3 imports
225
+ import { Switch, Button, Field, Dialog } from '@chakra-ui/react'
226
+
227
+ // Use compound components
228
+ <Switch.Root>
229
+ <Field.Root>
230
+ <Dialog.Root>
231
+ ```
232
+
233
+ ### 5. Checkbox Component (Compound Pattern)
234
+ Checkbox follows the compound component pattern in v3:
235
+
236
+ ```jsx
237
+ // ✅ v3: Checkbox compound component pattern
238
+ import { Checkbox } from '@chakra-ui/react'
239
+
240
+ // Basic checkbox
241
+ <Checkbox.Root
242
+ checked={isChecked}
243
+ onCheckedChange={(details) => setIsChecked(details.checked)}
244
+ >
245
+ <Checkbox.HiddenInput />
246
+ <Checkbox.Control />
247
+ <Checkbox.Label>Check me</Checkbox.Label>
248
+ </Checkbox.Root>
249
+
250
+ // Checkbox with indeterminate state
251
+ <Checkbox.Root
252
+ checked={isAllSelected}
253
+ indeterminate={isIndeterminate}
254
+ onCheckedChange={(details) => handleSelectAll(details.checked)}
255
+ >
256
+ <Checkbox.HiddenInput />
257
+ <Checkbox.Control />
258
+ </Checkbox.Root>
259
+
260
+ // ❌ Alternative: Native input (if compound pattern doesn't work)
261
+ <Box
262
+ as="input"
263
+ type="checkbox"
264
+ checked={isChecked}
265
+ onChange={(e) => setIsChecked(e.target.checked)}
266
+ cursor="pointer"
267
+ />
268
+ ```
269
+
270
+ ## Debugging Tips
271
+
272
+ 1. **Invalid Element Type Errors**: Usually means wrong import or component name
273
+ 2. **Props Not Working**: Check if prop names changed from v2 to v3
274
+ 3. **Event Handlers Not Firing**: Check if handler expects an object instead of direct value
275
+ 4. **Styling Issues**: v3 uses different CSS architecture - check if style props changed
276
+ 5. **Missing Component**: If a component doesn't exist, use native HTML elements with `Box as="element"`
277
+
278
+ ## Resources
279
+
280
+ - [Chakra UI v3 Migration Guide](https://v3.chakra-ui.com/docs/migration)
281
+ - [Chakra UI v3 Components](https://v3.chakra-ui.com/docs/components)
282
+ - Use MCP Context7 tool with library ID `/llmstxt/chakra-ui-llms-full.txt` for latest docs
283
+ - **Chakra UI MCP Server Tools:**
284
+ - `mcp__chakra-ui__get_component_props` - Get component properties and configuration options
285
+ - `mcp__chakra-ui__get_component_example` - Get practical implementation examples with code snippets
286
+ - `mcp__chakra-ui__list_components` - List all available Chakra UI components
287
+ - `mcp__chakra-ui__get_theme` - Retrieve theme specification (colors, fonts, textStyles)
288
+ - `mcp__chakra-ui__customize_theme` - Setup custom theme tokens
289
+ - `mcp__chakra-ui__v2_to_v3_code_review` - Get migration guidance for specific v2→v3 scenarios
290
+ - `mcp__chakra-ui__installation` - Get setup instructions for different frameworks
291
+
292
+ ## Data Fetching Architecture
293
+
294
+ ### React Query Implementation
295
+ **IMPORTANT**: This project uses React Query (@tanstack/react-query) for all data fetching. Never use manual useEffect patterns for API calls.
296
+
297
+ ```jsx
298
+ // ✅ Correct: Use React Query
299
+ import { useQuery, useInfiniteQuery } from '@tanstack/react-query';
300
+
301
+ // Simple data fetching
302
+ const { data, isLoading, error } = useQuery({
303
+ queryKey: ['tmStats'],
304
+ queryFn: () => fetchApi('/api/tm/stats'),
305
+ });
306
+
307
+ // Infinite scrolling with pagination
308
+ const {
309
+ data: infiniteData,
310
+ isLoading,
311
+ fetchNextPage,
312
+ hasNextPage,
313
+ isFetchingNextPage,
314
+ } = useInfiniteQuery({
315
+ queryKey: ['tmSearch', sourceLang, targetLang, filters],
316
+ queryFn: async ({ pageParam = 1 }) => {
317
+ const queryParams = new URLSearchParams({
318
+ sourceLang,
319
+ targetLang,
320
+ page: pageParam.toString(),
321
+ limit: '100',
322
+ ...Object.fromEntries(Object.entries(filters).filter(([_, value]) => value.trim() !== ''))
323
+ });
324
+ return fetchApi(`/api/tm/search?${queryParams}`);
325
+ },
326
+ getNextPageParam: (lastPage, allPages) => {
327
+ const hasMore = lastPage.data.length === parseInt(lastPage.limit);
328
+ return hasMore ? allPages.length + 1 : undefined;
329
+ },
330
+ staleTime: 30000, // 30 seconds for search results
331
+ });
332
+
333
+ // Flatten infinite query data
334
+ const data = useMemo(() => {
335
+ return infiniteData?.pages.flatMap(page => page.data) || [];
336
+ }, [infiniteData]);
337
+ ```
338
+
339
+ ### Query Key Patterns
340
+ Use consistent query keys that include all relevant parameters:
341
+
342
+ ```jsx
343
+ // ✅ Good query keys - include all parameters that affect the data
344
+ queryKey: ['status']
345
+ queryKey: ['tmStats']
346
+ queryKey: ['info']
347
+ queryKey: ['tmSearch', sourceLang, targetLang, filters]
348
+
349
+ // ❌ Bad - missing parameters
350
+ queryKey: ['tmSearch'] // Missing language and filter context
351
+ ```
352
+
353
+ ### Configuration
354
+ React Query is configured globally in App.jsx:
355
+
356
+ ```jsx
357
+ const queryClient = new QueryClient({
358
+ defaultOptions: {
359
+ queries: {
360
+ staleTime: 5 * 60 * 1000, // 5 minutes - data considered fresh
361
+ gcTime: 10 * 60 * 1000, // 10 minutes - cache retention
362
+ },
363
+ },
364
+ });
365
+ ```
366
+
367
+ ### Automatic Benefits
368
+ React Query provides automatic:
369
+ - **Request deduplication** - Multiple identical requests become one
370
+ - **Background refetching** - Data stays fresh without user seeing loading
371
+ - **Caching** - Data persists across navigation and component unmounting
372
+ - **Error handling** - Consistent error states across components
373
+ - **Loading states** - Built-in loading indicators
374
+
375
+ ### Filter Integration
376
+ For filtered data (like search), include filters in query key:
377
+
378
+ ```jsx
379
+ // ✅ Filters in query key trigger automatic refetch
380
+ const queryKey = useMemo(() => [
381
+ 'tmSearch',
382
+ sourceLang,
383
+ targetLang,
384
+ filters
385
+ ], [sourceLang, targetLang, filters]);
386
+
387
+ // Debounced filter updates
388
+ const handleFilterChange = (column, value) => {
389
+ const newFilters = { ...filters, [column]: value };
390
+ setSelectedRows(new Set());
391
+
392
+ clearTimeout(window.tmFilterTimeout);
393
+ window.tmFilterTimeout = setTimeout(() => {
394
+ setFilters(newFilters); // This triggers React Query refetch automatically
395
+ }, 300);
396
+ };
397
+ ```
398
+
399
+ ### Legacy Patterns to Avoid
400
+ ❌ **Don't use these patterns anymore:**
401
+
402
+ ```jsx
403
+ // ❌ Manual useEffect data fetching
404
+ useEffect(() => {
405
+ const fetchData = async () => {
406
+ try {
407
+ const data = await fetchApi('/api/endpoint');
408
+ setData(data);
409
+ } catch (err) {
410
+ setError(err);
411
+ } finally {
412
+ setLoading(false);
413
+ }
414
+ };
415
+ fetchData();
416
+ }, []);
417
+
418
+ // ❌ Manual loading/error state management
419
+ const [data, setData] = useState(null);
420
+ const [loading, setLoading] = useState(true);
421
+ const [error, setError] = useState(null);
422
+
423
+ // ❌ Location-based API guards
424
+ if (location.pathname !== '/target-path') return;
425
+
426
+ // ❌ Manual AbortController management
427
+ const abortControllerRef = useRef(null);
428
+
429
+ // ❌ Manual fetch guards
430
+ const fetchedRef = useRef(false);
431
+ if (fetchedRef.current) return;
432
+ ```
433
+
434
+ ## Navigation Architecture
435
+
436
+ ### React Router Implementation
437
+ **IMPORTANT**: This project uses pure React Router navigation. No tab systems or conditional rendering based on location.
438
+
439
+ ```jsx
440
+ // ✅ Correct: Pure React Router structure
441
+ function MainLayout() {
442
+ return (
443
+ <Box minH="100vh" bg="gray.50">
444
+ {/* Header with navigation buttons */}
445
+ <Box bg="white" borderBottom="1px" borderColor="gray.200">
446
+ <Container maxWidth="6xl" py={3}>
447
+ <Flex align="center" justify="space-between">
448
+ {/* Navigation Links */}
449
+ <Flex align="center" gap={4}>
450
+ <Box
451
+ cursor="pointer"
452
+ px={3}
453
+ py={1}
454
+ borderRadius="md"
455
+ bg={isActiveRoute('/') ? "blue.100" : "transparent"}
456
+ _hover={{ bg: isActiveRoute('/') ? "blue.100" : "gray.100" }}
457
+ onClick={() => navigate('/')}
458
+ >
459
+ <Text fontSize="sm" fontWeight="medium">Home</Text>
460
+ </Box>
461
+ {/* More nav items... */}
462
+ </Flex>
463
+ </Flex>
464
+ </Container>
465
+ </Box>
466
+
467
+ {/* Route-based Content */}
468
+ <Suspense fallback={<Spinner />}>
469
+ <Routes>
470
+ <Route path="/" element={<Welcome />} />
471
+ <Route path="/status" element={<Status />} />
472
+ <Route path="/sources" element={<Sources />} />
473
+ <Route path="/tm" element={<TM />} />
474
+ <Route path="/tm/:sourceLang/:targetLang" element={<TMDetail />} />
475
+ <Route path="/cart" element={<Cart />} />
476
+ </Routes>
477
+ </Suspense>
478
+ </Box>
479
+ );
480
+ }
481
+
482
+ // App.jsx routing setup
483
+ function App() {
484
+ return (
485
+ <QueryClientProvider client={queryClient}>
486
+ <Router>
487
+ <Routes>
488
+ <Route path="/*" element={<MainLayout />} />
489
+ <Route path="*" element={<NotFoundPage />} />
490
+ </Routes>
491
+ </Router>
492
+ </QueryClientProvider>
493
+ );
494
+ }
495
+ ```
496
+
497
+ ### Active Route Detection
498
+ Simple helper function for navigation state:
499
+
500
+ ```jsx
501
+ // ✅ Clean active route detection
502
+ const isActiveRoute = (path) => {
503
+ if (path === '/') return location.pathname === '/';
504
+ return location.pathname.startsWith(path);
505
+ };
506
+ ```
507
+
508
+ ### Navigation Benefits
509
+ This architecture provides:
510
+ - **Route-based mounting** - Only current route component mounts
511
+ - **Proper lazy loading** - Components load only when visited
512
+ - **Clean URLs** - Standard web navigation patterns
513
+ - **Browser back/forward** - Natural browser behavior
514
+ - **SEO friendly** - Proper route-based structure
515
+
516
+ ### Legacy Patterns to Avoid
517
+ ❌ **Don't use these patterns anymore:**
518
+
519
+ ```jsx
520
+ // ❌ Tab system with conditional rendering
521
+ <Tabs.Root value={activeTab} onValueChange={handleTabChange}>
522
+ <Tabs.Content value="tm">
523
+ {location.pathname.startsWith('/tm/') ? <TMDetail /> : <TM />}
524
+ </Tabs.Content>
525
+ </Tabs.Root>
526
+
527
+ // ❌ Complex tab change handlers
528
+ const handleTabChange = (details) => {
529
+ const value = typeof details === 'object' ? details.value : details;
530
+ setActiveTab(value);
531
+ if (value === 'home') {
532
+ navigate('/');
533
+ } else if (value === 'tm') {
534
+ navigate('/tm');
535
+ } else {
536
+ navigate(`/${value}`);
537
+ }
538
+ };
539
+
540
+ // ❌ Location-based conditional rendering
541
+ if (location.pathname === '/cart') {
542
+ return <CartSpecialLayout />;
543
+ }
544
+
545
+ // ❌ Manual active tab state management
546
+ const [activeTab, setActiveTab] = useState(getTabFromPath(location.pathname));
547
+ ```
548
+
549
+ ## Session Storage for Cart Functionality
550
+ Store cart data grouped by keys for persistence across navigation:
551
+
552
+ ```jsx
553
+ // ✅ Session storage cart pattern
554
+ const getCart = () => {
555
+ const cartData = sessionStorage.getItem('cartKey');
556
+ return cartData ? JSON.parse(cartData) : {};
557
+ };
558
+
559
+ const saveCart = (cart) => {
560
+ sessionStorage.setItem('cartKey', JSON.stringify(cart));
561
+ };
562
+
563
+ const addToCart = (items, groupKey) => {
564
+ const cart = getCart();
565
+ if (!cart[groupKey]) cart[groupKey] = [];
566
+ cart[groupKey].push(...items);
567
+ saveCart(cart);
568
+ };
569
+ ```
570
+
571
+ ## Architecture Summary
572
+
573
+ This application now follows modern React best practices:
574
+
575
+ ### Current Stack
576
+ - **React 19** with functional components and hooks
577
+ - **Chakra UI v3** with compound component patterns
578
+ - **React Router v6** for navigation (no tab system)
579
+ - **React Query** for all data fetching and caching
580
+ - **TypeScript** for type safety (where applicable)
581
+
582
+ ### Key Architectural Decisions
583
+ 1. **Pure React Router Navigation** - No mixing with tab systems
584
+ 2. **React Query for All Data** - No manual useEffect data fetching
585
+ 3. **Route-based Component Mounting** - Components only mount when visited
586
+ 4. **Session Storage for Cart** - Persistent cart across navigation
587
+ 5. **Compound Components** - Chakra UI v3 patterns throughout
588
+
589
+ ### Performance Characteristics
590
+ - **Zero duplicate API calls** - React Query deduplication
591
+ - **Automatic caching** - Data persists across navigation
592
+ - **Route-based code splitting** - Only load what's needed
593
+ - **Background refetching** - Data stays fresh without user awareness
594
+ - **Memory efficient** - Unused components unmount properly
595
+
596
+ ### Development Guidelines
597
+ 1. **Always use React Query** for API calls
598
+ 2. **Always use Chakra UI v3 patterns** (compound components)
599
+ 3. **Never use manual useEffect** for data fetching
600
+ 4. **Never mix tab systems** with React Router
601
+ 5. **Always include relevant parameters** in query keys
602
+ 6. **Use debouncing** for filter inputs (300ms)
603
+ 7. **Handle both old and new data formats** for backward compatibility
604
+
605
+ ## API Routes Documentation
606
+
607
+ The L10n Monster server provides RESTful API endpoints for managing translation projects. All routes are prefixed with `/api`.
608
+
609
+ ### Route Structure
610
+
611
+ Routes are organized by functionality in separate files under `/server/routes/`:
612
+
613
+ ```
614
+ /server/routes/
615
+ ├── info.js # System information endpoints
616
+ ├── status.js # Project status endpoints
617
+ ├── sources.js # Source content (channels, projects, resources)
618
+ ├── tm.js # Translation memory endpoints
619
+ ├── providers.js # Translation provider endpoints
620
+ └── dispatcher.js # Job creation and management
621
+ ```
622
+
623
+ ### Available Endpoints
624
+
625
+ #### System Information
626
+ - **GET `/api/info`** - Returns system information including:
627
+ - `version`: Server version
628
+ - `providers`: Array of available provider IDs
629
+ - `config`: System configuration
630
+
631
+ #### Project Status
632
+ - **GET `/api/status`** - Returns project status and statistics
633
+
634
+ #### Source Content
635
+ - **GET `/api/channel/:channelId`** - Returns channel metadata and active content statistics
636
+ - **Response:** `{ ts: number, store: string, projects: [...] }`
637
+ - **GET `/api/channel/:channelId/:prj`** - Returns project table of contents
638
+ - **Query Parameters:** `offset`, `limit` (pagination)
639
+ - **Response:** Array of resource handles for the project
640
+ - **GET `/api/resource/:channelId?rid=<resourceId>`** - Returns resource details with segments
641
+ - **Query Parameters:** `rid` (required) - Resource ID
642
+ - **Response:** Resource handle with segments array
643
+
644
+ #### Translation Memory
645
+ - **GET `/api/tm/stats`** - Returns available language pairs (sorted array)
646
+ - **GET `/api/tm/stats/:sourceLang/:targetLang`** - Returns TM statistics for specific language pair
647
+ - **Response:** Statistics object with counts, quality distribution, etc.
648
+ - **GET `/api/tm/lowCardinalityColumns/:sourceLang/:targetLang`** - Returns available filter options
649
+ - **Response:** `{ channel: [...], translationProvider: [...], ... }`
650
+ - **GET `/api/tm/search`** - Search translation memory entries with advanced filtering
651
+ - **Query Parameters:**
652
+ - `sourceLang`, `targetLang` (required)
653
+ - `page`, `limit` (pagination, default: page=1, limit=100)
654
+ - `guid`, `nid`, `jobGuid`, `rid`, `sid`, `channel` (exact or partial match)
655
+ - `nsrc`, `ntgt`, `notes`, `tconf` (text search - supports quoted exact match)
656
+ - `q` (quality score filtering)
657
+ - `translationProvider` (provider filtering)
658
+ - `onlyTNotes` (boolean: "1" to show only TUs with translator notes)
659
+ - `minTS`, `maxTS` (timestamp range filtering - milliseconds since epoch)
660
+ - **Text Search:** Use quotes for exact match (e.g., `nsrc="hello world"`), otherwise partial match
661
+ - **Date Range Filtering:** Filter by timestamp range using minTS and maxTS (milliseconds). UI displays clickable date range (M/D format without leading zeros, uses current year). Click to open popover with From/To date pickers and Apply/Clear buttons.
662
+ - **Response:** `{ data: [...], page: number, limit: number }`
663
+ - **GET `/api/tm/job/:jobGuid`** - Returns job details by GUID
664
+ - **Response:** Job object with metadata, TUs, timestamps, etc.
665
+
666
+ #### Translation Providers
667
+ - **GET `/api/providers`** - Returns detailed provider information (slower)
668
+ - Use `/api/info` for just provider IDs (faster)
669
+
670
+ #### Job Management
671
+ - **POST `/api/dispatcher/createJobs`** - Create translation jobs
672
+ - **Body:**
673
+ ```json
674
+ {
675
+ "sourceLang": "en",
676
+ "targetLang": "es",
677
+ "tus": [...], // Array of translation units
678
+ "providerList": [...] // Array of provider IDs
679
+ }
680
+ ```
681
+ - **Response:** Array of created job objects with properties like `jobGuid`, `tus`, `estimatedCost`, `translationProvider`
682
+ - **Options:** Automatically applies `skipQualityCheck: true` and `skipGroupCheck: true`
683
+
684
+ - **POST `/api/dispatcher/startJobs`** - Start created jobs
685
+ - **Body:**
686
+ ```json
687
+ {
688
+ "jobs": [...], // Array of job objects from createJobs
689
+ "instructions": "..." // Optional job-specific instructions
690
+ }
691
+ ```
692
+ - **Response:** Array of job status objects: `{ sourceLang, targetLang, jobGuid, translationProvider, status }`
693
+
694
+ ### Route Implementation Pattern
695
+
696
+ Each route file exports a setup function:
697
+
698
+ ```javascript
699
+ // routes/example.js
700
+ import { logInfo, logVerbose } from '@l10nmonster/core';
701
+
702
+ export function setupExampleRoutes(router, mm) {
703
+ router.get('/example', async (req, res) => {
704
+ logInfo`/example`;
705
+ try {
706
+ // Use MonsterManager (mm) to access core functionality
707
+ const result = await mm.someMethod();
708
+ logVerbose`Processed ${result.length} items`;
709
+ res.json(result);
710
+ } catch (error) {
711
+ logInfo`Error: ${error.message}`;
712
+ res.status(500).json({ error: error.message });
713
+ }
714
+ });
715
+ }
716
+ ```
717
+
718
+ ### Integration in main server (index.js):
719
+
720
+ ```javascript
721
+ import { setupExampleRoutes } from './routes/example.js';
722
+
723
+ // In API setup
724
+ setupExampleRoutes(apiRouter, mm);
725
+ ```
726
+
727
+ ### Error Handling Standards
728
+
729
+ - **400**: Bad Request (missing/invalid parameters)
730
+ - **500**: Internal Server Error (caught exceptions)
731
+ - Always use try/catch blocks for async operations
732
+ - Log requests with `logInfo` and details with `logVerbose`
733
+ - Return JSON error objects: `{ error: "message", details?: "..." }`
734
+
735
+ ### MonsterManager Access
736
+
737
+ Routes receive the MonsterManager instance (`mm`) which provides access to:
738
+ - `mm.tmm` - Translation Memory Manager
739
+ - `mm.dispatcher` - Job Dispatcher (`createJobs`, `startJobs`)
740
+ - `mm.rm` - Resource Manager
741
+ - `mm.ops` - Operations Manager
742
+ - `mm.currencyFormatter` - Currency formatting utility
743
+
744
+ ### Job Workflow
745
+
746
+ The job management system follows a two-step process:
747
+
748
+ 1. **Create Jobs** (`/api/dispatcher/createJobs`):
749
+ - Takes TUs and provider list
750
+ - Returns job objects with estimated costs and accepted TUs
751
+ - Jobs are created but not yet started
752
+ - Providers may accept some, all, or none of the submitted TUs
753
+
754
+ 2. **Start Jobs** (`/api/dispatcher/startJobs`):
755
+ - Takes job objects from step 1 and optional instructions
756
+ - Actually initiates the translation process
757
+ - Returns status information for tracking job progress
758
+
759
+ ## Production Build Configuration
760
+
761
+ The Vite build is configured with a hybrid chunking strategy for optimal performance:
762
+
763
+ ```javascript
764
+ // vite.config.js
765
+ build: {
766
+ rollupOptions: {
767
+ output: {
768
+ manualChunks: (id) => {
769
+ // All node_modules in one vendor bundle
770
+ if (id.includes('node_modules')) {
771
+ return 'vendor';
772
+ }
773
+ // Bundle utility files with index instead of creating tiny chunks
774
+ if (id.includes('/utils/') || id.includes('/src/components/')) {
775
+ return 'index';
776
+ }
777
+ // Pages remain separate for lazy loading
778
+ }
779
+ }
780
+ }
781
+ }
782
+ ```
783
+
784
+ **Build Output:**
785
+ - **1 vendor bundle** (~725 KB / 210 KB gzipped) - All dependencies (React, Chakra UI, React Router, etc.)
786
+ - **1 index bundle** (~28 KB) - App core, utilities, and shared components
787
+ - **~11 page chunks** (1-17 KB each) - Individual pages for code splitting
788
+
789
+ **Benefits:**
790
+ - ✅ Vendor bundle rarely changes (excellent for caching)
791
+ - ✅ Pages lazy load independently for faster initial render
792
+ - ✅ Shared components bundled efficiently
793
+ - ✅ Fewer files than default Vite chunking (~16 vs ~40)
794
+
795
+ **Commands:**
796
+ ```bash
797
+ npm run build # Build production bundle
798
+ npm run preview # Preview production build locally
799
+ ```
800
+
801
+ ## Remember
802
+
803
+ **When in doubt:**
804
+ 1. **Check React Query docs** for data fetching patterns
805
+ 2. **Check Chakra UI v3 docs** for component usage
806
+ 3. **Use pure React Router** for navigation
807
+ 4. **Never guess based on v2 Chakra UI knowledge**
808
+ 5. **Use Chakra UI MCP tools** for accurate component examples and props