Skip to content

Commit 8455786

Browse files
authored
[Data Cleanup] Redesign goal options/history page (#3836)
1 parent f5b63db commit 8455786

File tree

17 files changed

+508
-555
lines changed

17 files changed

+508
-555
lines changed

Backend.Tests/Controllers/UserEditControllerTests.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,9 @@ public async Task TestAddGoalToUserEdit()
150150

151151
await _userEditController.UpdateUserEditGoal(ProjId, userEdit.Id, newEdit);
152152

153-
var allUserEdits = await _userEditRepo.GetAllUserEdits(ProjId);
154-
Assert.That(allUserEdits, Does.Contain(updatedUserEdit).UsingPropertiesComparer());
153+
var repoUserEdit = await _userEditRepo.GetUserEdit(ProjId, userEdit.Id);
154+
newEdit.Modified = repoUserEdit!.Edits.FirstOrDefault(e => e.Guid == newEdit.Guid)!.Modified;
155+
Assert.That(repoUserEdit, Is.EqualTo(updatedUserEdit).UsingPropertiesComparer());
155156
}
156157

157158
[Test]

Backend/Models/UserEdit.cs

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,15 @@ public class UserEdit
1313
[Required]
1414
[BsonId]
1515
[BsonRepresentation(BsonType.ObjectId)]
16-
public string Id { get; set; }
16+
public string Id { get; set; } = "";
1717

1818
[Required]
1919
[BsonElement("edits")]
20-
public List<Edit> Edits { get; set; }
20+
public List<Edit> Edits { get; set; } = [];
2121

2222
[Required]
2323
[BsonElement("projectId")]
24-
public string ProjectId { get; set; }
25-
26-
public UserEdit()
27-
{
28-
Id = "";
29-
ProjectId = "";
30-
Edits = new();
31-
}
24+
public string ProjectId { get; set; } = "";
3225

3326
/// <summary> Create a deep copy. </summary>
3427
public UserEdit Clone()
@@ -66,7 +59,7 @@ public class Edit
6659
[BsonElement("guid")]
6760
[BsonGuidRepresentation(GuidRepresentation.CSharpLegacy)]
6861
#pragma warning disable CA1720
69-
public Guid Guid { get; set; }
62+
public Guid Guid { get; set; } = Guid.NewGuid();
7063
#pragma warning restore CA1720
7164

7265
/// <summary> Integer representation of enum GoalType in src/types/goals.ts </summary>
@@ -76,24 +69,20 @@ public class Edit
7669

7770
[Required]
7871
[BsonElement("stepData")]
79-
public List<string> StepData { get; set; }
72+
public List<string> StepData { get; set; } = [];
8073

8174
[Required]
8275
[BsonElement("changes")]
83-
public string Changes { get; set; }
76+
public string Changes { get; set; } = "{}";
8477

85-
public Edit()
86-
{
87-
Guid = Guid.NewGuid();
88-
GoalType = 0;
89-
StepData = new();
90-
Changes = "{}";
91-
}
78+
[BsonElement("modified")]
79+
public DateTime? Modified { get; set; }
9280

9381
/// <summary> Create a deep copy. </summary>
9482
public Edit Clone()
9583
{
9684
var clone = (Edit)MemberwiseClone();
85+
clone.Modified = Modified is null ? null : new DateTime(Modified.Value.Ticks);
9786
clone.StepData = StepData.Select(sd => sd).ToList();
9887
return clone;
9988
}

Backend/Services/UserEditService.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ public UserEditService(IUserEditRepository userEditRepo)
3333
return new Tuple<bool, Guid?>(false, null);
3434
}
3535

36+
edit.Modified = DateTime.UtcNow;
37+
3638
// Update existing Edit if guid exists, otherwise add new one at end of List.
3739
var editIndex = userEdit.Edits.FindLastIndex(e => e.Guid == edit.Guid);
3840
if (editIndex > -1)
@@ -64,6 +66,7 @@ public async Task<bool> AddStepToGoal(string projectId, string userEditId, Guid
6466
{
6567
return false;
6668
}
69+
edit.Modified = DateTime.UtcNow;
6770
edit.StepData.Add(stepString);
6871
return await _userEditRepo.Replace(projectId, userEditId, userEdit);
6972
}
@@ -87,6 +90,7 @@ public async Task<bool> UpdateStepInGoal(
8790
{
8891
return false;
8992
}
93+
edit.Modified = DateTime.UtcNow;
9094
edit.StepData[stepIndex] = stepString;
9195
return await _userEditRepo.Replace(projectId, userEditId, userEdit);
9296
}

public/locales/en/translation.json

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -318,25 +318,32 @@
318318
"progressMerge": "Merge Set {{ val1 }} of {{ val2 }}"
319319
},
320320
"createStrWordInv": {
321-
"title": "Create Structural Word Inventory"
321+
"title": "Create Structural Word Inventory",
322+
"description": "NOT IMPLEMENTED"
322323
},
323324
"handleFlags": {
324-
"title": "Handle Flags"
325+
"title": "Handle Flags",
326+
"description": "NOT IMPLEMENTED"
325327
},
326328
"reviewDeferredDups": {
327-
"title": "Review Deferred Duplicates"
329+
"title": "Review Deferred Duplicates",
330+
"description": "Revisit potential duplicates that were deferred during Merge Duplicates."
328331
},
329332
"spellCheckGloss": {
330-
"title": "Spell Check Gloss"
333+
"title": "Spell Check Gloss",
334+
"description": "NOT IMPLEMENTED"
331335
},
332336
"validateChars": {
333-
"title": "Validate Characters"
337+
"title": "Validate Characters",
338+
"description": "NOT IMPLEMENTED"
334339
},
335340
"validateStrWords": {
336-
"title": "Validate Structural Words"
341+
"title": "Validate Structural Words",
342+
"description": "NOT IMPLEMENTED"
337343
},
338344
"reviewEntries": {
339345
"title": "Review Entries",
346+
"description": "View, sort, filter, and edit entries in the project.",
340347
"allEntriesPerPageOption": "All",
341348
"editSense": "Edit sense",
342349
"discardChanges": "Discard changes?",
@@ -375,7 +382,8 @@
375382
}
376383
},
377384
"charInventory": {
378-
"title": "Create Character Inventory",
385+
"title": "Check Orthography",
386+
"description": "Review characters used in the spelling of vernacular words and check for typos.",
379387
"characters": "characters",
380388
"examples": "Examples",
381389
"occurrences": "occurrences",
@@ -418,6 +426,7 @@
418426
},
419427
"mergeDups": {
420428
"title": "Merge Duplicates",
429+
"description": "Review potential duplicates identified by The Combine and merge or eliminate true duplicates.",
421430
"helpText": {
422431
"dragCard": "Drag a card here to merge",
423432
"saveAndContinue": "Save changes and load a new set of words",

src/api/models/edit.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,10 @@ export interface Edit {
4242
* @memberof Edit
4343
*/
4444
changes: string;
45+
/**
46+
*
47+
* @type {string}
48+
* @memberof Edit
49+
*/
50+
modified?: string | null;
4551
}

src/api/models/user-edit.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,10 @@ export interface UserEdit {
3838
* @memberof UserEdit
3939
*/
4040
projectId: string;
41+
/**
42+
*
43+
* @type {string}
44+
* @memberof UserEdit
45+
*/
46+
modified?: string | null;
4147
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { Box, Button, Stack, Typography } from "@mui/material";
2+
import { Fragment, ReactElement } from "react";
3+
import { useTranslation } from "react-i18next";
4+
5+
import { CharInvChangesGoalList } from "goals/CharacterInventory/CharInvCompleted";
6+
import { CharInvChanges } from "goals/CharacterInventory/CharacterInventoryTypes";
7+
import { MergesCount } from "goals/MergeDuplicates/MergeDupsCompleted";
8+
import { MergesCompleted } from "goals/MergeDuplicates/MergeDupsTypes";
9+
import { EditsCount } from "goals/ReviewEntries/ReviewEntriesCompleted";
10+
import { EntriesEdited } from "goals/ReviewEntries/ReviewEntriesTypes";
11+
import { Goal, GoalName } from "types/goals";
12+
import { goalNameToIcon } from "utilities/goalUtilities";
13+
14+
interface GoalHistoryButtonProps {
15+
goal: Goal;
16+
onClick: () => void;
17+
}
18+
19+
export default function GoalHistoryButton(
20+
props: GoalHistoryButtonProps
21+
): ReactElement {
22+
const { goal, onClick } = props;
23+
const { i18n, t } = useTranslation();
24+
25+
const modifiedFormatted = goal.modified
26+
? new Date(goal.modified).toLocaleString(i18n.resolvedLanguage, {
27+
day: "numeric",
28+
hour: "numeric",
29+
hour12: true,
30+
minute: "2-digit",
31+
month: "short",
32+
weekday: "short",
33+
year: "numeric",
34+
})
35+
: null;
36+
37+
return (
38+
<Button
39+
onClick={onClick}
40+
sx={{ alignItems: "flex-start", minWidth: "225px", textAlign: "start" }}
41+
variant="outlined"
42+
>
43+
<Stack spacing={1}>
44+
{/* Goal name */}
45+
<Typography variant="h6">
46+
<Box
47+
component="span" // to be inline with the title
48+
sx={{ marginInlineEnd: 1, verticalAlign: "middle" }}
49+
>
50+
{goalNameToIcon(goal.name)}
51+
</Box>
52+
{t(goal.name + ".title")}
53+
</Typography>
54+
55+
{/* Change datetime */}
56+
{!!goal.modified && (
57+
<Typography variant="caption">{modifiedFormatted}</Typography>
58+
)}
59+
60+
{/* Change summary */}
61+
{!!goal.changes && <div>{getCompletedGoalInfo(goal)}</div>}
62+
</Stack>
63+
</Button>
64+
);
65+
}
66+
67+
function getCompletedGoalInfo(goal: Goal): ReactElement {
68+
const { changes, name } = goal;
69+
switch (name) {
70+
case GoalName.CreateCharInv:
71+
return CharInvChangesGoalList(changes as CharInvChanges);
72+
case GoalName.MergeDups:
73+
case GoalName.ReviewDeferredDups:
74+
return MergesCount(changes as MergesCompleted);
75+
case GoalName.ReviewEntries:
76+
return EditsCount(changes as EntriesEdited);
77+
default:
78+
return <Fragment />;
79+
}
80+
}

0 commit comments

Comments
 (0)