-
Notifications
You must be signed in to change notification settings - Fork 176
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a page for editing scrambles #10129
Open
gregorbg
wants to merge
9
commits into
thewca:main
Choose a base branch
from
gregorbg:feature/wrt-scramble-edit
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
3ad4c2f
Create dummy routes and untested backend for scrambles edit
gregorbg 2e3ef32
Frontend WIP
gregorbg 9f9d2e5
Create working skeleton of Scramble edit interface
gregorbg e7e9e10
Add fields for remaining scramble properties
gregorbg 8cfbfc4
Adapt nestedInputUpdater for checkboxes
gregorbg 4f1cd2f
Extra display bugfixing
gregorbg a6f2508
Fix remaining mentions of scramble VS result
gregorbg 18d939d
Fix the initialization data when creating new scramble
gregorbg 643133e
Fix single column override in scramble view
gregorbg File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
# frozen_string_literal: true | ||
|
||
module Admin | ||
class ScramblesController < AdminController | ||
# NOTE: authentication is performed by admin controller | ||
|
||
def new | ||
competition = Competition.find(params[:competition_id]) | ||
round = Round.find(params[:round_id]) | ||
# Create some basic attributes for that empty scramble. | ||
# Using Scramble.new wouldn't work here: we have no idea what the country | ||
# could be and so on, so serialization would fail. | ||
@scramble = { | ||
competitionId: competition.id, | ||
roundTypeId: round.round_type_id, | ||
eventId: round.event.id, | ||
} | ||
end | ||
|
||
def show | ||
respond_to do |format| | ||
format.json { render json: Scramble.find(params.require(:id)) } | ||
end | ||
end | ||
|
||
def edit | ||
@scramble = Scramble.includes(:competition).find(params[:id]) | ||
end | ||
|
||
def create | ||
json = {} | ||
# Build a brand new scramble, validations will make sure the specified round | ||
# data are valid. | ||
scramble = Scramble.new(scramble_params) | ||
if scramble.save | ||
# We just inserted a new scramble, make sure we at least give it correct information. | ||
validator = ResultsValidators::ScramblesValidator.new(apply_fixes: true) | ||
validator.validate(competition_ids: [scramble.competitionId]) | ||
json[:messages] = ["Scramble inserted!"].concat(validator.infos.map(&:to_s)) | ||
else | ||
json[:errors] = scramble.errors.map(&:full_message) | ||
end | ||
render json: json | ||
end | ||
|
||
def update | ||
scramble = Scramble.find(params.require(:id)) | ||
# Since we may move the scramble to another competition, we want to validate | ||
# both competitions if needed. | ||
competitions_to_validate = [scramble.competitionId] | ||
if scramble.update(scramble_params) | ||
competitions_to_validate << scramble.competitionId | ||
competitions_to_validate.uniq! | ||
validator = ResultsValidators::ScramblesValidator.new(apply_fixes: true) | ||
validator.validate(competition_ids: competitions_to_validate) | ||
info = if scramble.saved_changes.empty? | ||
["It looks like you submitted the exact same scramble, so no changes were made."] | ||
else | ||
["The scramble was saved."] | ||
end | ||
if competitions_to_validate.size > 1 | ||
info << "The scrambles was moved to another competition, make sure to check the competition validators for both of them." | ||
end | ||
render json: { | ||
# Make sure we emit the competition's id next to the info, because we | ||
# may validate multiple competitions at the same time. | ||
messages: info.concat(validator.infos.map { |i| "[#{i.competition_id}]#{i}" }), | ||
} | ||
else | ||
render json: { | ||
errors: scramble.errors.map(&:full_message), | ||
} | ||
end | ||
end | ||
|
||
def destroy | ||
scramble = Scramble.find(params.require(:id)) | ||
competition_id = scramble.competitionId | ||
scramble.destroy! | ||
|
||
# Create a results validator to fix information if needed | ||
validator = ResultsValidators::ScramblesValidator.new(apply_fixes: true) | ||
validator.validate(competition_ids: [competition_id]) | ||
|
||
render json: { | ||
messages: ["Scramble deleted!"].concat(validator.infos.map(&:to_s)), | ||
} | ||
end | ||
|
||
private def scramble_params | ||
params.require(:scramble).permit(:competitionId, :roundTypeId, :eventId, :groupId, | ||
:isExtra, :scrambleNum, :scramble) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<% provide(:title, "Edit a scramble") %> | ||
|
||
<div class="container"> | ||
<%= react_component("EditScramble", { | ||
id: @scramble.id, | ||
}) %> | ||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<% provide(:title, "Add a scramble") %> | ||
|
||
<div class="container"> | ||
<%= react_component("EditScramble/Create", { | ||
scramble: @scramble, | ||
}) %> | ||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import React from 'react'; | ||
import CreateEntry from '../ResultsData/Panel/CreateEntry'; | ||
import { InlineEditForm } from './index'; | ||
|
||
function NewScramble({ | ||
scramble, | ||
}) { | ||
return ( | ||
<CreateEntry | ||
initDataItem={scramble} | ||
dataType="scramble" | ||
EditForm={InlineEditForm} | ||
/> | ||
); | ||
} | ||
|
||
export default NewScramble; |
52 changes: 52 additions & 0 deletions
52
app/webpacker/components/EditScramble/ScrambleForm/AfterActionMessage.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import React from 'react'; | ||
import { Message, List } from 'semantic-ui-react'; | ||
|
||
import { | ||
adminCheckExistingResultsUrl, | ||
competitionScramblesUrl, | ||
competitionUrl, | ||
} from '../../../lib/requests/routes.js.erb'; | ||
|
||
function AfterActionMessage({ | ||
eventId, | ||
competitionId, | ||
response, | ||
}) { | ||
return ( | ||
<> | ||
<Message | ||
positive | ||
header={( | ||
<> | ||
Action performed for: | ||
{' '} | ||
<a href={competitionUrl(competitionId)} target="_blank" rel="noreferrer">{competitionId}</a> | ||
</> | ||
)} | ||
list={response.messages} | ||
/> | ||
<Message positive> | ||
<div> | ||
Please make sure to: | ||
<List ordered> | ||
<List.Item> | ||
<a | ||
href={adminCheckExistingResultsUrl(competitionId)} | ||
target="_blank" | ||
rel="noreferrer" | ||
> | ||
Check Competition Validators | ||
</a> | ||
</List.Item> | ||
</List> | ||
You can also | ||
{' '} | ||
<a href={competitionScramblesUrl(competitionId, eventId)}>go back to the scrambles</a> | ||
. | ||
</div> | ||
</Message> | ||
</> | ||
); | ||
} | ||
|
||
export default AfterActionMessage; |
27 changes: 27 additions & 0 deletions
27
app/webpacker/components/EditScramble/ScrambleForm/DeleteScrambleButton.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import React, { useState, useCallback } from 'react'; | ||
|
||
import { Button, Checkbox } from 'semantic-ui-react'; | ||
|
||
function DeleteScrambleButton({ deleteAction }) { | ||
const [confirmed, setConfirmed] = useState(false); | ||
const updater = useCallback(() => setConfirmed((prev) => !prev), [setConfirmed]); | ||
return ( | ||
<div> | ||
<Button | ||
negative | ||
className="delete-scramble-button" | ||
disabled={!confirmed} | ||
onClick={deleteAction} | ||
> | ||
Delete the scramble | ||
</Button> | ||
<Checkbox | ||
label="Yes, I want to delete that scramble" | ||
checked={confirmed} | ||
onChange={updater} | ||
/> | ||
</div> | ||
); | ||
} | ||
|
||
export default DeleteScrambleButton; |
101 changes: 101 additions & 0 deletions
101
app/webpacker/components/EditScramble/ScrambleForm/RoundForm.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
import React, { useState } from 'react'; | ||
import { | ||
Form, Grid, Icon, Popup, | ||
} from 'semantic-ui-react'; | ||
|
||
import _ from 'lodash'; | ||
import { events, roundTypes } from '../../../lib/wca-data.js.erb'; | ||
import useNestedInputUpdater from '../../../lib/hooks/useNestedInputUpdater'; | ||
import { competitionEventsDataUrl } from '../../../lib/requests/routes.js.erb'; | ||
import { fetchJsonOrError } from '../../../lib/requests/fetchWithAuthenticityToken'; | ||
|
||
const itemFromId = (id, items) => ({ | ||
key: id, | ||
value: id, | ||
text: items.byId[id].name, | ||
}); | ||
|
||
const formatRoundData = ({ eventId, formatId, roundTypeId }) => ({ | ||
[eventId]: { | ||
eventId, | ||
rounds: [{ formatId, roundTypeId }], | ||
}, | ||
}); | ||
|
||
const extractFromRoundData = (roundData, eventId, key, items) => { | ||
const ids = _.uniq(roundData[eventId].rounds.map((r) => r[key])); | ||
return ids.map((id) => itemFromId(id, items)); | ||
}; | ||
|
||
function RoundForm({ roundData, setRoundData }) { | ||
const { | ||
competitionId, roundTypeId, eventId, | ||
} = roundData; | ||
|
||
const setCompetition = useNestedInputUpdater(setRoundData, 'competitionId'); | ||
const setEvent = useNestedInputUpdater(setRoundData, 'eventId'); | ||
const setRoundType = useNestedInputUpdater(setRoundData, 'roundTypeId'); | ||
|
||
const [competitionIdError, setCompetitionIdError] = useState(null); | ||
|
||
const [localRoundData, setLocalRoundData] = useState(formatRoundData(roundData)); | ||
|
||
const availableEvents = Object.keys(localRoundData).map((k) => itemFromId(k, events)); | ||
const availableRoundTypes = extractFromRoundData(localRoundData, eventId, 'roundTypeId', roundTypes); | ||
|
||
const fetchDataForCompetition = (id) => { | ||
setCompetitionIdError(null); | ||
fetchJsonOrError(competitionEventsDataUrl(id)).then(({ data }) => { | ||
setLocalRoundData(data); | ||
}).catch((err) => setCompetitionIdError(err.message)); | ||
}; | ||
|
||
// FIXME: we use padded grid here because Bootstrap's row conflicts with | ||
// FUI's row and messes up the negative margins... :( | ||
return ( | ||
<Form> | ||
<Grid stackable padded columns={3}> | ||
<Grid.Column> | ||
<Form.Input | ||
label="Competition ID" | ||
value={competitionId} | ||
onChange={setCompetition} | ||
error={competitionIdError} | ||
icon={( | ||
<Popup | ||
trigger={( | ||
<Icon | ||
circular | ||
link | ||
onClick={() => fetchDataForCompetition(competitionId)} | ||
name="sync" | ||
/> | ||
)} | ||
content="Get the events and round data for that competition" | ||
position="top right" | ||
/> | ||
)} | ||
/> | ||
</Grid.Column> | ||
<Grid.Column> | ||
<Form.Select | ||
label="Event" | ||
value={eventId} | ||
onChange={setEvent} | ||
options={availableEvents} | ||
/> | ||
</Grid.Column> | ||
<Grid.Column> | ||
<Form.Select | ||
label="Round type" | ||
value={roundTypeId} | ||
onChange={setRoundType} | ||
options={availableRoundTypes} | ||
/> | ||
</Grid.Column> | ||
</Grid> | ||
</Form> | ||
); | ||
} | ||
|
||
export default RoundForm; |
26 changes: 26 additions & 0 deletions
26
app/webpacker/components/EditScramble/ScrambleForm/SaveMessage.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import React from 'react'; | ||
|
||
import { Message } from 'semantic-ui-react'; | ||
|
||
function SaveMessage({ response }) { | ||
return ( | ||
<> | ||
{response.messages && ( | ||
<Message | ||
positive | ||
header="Save was successful!" | ||
list={response.messages} | ||
/> | ||
)} | ||
{response.errors && ( | ||
<Message | ||
error | ||
list={response.errors} | ||
header="Something went wrong when saving the scramble." | ||
/> | ||
)} | ||
</> | ||
); | ||
} | ||
|
||
export default SaveMessage; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean by "country" here?