From 72d6c25d3b1a6d79843d5673ba78804dd442ae39 Mon Sep 17 00:00:00 2001
From: Daniel Karbach <daniel.karbach@localhorst.tv>
Date: Tue, 25 Oct 2022 14:29:23 +0200
Subject: [PATCH] basic seed code input

---
 app/Http/Controllers/RoundController.php      |   3 +
 resources/js/components/rounds/EditForm.js    |  27 +++
 resources/js/components/rounds/Item.js        |   2 +-
 .../js/components/rounds/SeedCodeInput.js     | 162 ++++++++++++++++++
 resources/js/i18n/de.js                       |   1 +
 resources/js/i18n/en.js                       |   1 +
 6 files changed, 195 insertions(+), 1 deletion(-)
 create mode 100644 resources/js/components/rounds/SeedCodeInput.js

diff --git a/app/Http/Controllers/RoundController.php b/app/Http/Controllers/RoundController.php
index 2b6c617..337e111 100644
--- a/app/Http/Controllers/RoundController.php
+++ b/app/Http/Controllers/RoundController.php
@@ -43,10 +43,13 @@ class RoundController extends Controller
 		$this->authorize('update', $round);
 
 		$validatedData = $request->validate([
+			'code' => 'array',
+			'code.*' => 'string',
 			'seed' => 'url',
 			'title' => 'string',
 		]);
 
+		$round->code = array_filter($validatedData['code']);
 		$round->seed = $validatedData['seed'];
 		$round->title = $validatedData['title'];
 		$round->update();
diff --git a/resources/js/components/rounds/EditForm.js b/resources/js/components/rounds/EditForm.js
index 57ee95a..3d0d0b3 100644
--- a/resources/js/components/rounds/EditForm.js
+++ b/resources/js/components/rounds/EditForm.js
@@ -6,6 +6,7 @@ import { Button, Col, Form, Modal, Row } from 'react-bootstrap';
 import { withTranslation } from 'react-i18next';
 import toastr from 'toastr';
 
+import SeedCodeInput from './SeedCodeInput';
 import laravelErrorsToFormik from '../../helpers/laravelErrorsToFormik';
 import i18n from '../../i18n';
 import yup from '../../schema/yup';
@@ -57,6 +58,25 @@ const EditForm = ({
 				: null}
 			</Form.Group>
 		</Row>
+		<Row>
+			<Form.Group as={Col}>
+				<Form.Label>{i18n.t('rounds.code')}</Form.Label>
+				<Form.Control
+					as={SeedCodeInput}
+					game={values.game || 'mixed'}
+					isInvalid={!!(touched.code && errors.code)}
+					name="code"
+					onBlur={handleBlur}
+					onChange={handleChange}
+					value={values.code || []}
+				/>
+				{touched.code && errors.code ?
+					<Form.Control.Feedback type="invalid">
+						{i18n.t(errors.code)}
+					</Form.Control.Feedback>
+				: null}
+			</Form.Group>
+		</Row>
 	</Modal.Body>
 	<Modal.Footer>
 		{onCancel ?
@@ -72,6 +92,7 @@ const EditForm = ({
 
 EditForm.propTypes = {
 	errors: PropTypes.shape({
+		code: PropTypes.arrayOf(PropTypes.string),
 		seed: PropTypes.string,
 		title: PropTypes.string,
 	}),
@@ -80,10 +101,13 @@ EditForm.propTypes = {
 	handleSubmit: PropTypes.func,
 	onCancel: PropTypes.func,
 	touched: PropTypes.shape({
+		code: PropTypes.arrayOf(PropTypes.bool),
 		seed: PropTypes.bool,
 		title: PropTypes.bool,
 	}),
 	values: PropTypes.shape({
+		code: PropTypes.arrayOf(PropTypes.string),
+		game: PropTypes.string,
 		seed: PropTypes.string,
 		title: PropTypes.string,
 	}),
@@ -110,11 +134,14 @@ export default withFormik({
 		}
 	},
 	mapPropsToValues: ({ round }) => ({
+		code: round.code || [],
+		game: round.game || 'mixed',
 		round_id: round.id,
 		seed: round.seed || '',
 		title: round.title || '',
 	}),
 	validationSchema: yup.object().shape({
+		code: yup.array().of(yup.string()),
 		seed: yup.string().url(),
 		title: yup.string(),
 	}),
diff --git a/resources/js/components/rounds/Item.js b/resources/js/components/rounds/Item.js
index 92f55a0..3197f6d 100644
--- a/resources/js/components/rounds/Item.js
+++ b/resources/js/components/rounds/Item.js
@@ -51,7 +51,7 @@ const Item = ({
 				{i18n.t('rounds.date', { date: new Date(round.created_at) })}
 			</p>
 			<p className="seed">
-				{round.code ?
+				{round.code && round.code.length ?
 					<>
 						<SeedCode code={round.code} game={round.game || 'alttpr'} />
 						<br />
diff --git a/resources/js/components/rounds/SeedCodeInput.js b/resources/js/components/rounds/SeedCodeInput.js
new file mode 100644
index 0000000..03dd682
--- /dev/null
+++ b/resources/js/components/rounds/SeedCodeInput.js
@@ -0,0 +1,162 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import { Form } from 'react-bootstrap';
+import { withTranslation } from 'react-i18next';
+
+import i18n from '../../i18n';
+
+const ALTTPR_CODES = [
+	'big-key',
+	'bow',
+	'blue-boomerang',
+	'bomb',
+	'bombos',
+	'book',
+	'boots',
+	'bottle',
+	'bow',
+	'bugnet',
+	'cape',
+	'compass',
+	'ether',
+	'flippers',
+	'flute',
+	'glove',
+	'green-mail',
+	'green-pendant',
+	'green-potion',
+	'hammer',
+	'heart-container',
+	'hookshot',
+	'ice-rod',
+	'lamp',
+	'map',
+	'mirror',
+	'mirror-shield',
+	'moonpearl',
+	'mushroom',
+	'powder',
+	'quake',
+	'shovel',
+];
+
+const SMR_CODES = [
+	'ALCOON',
+	'ATOMIC',
+	'BEETOM',
+	'BOYON',
+	'BULL',
+	'CHOOT',
+	'COVERN',
+	'EVIR',
+	'FUNE',
+	'GAMET',
+	'GEEMER',
+	'GERUTA',
+	'HOLTZ',
+	'KAGO',
+	'NAMIHE',
+	'OUM',
+	'OWTCH',
+	'POWAMP',
+	'PUROMI',
+	'PUYO',
+	'RINKA',
+	'RIPPER',
+	'SCISER',
+	'SKREE',
+	'SOVA',
+	'TATORI',
+	'VIOLA',
+	'WAVER',
+	'YARD',
+	'ZEBBO',
+	'ZEELA',
+	'ZOA',
+];
+
+const SeedCodeInput = ({
+	className,
+	game,
+	name,
+	onBlur,
+	onChange,
+	value,
+}) => {
+	if (game === 'alttpr') {
+		const code_trans = ALTTPR_CODES
+			.map(code => ({ code, label: i18n.t(`icon.zelda.${code}`)}))
+			.sort((a, b) => a.label.localeCompare(b.label));
+		return <div
+			className={`${className} seed-code-input-alttpr`}
+		>
+			{[0, 1, 2, 3, 4].map(num =>
+				<Form.Select
+					key={num}
+					onBlur={onBlur}
+					onChange={onChange}
+					name={`${name}[${num}]`}
+					value={(value && value[num]) || ''}
+				>
+					<option value=""></option>
+					{code_trans.map(({ code, label }) =>
+						<option key={code} value={code}>{label}</option>
+					)}
+				</Form.Select>
+			)}
+		</div>;
+	}
+	if (game === 'smr') {
+		return <div
+			className={`${className} seed-code-input-smr`}
+		>
+			{[0, 1, 2, 3].map(num =>
+				<Form.Select
+					key={num}
+					onBlur={onBlur}
+					onChange={onChange}
+					name={`${name}[${num}]`}
+					value={(value && value[num]) || ''}
+				>
+					<option value=""></option>
+					{SMR_CODES.sort((a, b) => a.localeCompare(b)).map(code =>
+						<option key={code} value={code}>{code}</option>
+					)}
+				</Form.Select>
+			)}
+		</div>;
+	}
+	return <div
+		className={`${className} seed-code-input-default`}
+	>
+		{[0, 1, 2, 3, 4].map(num =>
+			<Form.Control
+				key={num}
+				onBlur={onBlur}
+				onChange={onChange}
+				name={`${name}[${num}]`}
+				value={(value && value[num]) || ''}
+			/>
+		)}
+	</div>;
+};
+
+SeedCodeInput.propTypes = {
+	className: PropTypes.string,
+	game: PropTypes.string,
+	name: PropTypes.string,
+	onBlur: PropTypes.func,
+	onChange: PropTypes.func,
+	value: PropTypes.arrayOf(PropTypes.string),
+};
+
+SeedCodeInput.defaultProps = {
+	className: '',
+	game: '',
+	name: '',
+	onBlur: null,
+	onChange: null,
+	value: [],
+};
+
+export default withTranslation()(SeedCodeInput);
diff --git a/resources/js/i18n/de.js b/resources/js/i18n/de.js
index 97958d6..4af252d 100644
--- a/resources/js/i18n/de.js
+++ b/resources/js/i18n/de.js
@@ -425,6 +425,7 @@ export default {
 			time: 'Zeit: {{ time }}',
 		},
 		rounds: {
+			code: 'Code',
 			date: '{{ date, L }}',
 			edit: 'Runde bearbeiten',
 			editError: 'Fehler beim Speichern',
diff --git a/resources/js/i18n/en.js b/resources/js/i18n/en.js
index e7e0c59..9944fd7 100644
--- a/resources/js/i18n/en.js
+++ b/resources/js/i18n/en.js
@@ -425,6 +425,7 @@ export default {
 			time: 'Time: {{ time }}',
 		},
 		rounds: {
+			code: 'Code',
 			date: '{{ date, L }}',
 			edit: 'Edit round',
 			editError: 'Error saving round',
-- 
2.39.5