膨大な減点数を処理するためにアプリを作った話

<h1>きっかけ</h1>
<p>私が所属している学園祭実行委員会では、12月に行われる学園祭だけではなく、4月に新入生向けに行われる新歓祭典の運営もしています！<br>また、私はサークルブースの担当をしており、団体数は約120団体に及び、手作業でパトロールと違反行為の管理を行うのは毎年、至難の業となってしまっていました。さらに、昨年度は70団体ほどだったのが、団体の勧誘の場を増やすべく、今年度は約1.5倍へと枠を増加させることなり、パトロール時の団体の管理方法を改善する必要がありました。<br>そこで、TypeScriptとNext.jsの能力の向上も兼ね、この団体管理アプリを作成することとしました。


---

<h1>利用した技術</h1>
<h3>React</h3>
Version: 18.2.0<br>
TypeScriptを利用<br>
<a href="https://react.dev/">https://react.dev/</a>

>ユーザインターフェース構築のための JavaScript ライブラリ
>引用：https://ja.reactjs.org/

→フロントエンド開発のため


<h3>Material-UI</h3>
<a href="https://mui.com/">https://mui.com/</a>

>MUI offers a comprehensive suite of UI tools to help you ship new features faster. Start with Material UI, our fully-loaded component library, or bring your own design system to our production-ready components.
>引用：https://mui.com/

→デザイン構築のため

<h3>Next.js</h3>
Version: 13.2.4<br>
<a href="https://nextjs.org/">https://nextjs.org/</a>

>Used by some of the world's largest companies, Next.js enables you to create full-stack Web applications by extending the latest React features, and integrating powerful Rust-based JavaScript tooling for the fastest builds.
>引用：https://nextjs.org/

→Reactアプリケーション作成のため

<h3>Algolia</h3>
<a href="https://www.algolia.com/">https://www.algolia.com/</a>

>With Algolia Search, developers can rely on a simple and robust API to compose any search experience in the context of their companies’ applications. It democratizes the development of any search capability. The power of API building blocks supported by the flexibility of composing beautiful UIs, enables any developer to build applications and drive business results faster. From ecommerce to media, Algolia Search powers search and discovery experiences everywhere
>引用：https://www.algolia.com/products/search-and-discovery/hosted-search-api/

→検索ボックスを実装するため

<h3>Styled-Components</h3>
<a href="https://styled-components.com/">https://styled-components.com/</a>

>Utilising tagged template literals (a recent addition to JavaScript) and the power of CSS, styled-components allows you to write actual CSS code to style your components. It also removes the mapping between components and styles – using components as a low-level styling construct could not be easier!
>引用：https://styled-components.com/docs

→CSS-in-JSを利用するため

<h3>Firestore</h3>
<a href="https://firebase.google.com/">https://firebase.google.com/</a>

>Cloud Firestore は、Firebase と Google Cloud からのモバイル、ウェブ、サーバー開発に対応した、柔軟でスケーラブルなデータベースです。Firebase Realtime Database と同様に、リアルタイム リスナーを介してクライアント アプリ間でデータを同期し、モバイルとウェブのオフライン サポートを提供します。これにより、ネットワーク レイテンシやインターネット接続に関係なく機能するレスポンシブ アプリを構築できます。Cloud Firestore は、その他の Firebase および Google Cloud プロダクト（Cloud Functions など）とのシームレスな統合も実現します。
>引用：https://firebase.google.com/docs/firestore?hl=ja

→各団体の減点状況と減点項目のデータを管理するため。

<h3>Vercel</h3>
<a href="https://vercel.com/">https://vercel.com/</a>

>Vercel is the platform for frontend developers, providing the speed and reliability innovators need to create at the moment of inspiration.
>引用：https://vercel.com/docs

→Next.jsによるアプリケーションをデプロイするため

---

<h1>概要</h1>
<h3>システムの構成</h3>
<p>今回、作成したプログラムの構成は下のようになっています！</p>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2699602/2612f2dd-1573-8d55-b614-7206003d5266.png" width=500px>

---

<h4>① デプロイサーバとの通信</h4>
<p>Next.jsとReact、TypeScriptでプログラムを作成した関係上、デプロイサーバはVercelとすることにしました。Vercelのデプロイには、下記URLのサイトを参考にしました。<br>今回、利用しているAPIのAPIキーは、Vercelの環境変数として定義しています。</p>

https://weseek.co.jp/tech/621/

---

<h4>② Algoliaによる検索機能の実装</h4>
<p>検索機能が必要となる状況とは、正式な団体が分からない場合であり、「体育会○○部」や「立命館大学○○サークルブース」「立命館大学○○部」といった名称の団体が多く存在したため、先頭一致検索ではなく、全文検索を採用することとしました。また、ユーザはパトロール中の実行委員会員のため、スマートフォンでの利用が想定されるため、ユーザ側のリソースを消費しないSaaSが望ましいと考えました。そこで、今回はAlgoliaによるAPIを用いて検索機能を実装しました。また、検索対象は多く存在する「団体一覧」と「減点項目一覧」の２種類としました。<br>AlgoliaにはInstantSearchという検索窓を簡単に実装する機能があるようでしたが、その検索結果をJSONデータとしてそのまま欲しかったので、今回はInstantSeachを用いずに、通常の方法で行うこととしました。</p>

<p>実際に当日は230回ほど利用されました。</p>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2699602/0e25cf15-c360-c74c-1a88-fd03395ae04a.png" width=65%>





↓ 参考にしたサイト

https://dabohaze.site/react-algolia/

https://qiita.com/oiz-y/items/f828d37855e87ccbc49b

---

<h4>③ Cloud Firestoreへの団体情報の保存</h4>
<p>団体の情報を保存するにあたり、将来的には認証機能も備えたいため、今回はCloud Firestoreを利用しました。<br>データベースには各団体の情報として「区画番号」、「受付番号」、「団体名」、「減点数」、「減点履歴」、「団体の識別子」が保存されており、さらには「減点履歴」には、「減点処理の識別子」、「減点項目の識別子」、「対応結果」、「登録時間」、「誰が減点したか」が含まれています。<br>これらの情報により、今回のアプリケーションの機能を実装することができています。</p>


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2699602/1e6a874a-2406-3ea3-412f-e0c756d3fd8f.png" width=500px>

---
<h4>④、⑤ Google Apps Scriptでの違反記録
<p>我々は違反行為発見時のフローを下のようにしていました。
<br>1.「パトロール要員が発見」<br>&ensp;&ensp;↓<br>2.「サークルブース責任者へ報告」<br>&ensp;&ensp;↓<br>3.「サークルブース責任者が減点か注意かを決める」<br>&ensp;&ensp;↓<br>4.「団体へ報告」<br>そのため、違反行為が報告されてすぐに、減点する実装では、ダメです。そのため、一度、記録を行い、我々で3と4のフローを行う必要がありました。<br>そこで、Google Apps Scriptにて、Webhookを設定し、POSTリクエストにより送られてきた違反データをSpread Sheetへ記録することとしました。<br>もともと、Google Apps Scriptを用いて、メールの自動返信などを実装したことがあったため、比較的スムーズに作成することができました。<br>当日は実際に計200件くらいの減点が来たので、自動化によりかなり容易に当日のパトロールを行うことができました。</p>

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2699602/79b906dd-2675-9215-1aa6-18de5dceea50.png" width=500px>

---
<h4>⑥ Spread Sheetからのデータの読込 & Firestoreへのデータの処理の書き込み
<p>前述のフローにおいて、実際に3と4のフローを実装するべく、Google Apps Scriptにて、減点処理したい行数を入力し、処分を入力することで、自動的にFirestoreへ登録する機能を実装しました。</p>

---

<h1>開発したプログラム</h1>
<h3>ソースコード</h3>

https://github.com/TAKAYAMAN2180/public-deduction-webpage

※あくまでも公開用であるため、一部、非公開としております。

<h3>UI</h3>
①団体一覧
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2699602/e6c32ae2-a11f-0c5a-463d-79b9363fda6a.png" width=90%/>
<p><br>&ensp;↓
<br>団体を選択
<br>&ensp;↓</p>
<br>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2699602/97bd9af3-3ea5-fa1b-c172-4d817ec29d3d.png" width=90%/>
<br>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2699602/781b100d-aa6d-1f63-4db3-e531910bc13e.png" width=90%/>
<br>
<p>
<h3>各ページについて</h3>
ここからは各ページの説明をしていきます！
<h4>① 団体一覧</h4>
<p>このページでは、ソートもしくは検索することによって、対象の団体を探すことができます。<br>「区画番号」、「受付番号」、「団体名」でソートが可能です。<br>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2699602/471403d5-0109-766d-7940-40dd5d2d1cf7.png" width=70%/><br>
また、同じコラム名をタッチすることにより、昇順と降順を切り替えることができます。<br>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2699602/0d874fce-bfd5-ce29-df05-cfb84c63219c.png" width=60%/><br></p>

<h4>② 各団体の情報</h4>
<p>このページでは、「区画番号」、「受付番号」、「現在の減点数」、「特記事項」、「減点情報」を確認することができ、また違反行為を発見した際にはこのページより報告してもらうことになっています。</p>

<h4>③ 違反項目の選択</h4>
<p>違反行為のボタンを押すと、このページに移動し、違反した減点項目を選択してもらいます。団体一覧のページのように「減点数」と「減点番号」でソートすることができ、同じコラム名をタッチすることによって、昇順と降順を切り替えることができます。</p>

<h4>④ 情報の入力</h4>
<p>前のページで減点項目を選択すると、このページに移動します。このページでは、内容を確認し、名前を入力することができ、最後にボタンを押すことで、Firestoreへのデータの登録とGoogle Apps ScriptへのPOSTリクエストを送信します。</p>

<h4>⑤ 待機画面</h4>
<p>Firestoreへのデータの登録とGoogle Apps ScriptへのPOSTリクエストを送信している際には、このような待機画面が表示されます。</p>

<h4>⑥ 完了の通知</h4>
<p>Firestoreへのデータの登録とGoogle Apps ScriptへのPOSTリクエストを送信が完了すると、このような通知が表示されます。そして、「OK」のボタンを押すと、①の画面の団体一覧の画面へ移動します。</p>

<h4>⑦ 情報の反映</h4>
<p>実際に違反を報告すると、このようになり、責任者である自分が処分結果を入力するまでは「審査中」と表示されます。</p>


<h1>今後の展望</h1>
<p>・違反の処理結果について、責任者と副責任者へメールで自動通知<br>・団体側にもこのデータを確認してもらえるようにする<br>・GUIでの「⑥ Spread Sheetからのデータの読込 & Firestoreへのデータの処理の書き込み」の機能の実装

---

<h1>さいごに</h1>
<p>ご覧いただき、ありがとうございました。秋の学園祭に向けて、これからもこのアプリの改善を重ねていきたいと思います！</p>