From 6c51a1601e74bc822ccafe984d525d0e9a35ca28 Mon Sep 17 00:00:00 2001 From: Daniel Karbach Date: Mon, 2 Jan 2023 18:38:36 +0100 Subject: [PATCH] add tech index --- app/Http/Controllers/SitemapXmlController.php | 9 ++- app/Http/Controllers/TechniqueController.php | 19 ++++++ resources/js/components/App.js | 2 + resources/js/components/pages/Techniques.js | 61 +++++++++++++++++++ resources/js/components/techniques/List.js | 28 +++++++++ .../js/components/techniques/Overview.js | 19 ++++++ resources/js/helpers/Technique.js | 3 + resources/js/i18n/de.js | 3 + resources/js/i18n/en.js | 3 + resources/sass/techniques.scss | 16 +++++ routes/api.php | 1 + 11 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 resources/js/components/pages/Techniques.js create mode 100644 resources/js/components/techniques/List.js create mode 100644 resources/js/components/techniques/Overview.js diff --git a/app/Http/Controllers/SitemapXmlController.php b/app/Http/Controllers/SitemapXmlController.php index ebb7b62..1bd7749 100644 --- a/app/Http/Controllers/SitemapXmlController.php +++ b/app/Http/Controllers/SitemapXmlController.php @@ -22,11 +22,18 @@ class SitemapXmlController extends Controller $urls[] = $url; } + $url = new SitemapUrl(); + $url->path = '/tech'; + $url->lastmod = Technique::latest()->first()->created_at; + $url->changefreq = 'monthly'; + $url->priority = 0.5; + $urls[] = $url; + foreach (Technique::where('index', true)->get() as $tech) { $url = new SitemapUrl(); $url->path = '/tech/'.rawurlencode($tech->name); $url->lastmod = $tech->updated_at ? $tech->updated_at : ($tech->created_at ? $tech->created_at : now()); - $url->changefreq = 'monthly'; + $url->changefreq = 'never'; $url->priority = $tech->priority; $urls[] = $url; } diff --git a/app/Http/Controllers/TechniqueController.php b/app/Http/Controllers/TechniqueController.php index cce39ce..a1ba20e 100644 --- a/app/Http/Controllers/TechniqueController.php +++ b/app/Http/Controllers/TechniqueController.php @@ -3,11 +3,30 @@ namespace App\Http\Controllers; use App\Models\Technique; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Http\Request; class TechniqueController extends Controller { + public function search(Request $request) { + $validatedData = $request->validate([ + 'phrase' => 'string|nullable', + ]); + + $techs = Technique::where('index', '=', 1); + + if (!empty($validatedData['phrase'])) { + $search = $validatedData['phrase']; + $techs = $techs->where(function (Builder $query) use ($search) { + $query->where('title', 'LIKE', '%'.$search.'%') + ->orWhere('short', 'LIKE', '%'.$search.'%'); + }); + } + + return $techs->get()->toJson(); + } + public function single(Request $request, Technique $tech) { $this->authorize('view', $tech); $tech->load('chapters'); diff --git a/resources/js/components/App.js b/resources/js/components/App.js index 0d11f90..6d40303 100644 --- a/resources/js/components/App.js +++ b/resources/js/components/App.js @@ -6,6 +6,7 @@ import Header from './common/Header'; import AlttpSeed from './pages/AlttpSeed'; import Front from './pages/Front'; import Technique from './pages/Technique'; +import Techniques from './pages/Techniques'; import Tournament from './pages/Tournament'; import User from './pages/User'; import AlttpBaseRomProvider from '../helpers/AlttpBaseRomContext'; @@ -57,6 +58,7 @@ const App = () => {
} /> + } /> } /> } /> } /> diff --git a/resources/js/components/pages/Techniques.js b/resources/js/components/pages/Techniques.js new file mode 100644 index 0000000..9334155 --- /dev/null +++ b/resources/js/components/pages/Techniques.js @@ -0,0 +1,61 @@ +import axios from 'axios'; +import React from 'react'; +import { withTranslation } from 'react-i18next'; + +import NotFound from './NotFound'; +import ErrorBoundary from '../common/ErrorBoundary'; +import ErrorMessage from '../common/ErrorMessage'; +import Loading from '../common/Loading'; +import Overview from '../techniques/Overview'; +import { compareTranslation } from '../../helpers/Technique'; +import i18n from '../../i18n'; + +const Techniques = () => { + const [error, setError] = React.useState(null); + const [loading, setLoading] = React.useState(true); + const [techniques, setTechniques] = React.useState([]); + + React.useEffect(() => { + const ctrl = new AbortController(); + setLoading(true); + window.document.title = i18n.t('techniques.heading'); + axios + .get(`/api/tech`, { signal: ctrl.signal }) + .then(response => { + setError(null); + setLoading(false); + setTechniques(response.data.sort(compareTranslation('title', i18n.language))); + }) + .catch(error => { + setError(error); + setLoading(false); + setTechniques([]); + }); + return () => { + ctrl.abort(); + }; + }, []); + + React.useEffect(() => { + window.document.title = i18n.t('techniques.heading'); + setTechniques(t => [...t].sort(compareTranslation('title', i18n.language))); + }, [i18n.language]); + + if (loading) { + return ; + } + + if (error) { + return ; + } + + if (!techniques || !techniques.length) { + return ; + } + + return + + ; +}; + +export default withTranslation()(Techniques); diff --git a/resources/js/components/techniques/List.js b/resources/js/components/techniques/List.js new file mode 100644 index 0000000..0549cd3 --- /dev/null +++ b/resources/js/components/techniques/List.js @@ -0,0 +1,28 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import { Link } from 'react-router-dom'; + +import { getTranslation } from '../../helpers/Technique'; +import i18n from '../../i18n'; + +const List = ({ techniques }) =>
    + {techniques.map(tech => +
  • +

    + + {getTranslation(tech, 'title', i18n.language)} + +

    +

    {getTranslation(tech, 'short', i18n.language)}

    +
  • + )} +
; + +List.propTypes = { + techniques: PropTypes.arrayOf(PropTypes.shape({ + id: PropTypes.number, + name: PropTypes.string, + })), +}; + +export default List; diff --git a/resources/js/components/techniques/Overview.js b/resources/js/components/techniques/Overview.js new file mode 100644 index 0000000..8c7cd4d --- /dev/null +++ b/resources/js/components/techniques/Overview.js @@ -0,0 +1,19 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import { Container } from 'react-bootstrap'; +import { withTranslation } from 'react-i18next'; + +import List from './List'; +import i18n from '../../i18n'; + +const Overview = ({ techniques }) => +

{i18n.t('techniques.heading')}

+ +
; + +Overview.propTypes = { + techniques: PropTypes.arrayOf(PropTypes.shape({ + })), +}; + +export default withTranslation()(Overview); diff --git a/resources/js/helpers/Technique.js b/resources/js/helpers/Technique.js index ea60cc7..7ec22d3 100644 --- a/resources/js/helpers/Technique.js +++ b/resources/js/helpers/Technique.js @@ -10,6 +10,9 @@ export const getTranslation = (tech, prop, lang) => { return tech[prop]; }; +export const compareTranslation = (prop, lang) => (a, b) => + getTranslation(a, prop, lang).localeCompare(getTranslation(b, prop, lang)); + export default { getTranslation, }; diff --git a/resources/js/i18n/de.js b/resources/js/i18n/de.js index f794876..c8be7ae 100644 --- a/resources/js/i18n/de.js +++ b/resources/js/i18n/de.js @@ -460,6 +460,9 @@ export default { unlockError: 'Fehler beim Entsperren', unlockSuccess: 'Runde entsperrt', }, + techniques: { + heading: 'Techniken', + }, tournaments: { admins: 'Organisation', applicationDenied: 'Antrag wurde abgelehnt', diff --git a/resources/js/i18n/en.js b/resources/js/i18n/en.js index dbe37f1..62e486b 100644 --- a/resources/js/i18n/en.js +++ b/resources/js/i18n/en.js @@ -460,6 +460,9 @@ export default { unlockError: 'Error unlocking round', unlockSuccess: 'Round unlocked', }, + techniques: { + heading: 'Techniques', + }, tournaments: { admins: 'Admins', applicationDenied: 'Application denied', diff --git a/resources/sass/techniques.scss b/resources/sass/techniques.scss index c4fa505..19dcf41 100644 --- a/resources/sass/techniques.scss +++ b/resources/sass/techniques.scss @@ -1,3 +1,19 @@ +.tech-list { + margin: 1em 0; + padding: 0; + list-style: none; + + li { + margin: 1ex 0; + padding: 1ex; + border-top: thin solid silver; + } + + h2 > a { + text-decoration: none; + } +} + .tech-outline { float: right; } diff --git a/routes/api.php b/routes/api.php index 3d7062a..0f7aa3e 100644 --- a/routes/api.php +++ b/routes/api.php @@ -43,6 +43,7 @@ Route::post('rounds/{round}/lock', 'App\Http\Controllers\RoundController@lock'); Route::post('rounds/{round}/setSeed', 'App\Http\Controllers\RoundController@setSeed'); Route::post('rounds/{round}/unlock', 'App\Http\Controllers\RoundController@unlock'); +Route::get('tech', 'App\Http\Controllers\TechniqueController@search'); Route::get('tech/{tech:name}', 'App\Http\Controllers\TechniqueController@single'); Route::get('tournaments/{id}', 'App\Http\Controllers\TournamentController@single'); -- 2.39.2