Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit05209eb

Browse files
authored
Merge pull requestcoderoad#412 from coderoad/feature/admin-mode
WIP - Feature/admin mode
2 parentsc6261c7 +23bedee commit05209eb

File tree

10 files changed

+225
-74
lines changed

10 files changed

+225
-74
lines changed

‎src/actions/onRunReset.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,21 @@ import * as TT from 'typings/tutorial'
33
importContextfrom'../services/context/context'
44
import{exec}from'../services/node'
55
importresetfrom'../services/reset'
6-
importgetLastCommitHashfrom'../services/reset/lastHash'
6+
importgetCommitHashByPositionfrom'../services/reset/lastHash'
77

8-
constonRunReset=async(context:Context)=>{
8+
typeResetAction={
9+
type:'LATEST'|'POSITION'
10+
position?:T.Position
11+
}
12+
13+
// reset to the start of the last test
14+
constonRunReset=async(action:ResetAction,context:Context)=>{
915
// reset to timeline
1016
consttutorial:TT.Tutorial|null=context.tutorial.get()
11-
constposition:T.Position=context.position.get()
17+
constposition:T.Position=action.position ?action.position :context.position.get()
1218

1319
// get last pass commit
14-
consthash=getLastCommitHash(position,tutorial?.levels||[])
20+
consthash:string=getCommitHashByPosition(position,tutorial)
1521

1622
constbranch=tutorial?.config.repo.branch
1723

‎src/channel.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,11 @@ class Channel implements Channel {
7979
case'EDITOR_RUN_TEST':
8080
actions.onRunTest(action)
8181
return
82-
case'EDITOR_RUN_RESET':
83-
actions.onRunReset(this.context)
82+
case'EDITOR_RUN_RESET_LATEST':
83+
actions.onRunReset({type:'LATEST'},this.context)
84+
return
85+
case'EDITOR_RUN_RESET_POSITION':
86+
actions.onRunReset({type:'POSITION',position:action.payload.position},this.context)
8487
return
8588
default:
8689
logger(`No match for action type:${actionType}`)

‎src/services/reset/lastHash.test.ts

Lines changed: 71 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5,52 +5,83 @@ import getLastCommitHash from './lastHash'
55
describe('lastHash',()=>{
66
it('should grab the last passing hash from a step',()=>{
77
constposition:T.Position={levelId:'1',stepId:'1.2'}
8-
constlevels:TT.Level[]=[
9-
{
10-
id:'1',
11-
title:'',
12-
summary:'',
13-
content:'',
14-
steps:[
15-
{
16-
id:'1.1',
17-
content:'',
18-
setup:{commits:['abcdef1']},
19-
},
20-
{
21-
id:'1.2',
22-
content:'',
23-
setup:{commits:['abcdef2']},
24-
},
25-
],
26-
},
27-
]
28-
constresult=getLastCommitHash(position,levels)
8+
//@ts-ignore
9+
consttutorial:TT.Tutorial={
10+
levels:[
11+
{
12+
id:'1',
13+
title:'',
14+
summary:'',
15+
content:'',
16+
steps:[
17+
{
18+
id:'1.1',
19+
content:'',
20+
setup:{commits:['abcdef1']},
21+
},
22+
{
23+
id:'1.2',
24+
content:'',
25+
setup:{commits:['abcdef2']},
26+
},
27+
],
28+
},
29+
],
30+
}
31+
constresult=getLastCommitHash(position,tutorial)
2932
expect(result).toBe('abcdef2')
3033
})
3134
it('should grab the last passing hash from a step with several commits',()=>{
3235
constposition:T.Position={levelId:'1',stepId:'1.2'}
33-
constlevels:TT.Level[]=[
34-
{
35-
id:'1',
36-
title:'',
37-
summary:'',
38-
content:'',
39-
steps:[
40-
{
41-
id:'1.1',
42-
content:'',
43-
setup:{commits:['abcdef1']},
44-
},
45-
{
46-
id:'1.2',
47-
content:'',
48-
setup:{commits:['abcdef2','abcdef3']},
36+
//@ts-ignore
37+
consttutorial:TT.Tutorial={
38+
levels:[
39+
{
40+
id:'1',
41+
title:'',
42+
summary:'',
43+
content:'',
44+
steps:[
45+
{
46+
id:'1.1',
47+
content:'',
48+
setup:{commits:['abcdef1']},
49+
},
50+
{
51+
id:'1.2',
52+
content:'',
53+
setup:{commits:['abcdef2','abcdef3']},
54+
},
55+
],
56+
},
57+
],
58+
}
59+
constresult=getLastCommitHash(position,tutorial)
60+
expect(result).toBe('abcdef3')
61+
})
62+
it('should grab the last passing hash when level has no steps',()=>{
63+
constposition:T.Position={levelId:'1',stepId:null}
64+
//@ts-ignore
65+
consttutorial:TT.Tutorial={
66+
config:{
67+
//@ts-ignore
68+
testRunner:{
69+
setup:{
70+
commits:['abcdef2','abcdef3'],
4971
},
50-
],
72+
},
5173
},
52-
]
53-
constresult=getLastCommitHash(position,levels)
74+
levels:[
75+
{
76+
id:'1',
77+
title:'',
78+
summary:'',
79+
content:'',
80+
steps:[],
81+
},
82+
],
83+
}
84+
constresult=getLastCommitHash(position,tutorial)
5485
expect(result).toBe('abcdef3')
5586
})
5687
})

‎src/services/reset/lastHash.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,42 @@
11
import*asTTfrom'../../../typings/tutorial'
22
import*asTfrom'../../../typings'
33

4-
constgetLastCommitHash=(position:T.Position,levels:TT.Level[])=>{
4+
constgetLastCommitHash=(position:T.Position,tutorial:TT.Tutorial|null)=>{
5+
if(!tutorial){
6+
thrownewError('No tutorial found')
7+
}
8+
const{ levels}=tutorial
59
// get previous position
610
const{ levelId, stepId}=position
711

8-
constlevel:TT.Level|undefined=levels.find((l)=>levelId===l.id)
12+
letlevel:TT.Level|undefined=levels.find((l)=>levelId===l.id)
913
if(!level){
1014
thrownewError(`No level found matching${levelId}`)
1115
}
16+
17+
// handle a level with no steps
18+
if(!level.steps||!level.steps.length){
19+
if(level.setup&&level.setup.commits){
20+
// return level commit
21+
constlevelCommits=level.setup.commits
22+
returnlevelCommits[levelCommits.length-1]
23+
}else{
24+
// is there a previous level?
25+
//@ts-ignore
26+
constlevelIndex=levels.findIndex((l:TT.Level)=>level.id===l.id)
27+
if(levelIndex>0){
28+
level=levels[levelIndex-1]
29+
}else{
30+
// use init commit
31+
constconfigCommits=tutorial.config.testRunner.setup?.commits
32+
if(!configCommits){
33+
thrownewError('No commits found to reset back to')
34+
}
35+
returnconfigCommits[configCommits.length-1]
36+
}
37+
}
38+
}
39+
1240
conststep=level.steps.find((s)=>stepId===s.id)
1341
if(!step){
1442
thrownewError(`No step found matching${stepId}`)

‎web-app/src/containers/Tutorial/containers/Review.tsx

Lines changed: 77 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
import*asReactfrom'react'
22
import*asTfrom'typings'
3-
import{Switch}from'@alifd/next'
4-
importStepsfrom'../components/Steps'
3+
import{Button,Icon}from'@alifd/next'
4+
importStepfrom'../components/Step'
5+
importHintsfrom'../components/Hints'
56
importContentfrom'../components/Content'
67
import{Theme}from'../../../styles/theme'
8+
importAdminContextfrom'../../../services/admin/context'
79

810
interfaceProps{
911
levels:T.LevelUI[]
12+
onResetToPosition(position:T.Position):void
1013
}
1114

1215
conststyles={
@@ -36,28 +39,88 @@ const styles = {
3639
fontSize:'70%',
3740
},
3841
levels:{},
42+
steps:{
43+
padding:'1rem 1rem',
44+
},
45+
adminNav:{
46+
position:'absolute'as'absolute',
47+
right:'1rem',
48+
lineHeight:'16px',
49+
},
3950
}
4051

4152
constReviewPage=(props:Props)=>{
42-
const[stepVisibility,setStepVisibility]=React.useState(true)
53+
const{
54+
state:{ adminMode},
55+
}=React.useContext(AdminContext)
56+
constshow=(status:T.ProgressStatus):boolean=>{
57+
returnadminMode||status!=='INCOMPLETE'
58+
}
4359
return(
4460
<divcss={styles.container}>
4561
<divcss={styles.header}>
4662
<div>Review</div>
47-
<divcss={styles.control}>
48-
<span>Show steps&nbsp;</span>
49-
<Switchchecked={stepVisibility}onChange={(checked)=>setStepVisibility(checked)}/>
50-
</div>
5163
</div>
5264

5365
<divcss={styles.levels}>
54-
{props.levels.map((level:T.LevelUI,index:number)=>(
55-
<>
56-
<Contenttitle={level.title}content={level.content}/>
57-
{stepVisibility ?<Stepssteps={level.steps}displayAll/> :null}
58-
{index<props.levels.length-1 ?<hr/> :null}
59-
</>
60-
))}
66+
{props.levels.map((level:T.LevelUI,index:number)=>
67+
show(level.status) ?(
68+
<divkey={level.id}>
69+
{adminMode&&(
70+
<divcss={styles.adminNav}>
71+
<Button
72+
type="normal"
73+
warning
74+
onClick={()=>
75+
props.onResetToPosition({
76+
levelId:level.id,
77+
stepId:level.steps.length ?level.steps[0].id :null,
78+
})
79+
}
80+
>
81+
{level.id}&nbsp;
82+
<Icontype="refresh"/>
83+
</Button>
84+
</div>
85+
)}
86+
<Contenttitle={level.title}content={level.content}/>
87+
88+
<divcss={styles.steps}>
89+
{level.steps.map((step:T.StepUI)=>{
90+
if(!step){
91+
returnnull
92+
}
93+
returnshow(step.status) ?(
94+
<divkey={step.id}>
95+
{adminMode&&(
96+
<divcss={styles.adminNav}>
97+
<Button
98+
type="normal"
99+
warning
100+
onClick={()=>props.onResetToPosition({levelId:level.id,stepId:step.id})}
101+
>
102+
{step.id}&nbsp;
103+
<Icontype="refresh"/>
104+
</Button>
105+
</div>
106+
)}
107+
<Step
108+
key={step.id}
109+
status={step.status}
110+
displayAll={true}
111+
content={step.content}
112+
subtasks={step.subtasks}
113+
/>
114+
<Hintshints={step.hints||[]}/>
115+
</div>
116+
) :null
117+
})}
118+
</div>
119+
120+
{index<props.levels.length-1 ?<hr/> :null}
121+
</div>
122+
) :null,
123+
)}
61124
</div>
62125
</div>
63126
)

‎web-app/src/containers/Tutorial/formatLevels.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,24 @@ const formatLevels = ({ progress, position, levels, testStatus }: Input): Output
3232

3333
constcurrentLevel=levels[levelIndex]
3434

35+
letstepIndex=currentLevel.steps.findIndex((s:TT.Step)=>s.id===position.stepId)
36+
if(stepIndex===-1){
37+
stepIndex=levels[levelIndex].steps.length
38+
}
39+
3540
constlevelUI:T.LevelUI={
3641
...currentLevel,
3742
status:progress.levels[position.levelId] ?'COMPLETE' :'ACTIVE',
38-
steps:currentLevel.steps.map((step:TT.Step)=>{
43+
steps:currentLevel.steps.map((step:TT.Step,index)=>{
3944
// label step status for step component
4045
letstatus:T.ProgressStatus='INCOMPLETE'
4146
letsubtasks
42-
if(progress.steps[step.id]){
47+
if(index<stepIndex||(index===stepIndex&&progress.steps[step.id])){
4348
status='COMPLETE'
44-
}elseif(step.id===position.stepId){
49+
}elseif(index===stepIndex){
4550
status='ACTIVE'
51+
}else{
52+
status='INCOMPLETE'
4653
}
4754
if(step.subtasks&&step.subtasks){
4855
consttestSummaries=Object.keys(testStatus?.summary||{})
@@ -95,10 +102,6 @@ const formatLevels = ({ progress, position, levels, testStatus }: Input): Output
95102

96103
constlevelsUI:T.LevelUI[]=[...completed,levelUI, ...incompleted]
97104

98-
letstepIndex=levelUI.steps.findIndex((s:T.StepUI)=>s.status==='ACTIVE')
99-
if(stepIndex===-1){
100-
stepIndex=levels[levelIndex].steps.length
101-
}
102105
return{level:levelUI,levels:levelsUI, levelIndex, stepIndex}
103106
}
104107

‎web-app/src/containers/Tutorial/index.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ const TutorialPage = (props: PageProps) => {
119119
props.send({type:'RUN_RESET'})
120120
}
121121

122+
constonResetToPosition=(position:T.Position):void=>{
123+
props.send({type:'RUN_RESET_TO_POSITION',payload:{ position}})
124+
}
125+
122126
const[menuVisible,setMenuVisible]=React.useState(false)
123127

124128
const[page,setPage]=React.useState<'about'|'level'|'review'|'settings'>('level')
@@ -150,7 +154,7 @@ const TutorialPage = (props: PageProps) => {
150154
<Levellevel={level}/>
151155
</ScrollContent>
152156
)}
153-
{page==='review'&&<ReviewPagelevels={levels}/>}
157+
{page==='review'&&<ReviewPagelevels={levels}onResetToPosition={onResetToPosition}/>}
154158

155159
{/* {page === 'settings' && <SettingsPage />} */}
156160
</div>

‎web-app/src/environment.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,4 @@ export const TUTORIAL_LIST_URL: string = process.env.REACT_APP_TUTORIAL_LIST_URL
1616
exportconstDISPLAY_RUN_TEST_BUTTON=
1717
(process.env.CODEROAD_DISPLAY_RUN_TEST_BUTTON||'true').toLowerCase()!=='false'// default true
1818

19-
exportconstADMIN_MODE=false
20-
// (process.env.CODEROAD_ADMIN_MODE || process.env.STORYBOOK_ADMIN_MODE || '').toLowerCase() === 'true' // default false
19+
exportconstADMIN_MODE=(process.env.CODEROAD_ADMIN_MODE||'').toLowerCase()==='true'// default false

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp