Tristan Yu commited on
Commit
d8b2bc7
·
1 Parent(s): f129e6c

Align top-level backend routes to support image-only content (week>=3), remove legacy content-required validations

Browse files
apg5611-2b2170947124.json ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "type": "service_account",
3
+ "project_id": "apg5611",
4
+ "private_key_id": "2b2170947124eb920335bdcb80fbecc371e7cdec",
5
+ "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCqdwDBYHh2cBbe\nX8L33mR5/ng1RkULZ8YRh5X30POcjQE9KffnpXe1R7inOCtZPlkCW5AuawySeciX\nU9X/Px1Nkn7eNdAy2Im+RTJZw+9VqPufgHo7cp/ACBLUutrUmyTTFJJ3in07ciMz\nB07j1FqdlFD0d6yOsCNmJR2MaiYCaH/VHDJ1YOfCUrqfkQEz+92C6of9PfHsUVoV\nSlyb/4TC5FGtjVQ6fc2cs4wjXAbPLS8HVd8DjDQRmYYAAhL29BZCLoIgZQZh62lJ\nL6DMJH6rAn3OHpR/HX8JfiItt9iMuVN7v5ZrXc1fwizyt8dV3uxhLl5nAUsHrNAH\nrSu/LHGBAgMBAAECggEAFO0+EP9UWonhLNSJvV82a4mTXUrun/tR5AYGoaErvvzK\neaVE01HLc9xFrXWmULLoP3NZLHGk3fiIaRd4LnkjkknZPFNT52IQ6uRYzY9Ruuzf\nUxH1L4YfWXZERYxY3//Z0fX1EyrTu/ADpTiQ9UQ1CaJTG+fzLaMu/VOyIHJJj0Ls\neQbeBDbDCFbqbJTEE+144SNb8TI9Dg29npDWgiDptO7hzGZVsgVYmO6LTQNkv/mT\nhoxbDJMD1jDcSguEeNzxHL6jcTOfgcSbc94VFZv8YoGsvKUgU3bbW+YYKKDwq4ui\nJsOHVnN7zXmL90Fbb4kvi++eQ+h7VjZ0P22GmK3jLwKBgQDhpT8dn1DQs2jxexxs\ns4VFgUAVIPoXaiWeyceSzK3TWM5bKPxrUAK0Kwl23oA2TkBJw+NmP0VUSaDG2PUA\nGu/P6pVE1Kvm+HQUDEqAIvbRih+Qc8Ey2WvoCKOnL19H8UhuTpYYiQhL46f+4yVU\n7ybjxHzfbez+2BjghUaQXlx0OwKBgQDBZXOyBQTVjjkJ3MboIci9oQYZ0Cb0MWuu\nr1QbgZqIMp9VkXblDfUvbA9KwUPOfL8n21K6qoiA9DGfs9MZTn0HXcuaXx1guVgc\niYOepGdAxN0pAOTeJvf2mHDW5U0dzfnSeFg/Hylplwq3v4FOZsNDXK8ijcMCErmD\n3o5Q+pABcwKBgGW8B1Tnu5wdB7e4dfxchD3uJGJiV9+ur/+DokOBnO2pIIDhXc1U\naaOps76wWsYhScL4WtKCvFNgTujYcL33ZyAq99nsuQSOCGfxkrVrDjUzPT/3sKeo\nd4pKOt7p8N+S3cE+FVZCkkcFAX1eFDrTCImw0Uw7V5se+zhfXo1AvhZ5AoGBAIgs\nqVRWGFtOs3bwT9Yvw6tScHtIh8U17z6nEa/0iB0SmAKmVHNgap4jlnjqsJX6XJbx\n5cSuQ0OZrj9nXWLaU0sWjkB6bLMojpQ5d8YCuPlwJNG0YXLTOZIXMI0AFFbCLAA4\n4NpBVOWWhuNvIttTnHYYEvFfpEoXaS0jTk5Sv4dlAoGBAJCwW+dKPIYcFfMNU9ec\nMYqJuhe7EugYA6JBHXsAFaiyNj+MJBev4XuWShxyn8ApBxKcS1NdXF3pUjQEXSi/\ni8WtYUIFkh7J915/OWeWMqZE+hpHD3nLfb3dMoYnbrAJGpHeG0Thxa4RO2sMmfcz\n2jrpHf83SdfsMriOfhCvF8Ux\n-----END PRIVATE KEY-----\n",
6
+ "client_email": "apg5603-group-discussion-docs@apg5611.iam.gserviceaccount.com",
7
+ "client_id": "111100204656070048644",
8
+ "auth_uri": "https://accounts.google.com/o/oauth2/auth",
9
+ "token_uri": "https://oauth2.googleapis.com/token",
10
+ "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
11
+ "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/apg5603-group-discussion-docs%40apg5611.iam.gserviceaccount.com",
12
+ "universe_domain": "googleapis.com"
13
+ }
deploy/backend CHANGED
@@ -1 +1 @@
1
- Subproject commit 65ac4dd81ce69291c32135b07373be229302aaf6
 
1
+ Subproject commit 92347b29ca49a6324b7a4d4eba3b00633dd638a6
deploy/frontend CHANGED
@@ -1 +1 @@
1
- Subproject commit f7efd65221c84b86bf01d5ad4ba8e8463e3efeac
 
1
+ Subproject commit f129e6c37924dddd9d0db79052bbcb30ff5b3484
public/manifest.json CHANGED
@@ -2,11 +2,19 @@
2
  "short_name": "Transcreation Sandbox",
3
  "name": "Transcreation Sandbox - Translation Practice Tool",
4
  "icons": [
 
5
  { "src": "/favicon-192.png", "sizes": "192x192", "type": "image/png" },
6
  { "src": "/favicon-512.png", "sizes": "512x512", "type": "image/png" }
 
 
 
 
 
 
 
7
  ],
8
  "start_url": ".",
9
  "display": "standalone",
10
  "theme_color": "#000000",
11
  "background_color": "#ffffff"
12
- }
 
2
  "short_name": "Transcreation Sandbox",
3
  "name": "Transcreation Sandbox - Translation Practice Tool",
4
  "icons": [
5
+ <<<<<<< HEAD
6
  { "src": "/favicon-192.png", "sizes": "192x192", "type": "image/png" },
7
  { "src": "/favicon-512.png", "sizes": "512x512", "type": "image/png" }
8
+ =======
9
+ {
10
+ "src": "data:image/png;base64,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",
11
+ "sizes": "192x192",
12
+ "type": "image/png"
13
+ }
14
+ >>>>>>> d5a71c8 (Align top-level backend routes to support image-only content (week>=3), remove legacy content-required validations)
15
  ],
16
  "start_url": ".",
17
  "display": "standalone",
18
  "theme_color": "#000000",
19
  "background_color": "#ffffff"
20
+ }
server/routes/auth.js CHANGED
@@ -436,7 +436,6 @@ router.get('/admin/weekly-practice', authenticateToken, async (req, res) => {
436
  });
437
 
438
 
439
-
440
  // Create weekly practice task (admin only)
441
  router.post('/admin/weekly-practice', authenticateToken, requireAdmin, async (req, res) => {
442
  try {
@@ -447,19 +446,19 @@ router.post('/admin/weekly-practice', authenticateToken, requireAdmin, async (re
447
  if (!weekNumber) {
448
  return res.status(400).json({ error: 'Week number is required' });
449
  }
450
-
451
  // For week 3+, allow either content or imageUrl
452
  if (parseInt(weekNumber) >= 3) {
453
- if (!content && !imageUrl) {
454
  return res.status(400).json({ error: 'Either content or imageUrl is required' });
455
  }
456
  } else {
457
  // For weeks 1-2, require content
458
- if (!content) {
459
  return res.status(400).json({ error: 'Content is required' });
460
  }
461
  }
462
-
463
  const newPractice = new SourceText({
464
  content: content || (imageUrl ? 'Image-based practice' : ''),
465
  weekNumber: parseInt(weekNumber),
@@ -474,9 +473,9 @@ router.post('/admin/weekly-practice', authenticateToken, requireAdmin, async (re
474
  ...(imageUrl && (!content || content.trim() === '') && { imageAlignment: imageAlignment || 'center' }),
475
  translationBrief
476
  });
477
-
478
  const savedPractice = await newPractice.save();
479
-
480
  res.status(201).json({
481
  success: true,
482
  message: 'Weekly practice created successfully',
@@ -563,19 +562,19 @@ router.post('/admin/tutorial-tasks', authenticateToken, requireAdmin, async (req
563
  if (!weekNumber) {
564
  return res.status(400).json({ error: 'Week number is required' });
565
  }
566
-
567
  // For week 3+, allow either content or imageUrl
568
  if (parseInt(weekNumber) >= 3) {
569
- if (!content && !imageUrl) {
570
  return res.status(400).json({ error: 'Either content or imageUrl is required' });
571
  }
572
  } else {
573
  // For weeks 1-2, require content
574
- if (!content) {
575
  return res.status(400).json({ error: 'Content is required' });
576
  }
577
  }
578
-
579
  const newTask = new SourceText({
580
  content: content || (imageUrl ? 'Image-based task' : ''),
581
  weekNumber: parseInt(weekNumber),
@@ -590,9 +589,9 @@ router.post('/admin/tutorial-tasks', authenticateToken, requireAdmin, async (req
590
  ...(imageUrl && (!content || content.trim() === '') && { imageAlignment: imageAlignment || 'center' }),
591
  translationBrief
592
  });
593
-
594
  const savedTask = await newTask.save();
595
-
596
  res.status(201).json({
597
  success: true,
598
  message: 'Tutorial task created successfully',
 
436
  });
437
 
438
 
 
439
  // Create weekly practice task (admin only)
440
  router.post('/admin/weekly-practice', authenticateToken, requireAdmin, async (req, res) => {
441
  try {
 
446
  if (!weekNumber) {
447
  return res.status(400).json({ error: 'Week number is required' });
448
  }
449
+
450
  // For week 3+, allow either content or imageUrl
451
  if (parseInt(weekNumber) >= 3) {
452
+ if ((!content || content.trim() === '') && !imageUrl) {
453
  return res.status(400).json({ error: 'Either content or imageUrl is required' });
454
  }
455
  } else {
456
  // For weeks 1-2, require content
457
+ if (!content || content.trim() === '') {
458
  return res.status(400).json({ error: 'Content is required' });
459
  }
460
  }
461
+
462
  const newPractice = new SourceText({
463
  content: content || (imageUrl ? 'Image-based practice' : ''),
464
  weekNumber: parseInt(weekNumber),
 
473
  ...(imageUrl && (!content || content.trim() === '') && { imageAlignment: imageAlignment || 'center' }),
474
  translationBrief
475
  });
476
+
477
  const savedPractice = await newPractice.save();
478
+
479
  res.status(201).json({
480
  success: true,
481
  message: 'Weekly practice created successfully',
 
562
  if (!weekNumber) {
563
  return res.status(400).json({ error: 'Week number is required' });
564
  }
565
+
566
  // For week 3+, allow either content or imageUrl
567
  if (parseInt(weekNumber) >= 3) {
568
+ if ((!content || content.trim() === '') && !imageUrl) {
569
  return res.status(400).json({ error: 'Either content or imageUrl is required' });
570
  }
571
  } else {
572
  // For weeks 1-2, require content
573
+ if (!content || content.trim() === '') {
574
  return res.status(400).json({ error: 'Content is required' });
575
  }
576
  }
577
+
578
  const newTask = new SourceText({
579
  content: content || (imageUrl ? 'Image-based task' : ''),
580
  weekNumber: parseInt(weekNumber),
 
589
  ...(imageUrl && (!content || content.trim() === '') && { imageAlignment: imageAlignment || 'center' }),
590
  translationBrief
591
  });
592
+
593
  const savedTask = await newTask.save();
594
+
595
  res.status(201).json({
596
  success: true,
597
  message: 'Tutorial task created successfully',