野球リーグスコア管理システムの開発

2019-01-13に作成

image
野球リーグスコア管理システムに関する進捗です。

[PR]参加者募集しています!

使っている技術など

  • NodeJS
  • ReactJS
  • netlify
  • MySQL
  • materializecss
  • react-bootstrap
  • react-bootstrap-table-next

旧システムについてはこちらの記事をご覧ください。

残りタスクリスト

trello

所有者限定モードのためこのボードには投稿できません ボードとは?

一日で作るReactNativeアプリ

Screenshot_20191104-185009_pixel_quite_black_portrait.png

審査通りました!(11/7)

3日かかりましたが審査通りました!

開発の経緯

リーグ参加者にも少数ではあるがソフトウェア開発に知見がある人がいて、こういうのが欲しいというプロトタイプ(上記)まで見せてもらった。

作ったもの

開発に用いたライブラリ

deploygateならiOSもコストなく配布できると思っていた

Xcodeでarchiveはできる
dg deploy(deploygateにipaを上げるコマンド)
→iOSアプリ配布用の証明書が必要といわれる
→iOS developer programに入ってないと作れないよ
→iOS版断念...orz

Google Play Developer Consoleに登録した

当初は野良APKで配布するつもりだったのですが、iOS developer programほどひどいライセンス体系ではない(登録時に$25)ので登録しました。「提供元不明」だと誰も使ってくれなさそうだったので。

審査に出しました

いろいろ記入するところあって予想以上に大変だった....

10/23フィードバック対応

同率順位非表示の修正

ikenにフィードバックいただきました。ありがとうございます。

スタッツ画面で、チーム順位タブでは同率の場合に順位表示が消えますが、例えば3位が2チームある場合は「同率3」などの表現だと、試合数や勝率で並び替えた場合にも順位がわかり、わかりやすくなるかもと思いました。安打数とかもですかね?(並び替えはできないですが)

そもそもソートを想定していなかったのですが、投手部門だけ項目を増やした結果、ソート時に順位が非表示になっていると順位でソートし直せなかったり、どの順位タイなのかがわからなくなっていたのでありがたいご指摘でした。

引き続きご意見募集してます。

10/20進捗

ikenにフィードバックいただきました。ありがとうございます。

トップページの、現在提供中のデータ欄からデータに飛べますが、飛べることがちょっとわかりづらいかもです。例えば、[データを見る]と書かれたボタンの画像が、今あるそれぞれの画像の上に合成されると、押しやすくなるかも?と思いました。あと、最新3試合の結果欄で、何対何以外の部分をクリックした場合も、詳細画面に移ると楽かもと思いました。

チーム順位はみんな特に気にするかなと思うので、TOP3よりもうちょっと表示されてると嬉しいかもと思いました。

・「最近の試合結果」カード自体をクリックできるように修正
・チーム順位などの項目を5件まで表示

・「提供中のデータ」の選択状態がわかるように修正、画像右下に文言追加

・「チームページ」の直近の試合結果のUI崩れ修正

運営してくれているというのは競技者にとって、とても嬉しいところだと思います。応援してます!

ありがとうございます!

10/12-14進捗

image

ランディングページの実装

カルーセルのtouch対応

先日の記事でreact-slideshowを推しましたが、スワイプに対応していないという問題があったのでいくつかtouchイベントに対応しているカルーセルコンポーネントを調査しました。

react-slick、君に決めた!

LINE風CSSの適用

会社の同僚にアイデアをもらったので自力で実装してみました。

 <div style={{
        padding:5
       }}
     >
            <div className="question">掲載料金はかかりますか?</div>             
            <div className="answer">いいえ。料金はかかりません.</div>
            <div className="question">データはどうやって登録するのですか?</div>
            <div className="answer">現在、運営がデータを入力する形となっていますのでデータをお送りください。</div>
        </div>
.question{
  width: 70%;
  position: relative;
  padding: 10px;
  background-color: #f2f3f7;
  font-size: 16px;
  color: #231815;
  border-radius: 12px;
  box-sizing: border-box;
  margin: 5px;
}
.answer{
  width: 70%;
  position: relative;
  left:50px;
  padding: 10px;
  background-color: #fde5e5;
  padding: 10px;
  font-size: 16px;
  color: #231815;
  border-radius: 12px;
  box-sizing: border-box;
  margin: 5px;
}

webフォントの軽量化

https://gist.github.com/manabuyasuda/b5c867a7cbd17d1eb905b3a8cfd621a6

flexboxを実装する


今回はグリッドレイアウトを使わずにflex-boxを使いました。
幅500pxを境界に、flex-directionを切り替えるメディアクエリを書きます。flex-directionを切り替えるのと同時に画像をウインドウの50%か100%に切り替えます。
サイズの異なる画像を並べるのにobject-fit : coverが便利でした。

JSX

const imageLink = (toLink,image_url,text) => {
    return(
          <div 
            style={{
              position:'relative'
            }}
          >
            <div
              style={{
                fontSize: 20,
                cursor:'pointer',
                position:'relative'
              }}
              onClick={()=>{
                window.location.href= toLink
              }}
            >
              <img 
                style={{
                  height:'20vh',
                  objectFit:'cover'
                }}
                src={image_url}/>
              <span
                  style={{
                    fontSize: 20,
                    color:'aliceblue',
                    textShadow:'2px 2px 2px black',
                    position:'absolute',
                    top:0,
                    left:0
                  }}
                >{text}
              </span>
            </div>
          </div>
    )
  }

CSS

@media(max-width:500px){
  .flex-parent{
    display: flex;
    flex-direction: column;
  }
  .flex-parent img{
    width: 100vw;
  }
}
@media(min-width:501px){
  .flex-parent{
    display: flex;
    flex-direction: row;
  }
  .flex-parent img{
    width: 50vw;
  }
}

参考
- Flexbox【第1回】並べる方向 〜flex-direction編〜
- 縦横比の違う画像を均等に横並びにする方法
- 画像の上におしゃれに文字やボタンをのせる方法

10/6進捗

概要

  • 動画スライドコンポーネントの実装
  • 蓋ざんまいサイトとの連携
  • 独自ドメイン取りました
     - トラブルシューティング
  • 日別試合結果累計バグ対処
    • 原因

動画スライドコンポーネント

主にキャップ野球用の対応ですが、皆さんyoutubeとかtwitterにばらばらに動画を上げているので、蓋ざんまい非公式サイトではその集約の役割も果たしていました。

ただ、蓋ざんまいサイトでは
- 縦列に配置してコンテンツが長くなってしまう

という欠点があり、試合結果を一律JCBL-SCORE側で表示する対応をするため、どんなUIがいいか考えた結果、カルーセルみたいに横にスライドするのがよいという考えに。

使ったコンポーネント

  • react-slideshow
    • 今回新たに採用したコンポーネント
  • react-youtube
    • JCBL-SCOREで既に採用
  • react-twitter-embed
    • 蓋ざんまいサイトで既に採用

実装結果

蓋ざんまいサイトとの連携

蓋ざんまいで独自に実装していた試合結果ページを廃止(アクセスはできますがリンクは張っていません)して、一律JCBL-SCORE側で試合結果を表示するようにしました。

独自ドメイン取りました

  • 旧ドメイン(netlify)
    • https://jcblscore-react.netlify.com/league/cap_baseball
  • 新ドメイン
    • https://jcbl-score.com/league/cap_baseball

だいぶ短縮できました。
設定自体は大して難しくなく、ちょっと古いですが以下のサイトなどを参考に進めました。

手順としては、
1. ネームサーバの設定をnetlifyのものにする
2. DNSが浸透するまで待つ
3. Let's encryptでSSL証明書を発行する(netlifyがやってくれる)

トラブルシューティング

3でちょっとはまりました。
ERR_CERT_COMMON_NAME_INVALID(プライバシーエラー)が出てしまうのです。
作られた証明書を見ると、subjectが「*.netlify.com」になっています。証明する対象が間違っているわけです。
よくよく考えると、新しいドメインにリダイレクトしていないままLet's encryptの処理をしていたので、redirects

https://jcblscore-react.netlify.com/* https://jcbl-score.com/:splat 301!

1行追記します。これで証明書を発行し直すとプライバシーエラーが解消されました。

日別試合結果累計バグ

原因

APIコンテナとDBコンテナのタイムゾーンがGMTで、DBに入っている値がJSTだったので、日付で比較しようとすると、SQLで>や
composeファイルで書くと有効な対応策がないので、SQLに渡す際に日付オブジェクトにするのをやめました。

JCBL-SCORE ver3のαリリース

システムのURL

ver3αリリースについて

これまで単一のリーグ(カラーボール野球リーグ)にのみ成績提供をする設計でしたが、DB構成なども変更し、多リーグ対応と、それに伴い入力UIを実装したのがver3になります。

αリリースについて

まだシステム的に不具合は残っていますが、動作を見せたり使ってもらったりするのがよいと思ってαリリースをしています。
機能の修正は今後していきます。

システムの簡単な概要

リーグトップ画面

試合結果一覧

スタッツ

5.png

選手個人ページ

6.png

チーム・選手一覧

7.png

チームページ

8.png

日別試合結果

3.png

試合結果詳細

4.png

データ提供のお願い

以下の大会・リーグに関するデータを掲載したいと思っています。
データを提供していただける方はご連絡ください。

キャップ野球

  • 2019年2月 東西統一蓋祭
  • 2019年5月 大福大会
    2019年5月-7月 春季関東キャップリーグ
  • 2019年6月 ドデカミン杯
  • 2019年8月 桜咲く佐倉蓋ざんまい
    2019年10月 秋季関東キャップリーグ(予定)
  • その他公式戦や練習試合

リーグ管理したい方

現在はカラーボール野球・キャップ野球を主な対象としていますが、野球型のスポーツであればシステム的には対応可能ですので、
自分のリーグの成績を本システムで閲覧したいという方はお声がけください。将来的にはリーグ管理者にアカウントを発行したいと考えています。

今後の予定

安定稼働への開発継続と不具合報告のお願い

現在、α版としてリリースしていますが、不具合などもありますので、安定したシステムを構築するべく開発を継続します。
不具合や使用上の疑問などありましたらお気軽にお知らせください。

大会運営者への管理アカウント発行(時期未定)

iPadなどのモバイル端末で試合終了後にその場で入力していただいて、データ反映の遅延をなくしたいと考えています。

キャップ野球非公式特設サイト進捗(8/27)

「桜咲く佐倉蓋ざんまい」非公式特設サイト
https://sakura-cap.netlify.com/

非・公・式です。(大事なことなので2回言いました)

決勝トーナメントの内訳を前に配置しました


以前はトーナメント表を上に配置していましたが、内訳、トーナメント表の順番に表示するよう変更しました。

試合詳細ページへのリンク

「予選」タブのリーグ戦の組み合わせ表の中に動画があるものだけ下線を引いてリンクを張っています。


初期段階のUIと比べていただくと、今回UIに色々力をいれていることがわかっていただけるかと思います。

試合詳細ページ


スコアと試合動画ツイートを引用しています。

技術的なこと

今回使用しているのは、react-twitter-embedというReactコンポーネントです。

tweetIdにツイートIDが入るよう記述するとスタイルなどもCSSが用意されています。

<TwitterTweetEmbed tweetId={*******} />

サイトのソース

GitHub

技術的なこと

今回はDBやアプリケーションサーバを使わずにnetlifyだけで実装することを目標にしています。
データはjsの中にobjectで埋め込んでいます。
jsonはコメント書けないのが面倒なので。
興味がある方はData.jsを読んでみてください...

キャップ野球全国大会の非公式特設サイトを作った件

桜咲く佐倉蓋ざんまい

非公式特設サイト作りました。

先週末に千葉県佐倉市で開催されたキャップ野球の全国大会の結果をまとめたサイトをCRA(CreateReactApp)でサクッと作りました。
結局手直し含めると2.5人日ぐらいです。

テーマカラーは運営の公式twitterのロゴがこんな感じだったので
(実際は改行されているけれども)そこから採用しました。
カラーピッカーでだいたいの色コードを入れてそこから構成します。
今回はグラデーション使えるようになったので楽しくて多用しています。え、昔マーキーとかいっぱい使いませんでした?(インターネット老人会)

ちなみに、この後さらにUIをいじっています。
どこが変わったかわかるかな?

なぜ作ろうと思ったか

最初は手書きしかないトーナメントをまとめるだけ...のつもりだったのですが、どうせならリーグ戦も表示させたいなーと思い、
よさそうなツールを探したのですが、あまりピンとくるものがなくて自分で書いてしまった次第。最近UI書くの楽しすぎる問題

大会といえば

トーナメント結果と表彰状に個人開発のサービスを採用しています。いつもお世話になっております...!
2.png

トーナメント

おおにしさんの「THE TOURNAMENT」ですよね。

表彰状

鉄板のあんどさんの「WEB表彰」ですよね。

大会概要とか


体裁的にあった方がいいかなーと思ったのですが、
まあ誰も見ませんよね...!

ご意見・ご要望お待ちしています!

快適に楽しく使ってもらえるのが制作者冥利に尽きるので。

終わりに

最近このボードの野球リーグスコア管理システムの進捗があまり出ていません。何だかんだver2で安定しています...w

PWA対応

PWA

経緯

これまで読んだ本や積んでる本の値段の合計がわかるサービス「積読ハウマッチ」が最近リリースされたのですが、

PWA対応してるよ!という売りだったのでふとフロントがCRAのうちのサービスどうだったっけ...と思って確認。
manifest.json初期状態のままだったので「React App」....ダサい。

App Manifest Generatorを使ってみる

項目で設定を選んでアイコンに使う画像ファイルをアップロードするだけ。

項目の解説はこちらが参考になります。
- manifest.jsonでホーム画面へのアプリ追加【これからはじめるPWA】

manifest.json

{
  "short_name": "JCBL-SCORE",
  "name": "日本カラーボール野球連盟スコア管理システム「JCBL-SCORE」",
  "theme_color": "darkcyan",
  "background_color": "#ffffff",
  "display": "standalone",
  "Scope": "/",
  "start_url": "/",
  "icons": [
    {
      "src": "img/icons/icon-72x72.png",
      "sizes": "72x72",
      "type": "image/png"
    },
    .....
    {
      "src": "img/icons/icon-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  "splash_pages": null
}

対応した結果

7/20進捗

選手登録API実装

まだ画面(UI)の実装はまだですが、選手の新規登録APIは実装完了。
打撃成績などはまだマニュアルでDBに入れてます。

Nodeプロジェクトに統合していたReactプロジェクトを再分離

現在のSEOの資産を活かすのであればこのままnetlifyドメインでもいいかな...と思い始めました。
分けていた方がデプロイが楽なので。

画面イメージ

サンプルデータはいつものように「関東キャップリーグ」さんです。

ブランドロゴに副題をつけました。

チームページ

スタッツ

チーム・選手一覧

チーム成績登録API実装完了

チーム成績登録API実装完了

node-mysql2が数値を文字列として返してくる

おかげでtoFixed()とかコケます。parseFloat(文字列を小数にパースするメソッド)とか間に挟みました。

undef判定はちゃんと書きましょう

undefの判定に! valueって書いてたらvalue0で躓きましたw

テーブルの大文字小文字に注意しましょう

今回からdockerコンテナを使い始めたのですが、小文字のテーブルを大文字でSQL書くと認識しません。やっぱりlinuxですね。

エンドポイント調整

複数リーグ対応のためにUIからAPIに渡すパラメータなどを増やす必要があるので調整。

サンプルデータ投入しながらデバッグ

関東キャップリーグさんの試合データが公開されているので入力していきます。
1カード分探しても見つからなかったのですが。

シーズンスタッツ

image

日別試合結果一覧

image

試合結果詳細

image

7/14

SELECT結果の調整

image

これまでは単一のリーグ用のシステムだったため、where句で集計する期間の範囲を与えるだけだったが、リーグIDを渡して「どのリーグのどの期間か」を求めるように修正した。

  • チーム順位
  • 打撃タイトル
  • 投手タイトル

いずれも修正完了。

データ登録APIの実装開始

image

ようやく重い腰を上げてデータ登録機能の実装に着手。

7/7進捗

関東キャップリーグさんにシステム提供の打診中

Excelで集計した成績を写真に撮ってアップされてるので
同じマイナースポーツの誼で、システムを提供させてもらえないかと打診。
確定ではないですが、とりあえず前向きな回答をもらっています。

ver3(複数リーグ対応版)でサンプルデータ投入

若干滞り気味だったver3の開発を進めることに。
ただ、リーグの期間終了が今週末とのことなのでver3投入は間に合わないだろうなー。

  • docker-composeでの動作確認
  • サンプルデータの投入
  • DB設計修正
  • SQL修正

image

やっぱりモチベーションがある方が開発が進みます。

6/16 進捗

  • 既存システム、色々改修する必要のある箇所がある
  • フロント(react)、後でスタイル調整しようと思うが手を加えてしまう
  • サムサニモマケル、アツサニモマケル。

ログイン機能改修

現行システム(ver.2)ではコメントアウトして提供していない機能ですが、作りかけの残骸を改修しています。
とりあえずログインはできるようになった。

リーグ新規登録機能実装中

ドメイン未定←これ重要。

image

フロントに予想以上に時間使ってバックエンド実装できず。

ロゴ作成サービス「canva」使ってみる

canva
image

背景色透過は有料機能みたいです。

6/14 進捗

ver.3 システムの開発

  • 主な機能
    • 複数リーグへの対応
    • DB編集権限の提供

新ルーティングへの対応

/league/リーグ名/season/シーズンIDというルーティングへの対応を進めています。新システムではリーグのURLをユーザが決められるようにする予定です。

image

NodeJSのrouterをこう書いてしまって404エラーしか返ってこなくなって嵌まりました。。。

this.router.get("/:league_name(\\d{1,20})/season/:season_id(\\d{1,3})", this.showStats);

DBを新スキーマへ移行

旧システムのDBのダンプをスキーマを変えた新システムにリストアする方法を色々検討した結果、
一旦dockerにDBを2つ用意して新システムにinsert/selectすることにしました。

docker-composeで新たに設定を追加しました。

environment:
 #タイムゾーンの設定
 - TZ=Asia/Tokyo
 #mysqlサーバの起動時に実行するコマンド
 #文字セットの設定、SQLモードの設定
command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci --sql_mode="STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"

typescript

新スキーマに移行したのに伴い、DBアクセス周りのプログラムの改修が必要になっています。
interface作ったり、追加したテーブル用にservice(DBアクセスクラス)作ったり。

まだselect関係の機能を元に戻すので手一杯ですが、
insert/update関連の機能を早く実装したい。

宣伝

去る6/8に行われた公式戦の動画作りましたのでよろしければご覧ください。
- 第一試合

  • 第二試合